Skip to content

Fix builder disposal and keep collection views additive - #44

Merged
danReynolds merged 5 commits into
feature/collectionViewfrom
claude/collection-view-fixes
Sep 30, 2026
Merged

danReynolds merged 5 commits into
feature/collectionViewfrom
claude/collection-view-fixes

Conversation

@danReynolds

Copy link
Copy Markdown
Owner

Follow-ups to #41 from its review and the comparison with #43, keeping views read-only. There is one commit per change so any of them can be dropped; each passes flutter analyze and the core suite on its own.

  1. Dispose only the observers stream builders create (2bae11c). observe() on an observed document or query returns the same observer. A builder given one disposed the caller's observer when the builder was removed, closing it for every other listener, so a second builder sharing it silently stopped updating. Builders now dispose only observers they created. Disposing on a source switch had the same problem and predates Add live read-only subtype collection views #41; that is fixed too. test/core/stream_builder_test.dart covers both, and its caller-owned cases fail on Add live read-only subtype collection views #41.

  2. Construct collection views only through view() (6d007f8). CollectionView<String>(animals) compiled and silently matched nothing. The constructor is now private, so view<S>(), which bounds S at compile time, is the only way to create a view.

  3. Compare collection views by collection and subtype (0fb3f70). Views compared by identity, so a view created inline in build made QueryStreamBuilder observe and rescan the collection on every rebuild. Views of the same type over the same collection now compare equal, as collections compare by path and document views by path and type.

  4. Give views their own stream builders (a033225). Add live read-only subtype collection views #41 changed the DocumentStreamBuilder and QueryStreamBuilder callbacks to read-only snapshots for every caller, so existing builders could no longer write through snap.doc, and Queryable.toQuery() changed type. Those builders keep their signatures from main. DocumentViewStreamBuilder and QueryViewStreamBuilder build from views, and also accept ordinary documents and queries through the read interfaces they implement. With this, the view layer only adds API: the example app is back to its main version, and 6.0.0 code that edits through snap.doc in a builder compiles unchanged. This is the one design change here; drop the commit to keep the unified builders.

  5. Sort a view's projected snapshots instead of projecting in the comparator (2440e9d). The view keeps its sort and applies it to its projected snapshots, so comparisons no longer look up projections. In interleaved local runs over 2,000 matching documents, get(), reads after a write, and stream emissions were faster in every round, by roughly 2–2.7× at the median. The machine was under heavy load, so the absolute timings are not meaningful.

Validation

CI only runs for pull requests into main, so these ran locally at the branch head:

  • 189 core and 90 native tests pass. The collection view and builder suites pass in Chrome (27), as does the web suite (42). Both CI benchmark workload checks pass, and flutter analyze reports no issues for the package or the example.
  • Each commit passes flutter analyze and the core suite on its own.
  • The review's randomized comparison of views against plain observers of the parent collection, 400 seeds × 40 batches, still finds no divergence after the sorting change.
  • The new tests fail without their fixes: the caller-owned observer tests against Add live read-only subtype collection views #41's builders, and the equality and rebuild tests with the equality reverted.

🤖 Generated with Claude Code

danReynolds and others added 5 commits September 29, 2026 22:20
`observe()` on an observed document or query returns the same observer, so
a builder given one was disposing the caller's observer when it was removed
or switched sources, closing it for every other listener. The builders now
dispose an observer only if they created it.

Removal is the case this branch introduced; disposing on a source switch
predates it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The public `CollectionView<S>(collection)` constructor accepted any type, so
`CollectionView<String>(animals)` compiled and silently matched nothing.
`view<S>()` requires S to extend the collection's type at compile time, and
is now the only way to create a view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A collection view compared by identity, so a view created inline in `build`
made `QueryStreamBuilder` observe and scan the collection again on every
rebuild. Views of the same type over the same collection select the same
documents, so they now compare equal, as collections compare by path and
document views by path and type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Accepting views in DocumentStreamBuilder and QueryStreamBuilder changed their
callbacks to read-only snapshots for every caller, so existing builders could
no longer write through `snap.doc`, and `Queryable.toQuery()` changed type.
Those builders keep their signatures from main. DocumentViewStreamBuilder and
QueryViewStreamBuilder build from views, and also accept ordinary documents
and queries through the read interfaces they implement.

With this, the view layer only adds API: no existing signature changes, and
the example app is back to writing through `userSnap.doc`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ator

A view's sort ran in the underlying query, with a comparator that looked up
both snapshots' projections on every comparison. The view now keeps its sort
and applies it to its projected snapshots, so each snapshot's projection is
looked up once per read. In interleaved local runs over 2,000 matching
documents, `get()`, reads after a write, and stream emissions were faster in
every round, by roughly 2-2.7x at the median.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant