TanStack

Content temporarily unavailable

Group representative work and exact-value choice review

Evidence by revision, on perf-aggregate-representatives:

  • First campaign: tests ef9d4dad4, production change 9b021bbdc. The RED results ran on ea51b67b0 with the new tests.
  • Review follow-up: tests 871bdea11, production fix e5449edc2. The RED results ran on 9b021bbdc and the GREEN and mutant results on e5449edc2. f67789cd3 merges origin/main without changes to these files.

Laws and authority

  1. Work law. The work that a grouped aggregate does for one inserted or deleted member does not depend on the number of members that the group holds. The law applies to a groupBy aggregate and to an aggregate inside an include, which the compiler groups by its correlation route. Authority: incremental view maintenance. A count changes by the multiplicity of the delta, so the result does not need a re-read of each member.

  2. Route law. One representative carries the whole correlation route. The route has two fields, correlationKey and parentContext. Members of one route share the correlation key instance and the parent context instance, so the representative's identity is the exact identity of those two instances. The first campaign used the parent context's equality identity instead, which does not distinguish exactly different parent values (review finding F4).

  3. Positive-contributor law. A projected group value, and a min or max result, is an exact value that a currently positive member holds (ARCHITECTURE.md, "Value identity"). D2 consolidates contributions whose hashes match, its hash treats -0 as 0 and equal Dates as one value, and a consolidated entry keeps the record of its latest change. Thus a contribution must carry a key that separates every value it can supply. A primitive's key is its exact value. An object's key holds its row key, because a rebuilt argument is a new instance each time.

  4. Group-value law (revised by a product decision). When several members are equal under query equality but differ exactly, the projected value comes from the member with the smallest exact value:

    • another number before -0, and every primitive before an object;
    • objects by an explicit type tag: Buffer, Date, a Temporal type, then Uint8Array. The tags do not come from constructor names, so minification cannot change the order (review finding F7).

    -0 and NaN are never equal under query equality, so their order is not observable. Members of one tag are equal in content, and any positive instance can be projected. The previous law selected the member with the smallest row key.

Old and new predictions

The six cases in group-by.test.ts (groups %s by query equality, autoIndex off and eager). Each case observes three checkpoints: both members present, after the delete of row 1, and after the reinsert of row 1. Arrival order is row 1, then row 2.

Members (row 1, row 2)Old predictionNew prediction
Date(0), 0Date, 0, Date0, 0, 0
invalid Date, NaNDate, NaN, DateNaN, NaN, NaN
-0, 0-0, 0, -00, 0, 0

The test now also runs each case with the members in reversed order. A Buffer and a Uint8Array with the same bytes passed under the old code only in the original order. In the reversed order, the old code projected the Uint8Array.

Evidence

  • Work law. group-by-work.test.ts counts the Map and Set iterator steps in one synchronous commit, at 10, 100, 1,000 and 5,000 members. On ea51b67b0, a groupBy count took 157 steps at 100 members against 67 at 10 members, and an include count took 219 against 129. On the branch, each count takes the same number of steps at every size: 55 and 117 in the probe runs. The published count is also checked at each size.
  • Route histories. The same file deletes the member that arrived first, empties and refills a route, adds a null correlation key, moves a member between routes, and updates the parent. Each history checks the published count. The histories pass on ea51b67b0 and on the branch, because the change keeps the result and changes only the work.
  • Group-value law. The test model smallestExact orders values by kind and type name without production code. On ea51b67b0, 8 cells fail. For example, expected 'date' to be 'number'.

Mutants

MutantOutcome
Row key restored in the route representativeAssertion failure: the include work law (219 against 129)
Row key restored in the groupBy representativeAssertion failure: the groupBy work law and 6 group-value cells
Largest exact value selectedAssertion failure in 8 group-value cells
First member that arrived selectedAssertion failure in 8 group-value cells

ORC-012 requirement audit

RequirementOutcome
ORC-001Applicable. The three laws and their authority are above. The group-value law is a product decision of this change.
ORC-002Applicable. The expected counts come from the fixture sizes. The expected group value comes from the test model smallestExact, which does not use production identity or serialization.
ORC-003Applicable. Both test files state each law, its observation and its checkpoint before the assertions.
ORC-004Applicable to the finite matrices only. No generated grammar is claimed. The sizes 10, 100, 1,000 and 5,000 bound the work law.
ORC-005Applicable. The tests use the public live-query Collection and its published rows at the return of the commit.
ORC-006Applicable. The four mutants above fail at the intended checkpoints.
ORC-007Inapplicable. These tests are finite, not generated properties.
ORC-008Inapplicable. No stateful reference model changes.
ORC-009Applicable. "Iterator step" is a test observation, not a production concept. "Exact value" is the value relation that ValueIdentity.exact keeps: primitives by value, with -0 and NaN kept apart, and objects, including Dates, binary arrays and Temporal values, by instance. The group-value order compares objects by type tag only; the earlier text of this row said that exact identity compares them by content, which was false.
ORC-010Applicable, with a gap. Cleanup runs in finally blocks, so a cleanup error can replace an assertion error.
ORC-011Inapplicable. No shared fault was named.
ORC-013Applicable. The work law is a scaling law. Four sizes over two orders of magnitude separate a constant cost from a cost per member.
ORC-014Inapplicable. No controlled provider supplies a premise.

Review follow-up

A high-effort review of 9b021bbdc raised ten findings. Probes and the extended oracle confirmed the product defects:

FindingVerdictEvidence on 9b021bbdcDisposition
F1 min/max returns a deleted row's valueConfirmedRows 0 and -0, delete 0: min and max return 0. On main: -0. Two equal Dates: the deleted row's instance.Fixed: contributions carry the exact identity of each min or max input. This row first named sum and avg too; the medium follow-up removed them, and the targeted follow-up keys object inputs by row
F2 projected value is a deleted row's instanceConfirmedTwo Date(0) instances, delete row 1: the deleted instance is projectedFixed: the representative key holds the instance identity
F3 oracle compares only a type labelConfirmedThe F2 defect passed the old oracleFixed: the oracle requires a positive member's instance
F4 route identity uses parent equalityEvidence gapAn include aggregate cannot project a parent field, so no public observation was found. The mutant that restores the equality identity survivesUses the parent context instance; recorded as unobserved
F5 Date and binary correlation keys never consolidateConfirmed, acceptedReference identity keeps distinct key instances apartLimit below: correctness needs instance identity
F6 work law over-promisesConfirmedOnly identical inputs consolidateThe law, changeset and architecture text now state the scope
F7 order depends on constructor names and serializationConfirmed by source—Fixed: explicit type tags
F8 binary contents serialized into each keyConfirmedA 1 MiB group value is encoded twice per insert (12.6 MB); main encodes it onceFixed: once, which is the group key's existing cost
F9 counter misses array walksConfirmed by source—Fixed: the counter also counts array iteration and callbacks
F10 architecture text contradictedConfirmed—Rewritten

Measured with the investigation probe (NODE_ENV=production, an include count over one issue, median per comment insert, two runs each):

Commentsmain 2c98b4992Branch f67789cd3
100.109 / 0.125 ms0.168 / 0.162 ms
1000.096 / 0.095 ms0.129 / 0.113 ms
1,0000.175 / 0.153 ms0.098 / 0.108 ms
5,0000.557 / 0.439 ms0.101 / 0.091 ms

The 10-comment size runs first in each process, so it includes warm-up.

Review mutants on e5449edc2:

MutantOutcome
No exact-input identity (merged retraction kept)Assertion failure, 4 tests
No instance identity in the group-value representativeAssertion failure, 4 tests
Binary contents in the order keyAssertion failure, 1 test (binary encoding)
Parent context equality identity in the routeSurvived: no public observation (F4)
NaN ordered before -0Equivalent: the two never share a group

Limits

  • The work counter observes Map, Set and array iteration and array callbacks. A walk through an indexed for loop would not count.
  • Contributions consolidate only when their representative keys and min or max inputs are identical. A min or max over objects keeps one contribution per member. A min or max over distinct values, and an include correlated on Date, binary, or Temporal key instances, keep one contribution per distinct input or instance.
  • The group key serializes a large binary group value's contents once per member. That cost predates this change.
  • A groupBy over values whose exact identity is a reference, such as plain objects, still has one contribution per distinct object. Equality for those values is also by reference, so each such group holds one value.
  • The fixed per-change overhead from #1740 is a separate cost and is outside this change.

Medium review follow-up (2026-10-08)

Reviewed head 14d5cd49b. Fix commits 8db25f3b0 (laws) and b86aaa7e2 (production). Ledger: review-medium-ledger.md in the task scratch notes.

  • Rebuilt min/max argument. An inline subquery rebuilds projected objects each time it runs, so the retraction of a row carried a new argument instance. The min or max contribution was keyed by that instance and did not cancel its insert. RED on 14d5cd49b: after inserting and deleting a row with x: 7, max stayed 7. The base commit before this pull request did not have the fault. The identity now comes from the value the min or max compares (minMaxInput), and the work law keeps cycle work constant over 300 insert and delete cycles.
  • Sum and avg no longer carry exact inputs. Their reduce adds coerced numbers, so merging equal inputs cannot change the result.
  • Equal instances. Among content-equal object group values, the member with the smallest row key supplies the instance. RED on 14d5cd49b: when rows arrived as 2, 1, row 2's instance was projected. The choice no longer depends on arrival order.
  • Test model. exactRank now matches the documented order: another primitive, then -0, then objects by type tag read with Object.prototype.toString.
  • Include work law covers insert, an update that moves a member out and back, and delete.
MutantOutcome
Exact input from the raw argument (14d5cd49b)Assertion failure: max keeps the deleted value
No min or max exact inputsAssertion failure, 4 tests
Instance token as the tie among equal objects (14d5cd49b)Assertion failure: arrival order 2, 1
Row key in the route representativeAssertion failure: include work law

Not changed:

  • A parent field in an include aggregate's select is not projected into the result on this commit or the base, so the route's parent context stays without a public observation.
  • A min or max argument compiles twice per query, once for the aggregate and once for its exact input. This is a compile-time cost only.
  • db-ivm merges hash-equal values by design. Choosing which instance remains needs per-instance state, which the compiler identity supplies, so the fix stays in the compiler.

Targeted review follow-up (2026-10-08)

Reviewed head 28a2f8c79. Ledger: review-targeted-ledger.md in the task scratch notes.

  • Rebuilt Date min/max (regression). The medium follow-up keyed a min or max contribution by the exact identity of the compared value. A rebuilt Date is a new instance at each evaluation, so a retraction did not cancel its insert, and the Index kept the deleted row's instance. RED on 28a2f8c79: 6 cells, for example expected [ 'Date(2000)' ] to include 'Date(5000)'. The base before this pull request passes all cells. An object input is now keyed by its row key, as a group value is.
  • Law. min and max equal the remaining members' values deletes members one at a time. After each delete, the published min and max must be exactly one of the remaining members' values that ties for the extreme. The matrix covers max alone, min and max of one argument, and of two arguments; signed zero and Dates; stored and rebuilt arguments.
  • Realm. group-by.test.ts runs in the node environment. Under jsdom a Node Buffer is not an instance of jsdom's Uint8Array, so the binary case could not see the Buffer tag.
MutantOutcome
Production of 28a2f8c79Assertion failure, 6 cells (rebuilt Dates)
max without exact inputsAssertion failure, 8 cells (signed zero)
No Buffer tagAssertion failure, 2 tests (expected 'uint8array' to be 'buffer')
Object input keyed by instance, not rowAssertion failure, 6 cells

Not changed, and recorded as proposals:

  • An include whose parents are equal under query equality but differ exactly shares one route, so a parent field can come from another parent. This predates this pull request.
  • A single-group aggregate drops every non-aggregate select field, also in an include, while the same select with groupBy throws NonAggregateExpressionNotInGroupByError. This predates this pull request.
  • db-ivm min and max ignore multiplicity, and the Index keeps the latest record. The compiler's exact inputs work around both.
  • The route representative is not covered by a deterministic-choice law.
  • The work counter does not count indexed loops.