Repository: rocicorp/mono
Pull request #3977: fix(zql): Fix ref count for edit in applyChange
Author: login: arv
Created: 2025-03-13T11:15:14Z | Merged: 2025-03-13T12:35:11Z

The edit branch was incomplete.


### Singular

We replace the old object with the new object transferring the ref count.


### Not Singular

If we edit the value of a non primary column, we transfer the ref count to the new object.

If we edit the value of a primary column, we need to decrement the ref count of the old object and increment the ref count of the new object.



Diff:
diff --git a/packages/zql/src/ivm/view-apply-change.test.ts b/packages/zql/src/ivm/view-apply-change.test.ts
index 08a949dca2..f34b94ef17 100644
--- a/packages/zql/src/ivm/view-apply-change.test.ts
+++ b/packages/zql/src/ivm/view-apply-change.test.ts
@@ -1024,5 +1024,647 @@ describe('applyChange', () => {
         }),
       ).toThrowError(new Error('node does not exist'));
     });
+
+    test('edit, singular: false', () => {
+      const refCountMap = new Map<Entry, number>();
+      const schema = {
+        tableName: 'event',
+        columns: {
+          id: {type: 'string'},
+          name: {type: 'string'},
+        },
+        primaryKey: ['id'],
+        sort: [['id', 'asc']],
+        system: 'client',
+        relationships: {},
+        isHidden: false,
+        compareRows: makeComparator([['id', 'asc']]),
+      } as const;
+      const root = {'': []};
+      const format = {
+        singular: false,
+        relationships: {},
+      };
+
+      const apply = (change: ViewChange) =>
+        applyChange(root, change, schema, '', format, refCountMap);
+
+      apply({
+        type: 'add',
+        node: {
+          row: {
+            id: '1',
+            name: 'Aaron',
+          },
+          relationships: {},
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": [
+            {
+              "id": "1",
+              "name": "Aaron",
+            },
+          ],
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 1,
+        }
+      `);
+
+      apply({
+        type: 'edit',
+        oldNode: {
+          row: {
+            id: '1',
+            name: 'N/A',
+          },
+        },
+        node: {
+          row: {
+            id: '1',
+            name: 'Greg',
+          },
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": [
+            {
+              "id": "1",
+              "name": "Greg",
+            },
+          ],
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Greg",
+          } => 1,
+        }
+      `);
+
+      for (let i = 0; i < 2; i++) {
+        apply({
+          type: 'add',
+          node: {
+            row: {
+              id: '1',
+              name: 'Aaron',
+            },
+            relationships: {},
+          },
+        });
+      }
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": [
+            {
+              "id": "1",
+              "name": "Aaron",
+            },
+          ],
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 3,
+        }
+      `);
+
+      apply({
+        type: 'edit',
+        oldNode: {
+          row: {
+            id: '1',
+            name: 'Aaron',
+          },
+        },
+        node: {
+          row: {
+            id: '1',
+            name: 'Greg',
+          },
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": [
+            {
+              "id": "1",
+              "name": "Greg",
+            },
+          ],
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Greg",
+          } => 3,
+        }
+      `);
+    });
+
+    test('edit primary key, singular: false', () => {
+      const refCountMap = new Map<Entry, number>();
+      const schema = {
+        tableName: 'event',
+        columns: {
+          id: {type: 'string'},
+          name: {type: 'string'},
+        },
+        primaryKey: ['id'],
+        sort: [['id', 'asc']],
+        system: 'client',
+        relationships: {},
+        isHidden: false,
+        compareRows: makeComparator([['id', 'asc']]),
+      } as const;
+      const root = {'': []};
+      const format = {
+        singular: false,
+        relationships: {},
+      };
+
+      const apply = (change: ViewChange) =>
+        applyChange(root, change, schema, '', format, refCountMap);
+
+      apply({
+        type: 'add',
+        node: {
+          row: {
+            id: '1',
+            name: 'Aaron',
+          },
+          relationships: {},
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": [
+            {
+              "id": "1",
+              "name": "Aaron",
+            },
+          ],
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 1,
+        }
+      `);
+
+      apply({
+        type: 'edit',
+        oldNode: {
+          row: {
+            id: '1',
+            name: 'N/A',
+          },
+        },
+        node: {
+          row: {
+            id: '2',
+            name: 'Greg',
+          },
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": [
+            {
+              "id": "2",
+              "name": "Greg",
+            },
+          ],
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "2",
+            "name": "Greg",
+          } => 1,
+        }
+      `);
+      apply({
+        type: 'remove',
+        node: {
+          row: {
+            id: '2',
+            name: 'Greg',
+          },
+          relationships: {},
+        },
+      });
+
+      for (let i = 0; i < 2; i++) {
+        apply({
+          type: 'add',
+          node: {
+            row: {
+              id: '1',
+              name: 'Aaron',
+            },
+            relationships: {},
+          },
+        });
+      }
+      expect(root).toMatchInlineSnapshot(`
+    {
+      "": [
+        {
+          "id": "1",
+          "name": "Aaron",
+        },
+      ],
+    }
+  `);
+
+      for (let i = 0; i < 2; i++) {
+        apply({
+          type: 'add',
+          node: {
+            row: {
+              id: '2',
+              name: 'Greg',
+            },
+            relationships: {},
+          },
+        });
+      }
+
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": [
+            {
+              "id": "1",
+              "name": "Aaron",
+            },
+            {
+              "id": "2",
+              "name": "Greg",
+            },
+          ],
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 2,
+          {
+            "id": "2",
+            "name": "Greg",
+          } => 2,
+        }
+      `);
+
+      apply({
+        type: 'edit',
+        oldNode: {
+          row: {
+            id: '1',
+            name: 'Aaron',
+          },
+        },
+        node: {
+          row: {
+            id: '2',
+            name: 'Greg',
+          },
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": [
+            {
+              "id": "1",
+              "name": "Aaron",
+            },
+            {
+              "id": "2",
+              "name": "Greg",
+            },
+          ],
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 1,
+          {
+            "id": "2",
+            "name": "Greg",
+          } => 3,
+        }
+      `);
+    });
+
+    test('edit, singular: true', () => {
+      const refCountMap = new Map<Entry, number>();
+      const schema = {
+        tableName: 'event',
+        columns: {
+          id: {type: 'string'},
+          name: {type: 'string'},
+        },
+        primaryKey: ['id'],
+        sort: [['id', 'asc']],
+        system: 'client',
+        relationships: {},
+        isHidden: false,
+        compareRows: makeComparator([['id', 'asc']]),
+      } as const;
+      const root = {'': undefined};
+      const format = {
+        singular: true,
+        relationships: {},
+      };
+
+      const apply = (change: ViewChange) =>
+        applyChange(root, change, schema, '', format, refCountMap);
+
+      apply({
+        type: 'add',
+        node: {
+          row: {
+            id: '1',
+            name: 'Aaron',
+          },
+          relationships: {},
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": {
+            "id": "1",
+            "name": "Aaron",
+          },
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 1,
+        }
+      `);
+
+      apply({
+        type: 'edit',
+        oldNode: {
+          row: {
+            id: '1',
+            name: 'N/A',
+          },
+        },
+        node: {
+          row: {
+            id: '1',
+            name: 'Greg',
+          },
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": {
+            "id": "1",
+            "name": "Greg",
+          },
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Greg",
+          } => 1,
+        }
+      `);
+
+      apply({
+        type: 'add',
+        node: {
+          row: {
+            id: '1',
+            name: 'Greg',
+          },
+          relationships: {},
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": {
+            "id": "1",
+            "name": "Greg",
+          },
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Greg",
+          } => 2,
+        }
+      `);
+
+      apply({
+        type: 'edit',
+        oldNode: {
+          row: {
+            id: '2',
+            name: 'Greg',
+          },
+        },
+        node: {
+          row: {
+            id: '1',
+            name: 'Aaron',
+          },
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": {
+            "id": "1",
+            "name": "Aaron",
+          },
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 2,
+        }
+      `);
+    });
+
+    test('edit primary key, singular: true', () => {
+      const refCountMap = new Map<Entry, number>();
+      const schema = {
+        tableName: 'event',
+        columns: {
+          id: {type: 'string'},
+          name: {type: 'string'},
+        },
+        primaryKey: ['id'],
+        sort: [['id', 'asc']],
+        system: 'client',
+        relationships: {},
+        isHidden: false,
+        compareRows: makeComparator([['id', 'asc']]),
+      } as const;
+      const root = {'': undefined};
+      const format = {
+        singular: true,
+        relationships: {},
+      };
+
+      const apply = (change: ViewChange) =>
+        applyChange(root, change, schema, '', format, refCountMap);
+
+      apply({
+        type: 'add',
+        node: {
+          row: {
+            id: '1',
+            name: 'Aaron',
+          },
+          relationships: {},
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": {
+            "id": "1",
+            "name": "Aaron",
+          },
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 1,
+        }
+      `);
+
+      apply({
+        type: 'edit',
+        oldNode: {
+          row: {
+            id: '1',
+            name: 'N/A',
+          },
+        },
+        node: {
+          row: {
+            id: '2',
+            name: 'Greg',
+          },
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": {
+            "id": "2",
+            "name": "Greg",
+          },
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "2",
+            "name": "Greg",
+          } => 1,
+        }
+      `);
+
+      apply({
+        type: 'add',
+        node: {
+          row: {
+            id: '2',
+            name: 'Greg',
+          },
+          relationships: {},
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": {
+            "id": "2",
+            "name": "Greg",
+          },
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "2",
+            "name": "Greg",
+          } => 2,
+        }
+      `);
+
+      apply({
+        type: 'edit',
+        oldNode: {
+          row: {
+            id: '2',
+            name: 'Greg',
+          },
+        },
+        node: {
+          row: {
+            id: '1',
+            name: 'Aaron',
+          },
+        },
+      });
+      expect(root).toMatchInlineSnapshot(`
+        {
+          "": {
+            "id": "1",
+            "name": "Aaron",
+          },
+        }
+      `);
+      expect(refCountMap).toMatchInlineSnapshot(`
+        Map {
+          {
+            "id": "1",
+            "name": "Aaron",
+          } => 2,
+        }
+      `);
+    });
   });
 });
diff --git a/packages/zql/src/ivm/view-apply-change.ts b/packages/zql/src/ivm/view-apply-change.ts
index 83820e6843..08b8273339 100644
--- a/packages/zql/src/ivm/view-apply-change.ts
+++ b/packages/zql/src/ivm/view-apply-change.ts
@@ -244,15 +244,20 @@ export function applyChange(
     }
     case 'edit': {
       if (singular) {
-        assertObject(parentEntry[relationship]);
-        // @ts-expect-error parentEntry is readonly
-        parentEntry[relationship] = {
-          ...parentEntry[relationship],
+        const existing = parentEntry[relationship];
+        assertEntry(existing);
+        const rc = must(refCountMap.get(existing));
+        const newEntry = {
+          ...existing,
           ...change.node.row,
         };
+        refCountMap.set(newEntry, rc);
+        refCountMap.delete(existing);
+        // @ts-expect-error parentEntry is readonly
+        parentEntry[relationship] = newEntry;
       } else {
-        const view = parentEntry[relationship] as EntryList | undefined;
-        assertArray(view);
+        const view = parentEntry[relationship];
+        assertEntryList(view);
         // If the order changed due to the edit, we need to remove and reinsert.
         if (schema.compareRows(change.oldNode.row, change.node.row) === 0) {
           const {pos, found} = binarySearch(
@@ -263,6 +268,7 @@ export function applyChange(
           assert(found, 'node does not exists');
           const rc = must(refCountMap.get(view[pos]));
           refCountMap.delete(view[pos]);
+          // @ts-expect-error view is readonly
           view[pos] = makeEntryPreserveRelationships(
             change.node.row,
             view[pos],
@@ -278,7 +284,14 @@ export function applyChange(
           );
           assert(found, 'node does not exists');
           const oldEntry = view[pos];
-          view.splice(pos, 1);
+          const rc = must(refCountMap.get(oldEntry));
+          if (rc === 1) {
+            refCountMap.delete(oldEntry);
+            // @ts-expect-error view is readonly
+            view.splice(pos, 1);
+          } else {
+            refCountMap.set(oldEntry, rc - 1);
+          }
 
           // Insert
           {
@@ -287,16 +300,24 @@ export function applyChange(
               change.node.row,
               schema.compareRows,
             );
-            assert(!found, 'node already exists');
-            view.splice(
-              pos,
-              0,
-              makeEntryPreserveRelationships(
-                change.node.row,
-                oldEntry,
-                format.relationships,
-              ),
+            let rc = 1;
+            const newEntry = makeEntryPreserveRelationships(
+              change.node.row,
+              oldEntry,
+              format.relationships,
             );
+            let deleteCount = 0;
+            if (found) {
+              // We changed a row to a row that already exists. The existing row
+              // should increase its ref count.
+              deleteCount = 1;
+              const existing = view[pos];
+              rc = must(refCountMap.get(existing)) + 1;
+              refCountMap.delete(existing);
+            }
+            // @ts-expect-error view is readonly
+            view.splice(pos, deleteCount, newEntry);
+            refCountMap.set(newEntry, rc);
           }
         }
       }
@@ -347,3 +368,11 @@ function getChildEntryList(
   assertArray(view);
   return view as EntryList;
 }
+
+function assertEntryList(v: unknown): asserts v is EntryList {
+  assertArray(v);
+}
+
+function assertEntry(v: unknown): asserts v is Entry {
+  assertObject(v);
+}


Reviews:
user: login: grgbkr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T18:05:38Z
user: login: grgbkr
body: LGTM
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T18:06:00Z
user: login: arv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T19:53:05Z

Review comments:
diff_hunk: @@ -278,7 +284,14 @@ export function applyChange(
           );
           assert(found, 'node does not exists');
           const oldEntry = view[pos];
-          view.splice(pos, 1);
+          const rc = must(refCountMap.get(oldEntry));
path: packages/zql/src/ivm/view-apply-change.ts
user: login: grgbkr
body: I think this code is repeated for remove, so maybe a little helper can be factored out.
created_at: 2025-03-13T18:05:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -278,7 +284,14 @@ export function applyChange(
           );
           assert(found, 'node does not exists');
           const oldEntry = view[pos];
-          view.splice(pos, 1);
+          const rc = must(refCountMap.get(oldEntry));
path: packages/zql/src/ivm/view-apply-change.ts
user: login: arv
body: Good catch
created_at: 2025-03-13T19:53:04Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-13T11:15:16Z
body: [vc]: #3RT/ZDAkIhOUd4L+kOXHTl69SnpMPjWejZAShpTQ2Bs=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ6YnVncyIsInJvb3REaXJlY3RvcnkiOiJhcHBzL3pidWdzIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3JvY2ljb3JwL3pidWdzL0ExS2F1S1ZXTGJBQzlmd3BRZGF5emYybXZCUjgiLCJwcmV2aWV3VXJsIjoiemJ1Z3MtZ2l0LWFydi1hcnJheS12aWV3LXJlZi1jb3VudC1lZGl0LXJvY2ljb3JwLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiemJ1Z3MtZ2l0LWFydi1hcnJheS12aWV3LXJlZi1jb3VudC1lZGl0LXJvY2ljb3JwLnZlcmNlbC5hcHAifX0seyJuYW1lIjoicmVwbGljYWNoZS1kb2NzIiwicm9vdERpcmVjdG9yeSI6bnVsbCwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3JvY2ljb3JwL3JlcGxpY2FjaGUtZG9jcy9DS2I5OWRaTUxnZVp4V1lUTndGSzRlcGpLNWpEIiwicHJldmlld1VybCI6InJlcGxpY2FjaGUtZG9jcy1naXQtYXJ2LWFycmF5LXZpZXctcmVmLWNvdW50LWVkaXQtcm9jaWNvcnAudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJyZXBsaWNhY2hlLWRvY3MtZ2l0LWFydi1hcnJheS12aWV3LXJlZi1jb3VudC1lZGl0LXJvY2ljb3JwLnZlcmNlbC5hcHAifX1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **replicache-docs** | ✅ Ready ([Inspect](https://vercel.com/rocicorp/replicache-docs/CKb99dZMLgeZxWYTNwFK4epjK5jD)) | [Visit Preview](https://replicache-docs-git-arv-array-view-ref-count-edit-rocicorp.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/replicache-docs-git-arv-array-view-ref-count-edit-rocicorp.vercel.app?via=pr-comment-feedback-link) | Mar 13, 2025 11:32am |
| **zbugs** | ✅ Ready ([Inspect](https://vercel.com/rocicorp/zbugs/A1KauKVWLbAC9fwpQdayzf2mvBR8)) | [Visit Preview](https://zbugs-git-arv-array-view-ref-count-edit-rocicorp.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/zbugs-git-arv-array-view-ref-count-edit-rocicorp.vercel.app?via=pr-comment-feedback-link) | Mar 13, 2025 11:32am |



author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-13T11:17:03Z
body: <h2><a href="https://bencher.dev/perf/zero/reports/fb4f6c10-8fe8-40c6-a1d9-a77720065213?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero"><img src="https://bencher.dev/favicon.svg" width="24" height="24" alt="🐰" /> Bencher Report</a></h2><table><tr><td>Branch</td><td><a href="https://bencher.dev/perf/zero/branches/arv-array-view-ref-count-edit?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">arv/array-view-ref-count-edit</a></td></tr><tr><td>Testbed</td><td><a href="https://bencher.dev/perf/zero/testbeds/localhost?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">localhost</a></td></tr></table><details><summary>Click to view all benchmark results</summary><br /><table><thead><tr><th>Benchmark</th><th><a href="https://bencher.dev/perf/zero/measures/file-size?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">File Size</a></th><th>Benchmark Result<br />kilobytes (KB)<br />(Result Δ%)</th><th>Upper Boundary<br />kilobytes (KB)<br />(Limit %)</th></tr></thead><tbody><tr><td><a href="https://bencher.dev/perf/zero/benchmarks/zero-package-tgz?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">zero-package.tgz</a></td><td>📈 <a href="https://bencher.dev/perf/zero?branches=6473a5a3-d381-4929-a977-1d3cf5eb05bc&heads=63d5696c-04c6-4037-b45c-593891823d02&testbeds=50a21deb-8296-4657-b7ad-5a4002b77d68&benchmarks=2bcdd867-a170-440b-8c18-ee15b6476b90&measures=8d8dbe98-8c7a-4838-b351-cd9d621320fe&start_time=1739273615000&end_time=1741865615000&report=fb4f6c10-8fe8-40c6-a1d9-a77720065213&upper_boundary=true">view plot</a><br />🚷 <a href="https://bencher.dev/perf/zero/thresholds/26b640ce-b4c4-4486-9624-7fab50a64f67?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">view threshold</a></td><td>1,027.21 KB<br /><details><summary>(+0.01%)</summary>Baseline: 1,027.07 KB</details></td><td>1,047.61 KB<br />(98.05%)</td></tr><tr><td><a href="https://bencher.dev/perf/zero/benchmarks/zero-js?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">zero.js</a></td><td>📈 <a href="https://bencher.dev/perf/zero?branches=6473a5a3-d381-4929-a977-1d3cf5eb05bc&heads=63d5696c-04c6-4037-b45c-593891823d02&testbeds=50a21deb-8296-4657-b7ad-5a4002b77d68&benchmarks=4f172dbf-1d31-4809-9c2d-12c79ed4c9d4&measures=8d8dbe98-8c7a-4838-b351-cd9d621320fe&start_time=1739273615000&end_time=1741865615000&report=fb4f6c10-8fe8-40c6-a1d9-a77720065213&upper_boundary=true">view plot</a><br />🚷 <a href="https://bencher.dev/perf/zero/thresholds/26b640ce-b4c4-4486-9624-7fab50a64f67?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">view threshold</a></td><td>187.42 KB<br /><details><summary>(+0.05%)</summary>Baseline: 187.33 KB</details></td><td>191.07 KB<br />(98.09%)</td></tr><tr><td><a href="https://bencher.dev/perf/zero/benchmarks/zero-js-br?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">zero.js.br</a></td><td>📈 <a href="https://bencher.dev/perf/zero?branches=6473a5a3-d381-4929-a977-1d3cf5eb05bc&heads=63d5696c-04c6-4037-b45c-593891823d02&testbeds=50a21deb-8296-4657-b7ad-5a4002b77d68&benchmarks=f9d12b7c-f48c-41fc-a662-5019d902c9f9&measures=8d8dbe98-8c7a-4838-b351-cd9d621320fe&start_time=1739273615000&end_time=1741865615000&report=fb4f6c10-8fe8-40c6-a1d9-a77720065213&upper_boundary=true">view plot</a><br />🚷 <a href="https://bencher.dev/perf/zero/thresholds/26b640ce-b4c4-4486-9624-7fab50a64f67?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">view threshold</a></td><td>52.52 KB<br /><details><summary>(+0.08%)</summary>Baseline: 52.48 KB</details></td><td>53.53 KB<br />(98.12%)</td></tr></tbody></table></details><a href="https://bencher.dev/perf/zero/reports/fb4f6c10-8fe8-40c6-a1d9-a77720065213?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">🐰 View full continuous benchmarking report in Bencher</a><div id="bencher.dev/projects/zero/id/size"></div>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-13T11:18:00Z
body: <h2><a href="https://bencher.dev/perf/zero/reports/7fa2b602-b232-4997-8496-a08e773ff31c?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero"><img src="https://bencher.dev/favicon.svg" width="24" height="24" alt="🐰" /> Bencher Report</a></h2><table><tr><td>Branch</td><td><a href="https://bencher.dev/perf/zero/branches/arv-array-view-ref-count-edit?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">arv/array-view-ref-count-edit</a></td></tr><tr><td>Testbed</td><td><a href="https://bencher.dev/perf/zero/testbeds/localhost?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">localhost</a></td></tr></table><h3>🚨 1 Alert</h3><table><thead><tr><th>Benchmark</th><th>Measure<br />Units</th><th>View</th><th>Benchmark Result<br />(Result Δ%)</th><th>Lower Boundary<br />(Limit %)</th></tr></thead><tbody><tr><td><a href="https://bencher.dev/perf/zero/benchmarks/src-client-zero-bench-ts-with-filter-lower-rows-500-x-10-columns?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">src/client/zero.bench.ts > with filter > Lower rows 500 x 10 columns (numbers)</a></td><td><a href="https://bencher.dev/perf/zero/measures/throughput?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">Throughput<br />operations / second (ops/s)</a></td><td>📈 <a href="https://bencher.dev/perf/zero?branches=6473a5a3-d381-4929-a977-1d3cf5eb05bc&heads=6d0bde35-0412-448c-9710-45bca20aaf44&testbeds=50a21deb-8296-4657-b7ad-5a4002b77d68&benchmarks=f74ffb47-faeb-4174-abfd-4879417dc53c&measures=fc802276-c2ed-462d-9fa5-0c1fcf2deb8e&start_time=1739273660000&end_time=1741865660000&report=7fa2b602-b232-4997-8496-a08e773ff31c&lower_boundary=true">plot</a><br />🚷 <a href="https://bencher.dev/perf/zero/thresholds/406f4a01-508e-4a5d-a3a2-7771bcb183a1?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">threshold</a><br />🚨 <a href="https://bencher.dev/perf/zero/alerts/2d43586f-36ac-473c-816e-c75752b7e8f8?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">alert (🔔)</a></td><td><b>59.80 ops/s<br /><details><summary>(-39.85%)</summary>Baseline: 99.43 ops/s</details></b></td><td><b>74.55 ops/s<br />(124.66%)<br /><br /></b></td></tr></tbody></table><details><summary>Click to view all benchmark results</summary><br /><table><thead><tr><th>Benchmark</th><th><a href="https://bencher.dev/perf/zero/measures/throughput?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">Throughput</a></th><th>Benchmark Result<br />operations / second (ops/s)<br />(Result Δ%)</th><th>Lower Boundary<br />operations / second (ops/s)<br />(Limit %)</th></tr></thead><tbody><tr><td><a href="https://bencher.dev/perf/zero/benchmarks/src-client-custom-bench-ts-big-schema?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">src/client/custom.bench.ts > big schema</a></td><td>📈 <a href="https://bencher.dev/perf/zero?branches=6473a5a3-d381-4929-a977-1d3cf5eb05bc&heads=6d0bde35-0412-448c-9710-45bca20aaf44&testbeds=50a21deb-8296-4657-b7ad-5a4002b77d68&benchmarks=da25d28a-0b6b-4baa-8550-fb143072856d&measures=fc802276-c2ed-462d-9fa5-0c1fcf2deb8e&start_time=1739273660000&end_time=1741865660000&report=7fa2b602-b232-4997-8496-a08e773ff31c&lower_boundary=true">view plot</a><br />🚷 <a href="https://bencher.dev/perf/zero/thresholds/406f4a01-508e-4a5d-a3a2-7771bcb183a1?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">view threshold</a></td><td>0.00 ops/s<br /><details><summary>(-22.11%)</summary>Baseline: 0.00 ops/s</details></td><td>0.00 ops/s<br />(14.43%)</td></tr><tr><td><a href="https://bencher.dev/perf/zero/benchmarks/src-client-zero-bench-ts-basics-all-1000-rows-x-10-columns-numbe?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">src/client/zero.bench.ts > basics > All 1000 rows x 10 columns (numbers)</a></td><td>📈 <a href="https://bencher.dev/perf/zero?branches=6473a5a3-d381-4929-a977-1d3cf5eb05bc&heads=6d0bde35-0412-448c-9710-45bca20aaf44&testbeds=50a21deb-8296-4657-b7ad-5a4002b77d68&benchmarks=b46723ed-a942-4b77-ad67-e5557cfd9304&measures=fc802276-c2ed-462d-9fa5-0c1fcf2deb8e&start_time=1739273660000&end_time=1741865660000&report=7fa2b602-b232-4997-8496-a08e773ff31c&lower_boundary=true">view plot</a><br />🚷 <a href="https://bencher.dev/perf/zero/thresholds/406f4a01-508e-4a5d-a3a2-7771bcb183a1?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">view threshold</a></td><td>59.00 ops/s<br /><details><summary>(-26.41%)</summary>Baseline: 80.17 ops/s</details></td><td>56.94 ops/s<br />(96.51%)</td></tr><tr><td><a href="https://bencher.dev/perf/zero/benchmarks/src-client-zero-bench-ts-with-filter-lower-rows-500-x-10-columns?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">src/client/zero.bench.ts > with filter > Lower rows 500 x 10 columns (numbers)</a></td><td>📈 <a href="https://bencher.dev/perf/zero?branches=6473a5a3-d381-4929-a977-1d3cf5eb05bc&heads=6d0bde35-0412-448c-9710-45bca20aaf44&testbeds=50a21deb-8296-4657-b7ad-5a4002b77d68&benchmarks=f74ffb47-faeb-4174-abfd-4879417dc53c&measures=fc802276-c2ed-462d-9fa5-0c1fcf2deb8e&start_time=1739273660000&end_time=1741865660000&report=7fa2b602-b232-4997-8496-a08e773ff31c&lower_boundary=true">view plot</a><br />🚷 <a href="https://bencher.dev/perf/zero/thresholds/406f4a01-508e-4a5d-a3a2-7771bcb183a1?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">view threshold</a><br />🚨 <a href="https://bencher.dev/perf/zero/alerts/2d43586f-36ac-473c-816e-c75752b7e8f8?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">view alert (🔔)</a></td><td><b>59.80 ops/s<br /><details><summary>(-39.85%)</summary>Baseline: 99.43 ops/s</details></b></td><td><b>74.55 ops/s<br />(124.66%)<br /><br /></b></td></tr></tbody></table></details><a href="https://bencher.dev/perf/zero/reports/7fa2b602-b232-4997-8496-a08e773ff31c?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr%2Bcomments&utm_term=zero">🐰 View full continuous benchmarking report in Bencher</a><div id="bencher.dev/projects/zero/id/perf"></div>
author_association: NONE