Skip to content

Build rows lazily: components, minimum_spanning_forest, vector_topk, most_similar - #63

Merged
wfaithfull merged 5 commits into
mainfrom
m10-lazy-rows
Oct 24, 2026
Merged

wfaithfull merged 5 commits into
mainfrom
m10-lazy-rows

Conversation

@wfaithfull

Copy link
Copy Markdown
Contributor

These four SRFs built every row's external-id datum up front in the SRF's multi-call context, which holds it until the call ends. That cost about 72 bytes per id on text graphs, which showed in the full-scale soak (#62).

They now return a boxed iterator over the kernel's result (still in the extension's own try_reserved buffer) and the Arc<LoadedVersion<E>>. Each row's ids are built in next(), in the per-row context Postgres resets between rows, as bfs, vector_values and gather already do.

  • TDD: a #[pg_test] for each function compares the memory held by a 50k-row call and a 1k-row call. On main the 50k-row call held 3.6 MB (7.3 MB for the forest) against 0.13–0.17 MB; it now holds less than 256 kB more.

  • test/sql/shared_cache_lazy_rows.sql, in all three cache modes:

    • LIMIT;
    • a cursor read across statements after warren.evict;
    • an error mid-scan;
    • a cancel between rows.

    After each case, nothing is left in use or held.

  • Half scale (5M nodes), peaks with text ids: components 1.28 → 0.84 GB, minimum_spanning_forest 1.77 → 1.10 GB. Times are unchanged.

  • DESIGN.md §12 and §13: the fix is described as implemented. walk and neighbours still build rows up front, bounded by their limits and by a node's degree; they're listed for M11 with the reason.

Local gate: fmt, clippy, rustdoc; core 702; pgrx PG16 775 / PG18 776; run.py PG16 62/62 in all three modes; dump.py ok.


Generated by Claude Code

components, minimum_spanning_forest, vector_topk and most_similar built
every row's external id before returning the first, in the call's
multi-call memory context: about 72 bytes a text id, held until the
call ended (0.72 and 1.38 GB at 10M nodes). They now return an iterator
over the kernel's result, still in the extension's own buffer, and the
loaded version, and make each row's ids in next(), in the per-row
context Postgres resets. A pg_test for each opens a cursor on the call
and checks that what it holds after its first row does not grow with
the rows to come: 3.6 to 7.3 MB more for 50,000 rows before, now none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Rxfe2L3iVjSwSBPdUu1Ke
In every shared cache mode: a call stopped early, a cursor read across
statements after its version is evicted, a call that errors mid-scan
and one cancelled between rows each return what they should and leave
no version held.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Rxfe2L3iVjSwSBPdUu1Ke
The text peaks of components and minimum_spanning_forest fall to what
the version's size alone accounts for (0.84 and 1.10 GB from 1.28 and
1.77 at 5M nodes). Lists the SRFs that already streamed and the two
that still build their rows up front.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Rxfe2L3iVjSwSBPdUu1Ke
# Conflicts:
#	DESIGN.md
#	bench/results/soak/README.md
The full-scale text paragraph now says its components and
minimum_spanning_forest peaks were measured before rows were made as
they are returned, and points to the half-scale re-measure. walk and
neighbours, which still build their rows up front, are noted with why
and listed for M11.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Rxfe2L3iVjSwSBPdUu1Ke
@wfaithfull
wfaithfull merged commit 5721929 into main Oct 24, 2026
17 checks passed
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.

2 participants