Skip to content

Build rows lazily: walk and neighbours (M11) - #66

Merged
wfaithfull merged 1 commit into
mainfrom
m11-lazy-walk
Oct 24, 2026
Merged

wfaithfull merged 1 commit into
mainfrom
m11-lazy-walk

Conversation

@wfaithfull

Copy link
Copy Markdown
Contributor

This finishes the lazy-rows work from #63. warren.walk and warren.neighbours now build each row's external ids in next(), in the per-row context, instead of up front in the multi-call context.

  • walk:
    • The walk still runs in full in the first call, now through warren-core's traversal::walk. Its result (end dense ids, or each path's nodes and relations) stays in warren-core's try_reserved buffers, bounded by max_results and warren.max_walk_paths as before.
    • next() builds the end id, the path array and the relation names. It reads only the type cache: no SPI, search_path or identity switch.
    • The adapter's own row buffer is gone.
  • neighbours:
    • A new warren-core Adjacency::collect_neighbours collects one node's distinct neighbours as dense ids into a buffer reserved up front: 8 B per neighbour, bounded by the node's degree. Before, about 72 B per text id was held.
    • The iterator holds the Arc<LoadedVersion>.

Memory held after the first row, 50,000 vs 1,000 rows (TDD: these tests fail on main):

Call Main This branch
neighbours 3.72 MB vs 0.13 MB 88 KB vs 54 KB
walk ends 3.72 MB vs 0.14 MB 90 KB vs 56 KB
walk paths 17.9 MB vs 0.41 MB 90 KB vs 56 KB

test/sql/shared_cache_lazy_rows.sql now covers both functions in all three cache modes: LIMIT, cursors read across statements after warren.evict, an error at row 5,000, and a cancel between rows. DESIGN §12 and §13 are updated.

Gate:

  • fmt, clippy, rustdoc: clean.
  • core: 703 passed.
  • pgrx PG16 / PG18: 777 / 778 passed.
  • run.py PG16, all three modes: 62/62.
  • dump.py: ok.

Generated by Claude Code

walk built every row's external ids as the walk ran, and neighbours built
a node's neighbours' ids inside a closure over the loaded version, both
in the call's multi-call memory context, held until the call ended. walk
now keeps warren_core's walk result (the ends' dense ids, or each path's
nodes and relations) in its reserved buffers and neighbours collects the
node's distinct neighbours as dense ids (Adjacency::collect_neighbours);
each returns an iterator holding the loaded version that makes a row's
ids, path array and relation names in next(). A pg_test for each opens a
cursor on a call over a star of 50,000 or 1,000 IRI leaves and checks
what it holds after its first row does not grow with the rows to come:
3.7 MB (neighbours, walk ends) and 17.9 MB (walk paths) more for 50,000
before, 34 KB after. shared_cache_lazy_rows.sql covers both under LIMIT,
in cursors read after warren.evict, and erroring or cancelled mid-scan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Rxfe2L3iVjSwSBPdUu1Ke
@wfaithfull
wfaithfull merged commit 876181f 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