Bound indexing peak memory by streaming typed persistence mapping #38
Labels
No labels
area/cli
area/core
area/database
area/indexing
area/query
type/epic
type/feature
type/foundation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
JayKobe6k/ClariTree#38
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem and impact
Index publication currently materializes a complete persistence
MetadataGraphwhile retaining the complete application-ownedProjectIndex. On a bounded 200-translation-unit workload, this nearly doubled process peak RSS before any LadybugDB write occurred. This amplification increases OOM and swap-pressure risk for large projects and prevents safely validating the full 1,103-TU melee workload on a busy workstation.Environment and workload
eccc45a8aed2fd9ebe6334b4975d83cdb0751defplus analyzer-only performance changes from #37; persistence mapping itself is unchangedmelee-event-matchat6b6f8a8a987c2e871b8b96baa863aaa99e406f33tests/profile_lineage.rs, with optionalCLARITREE_PROFILE_METADATA_MAPPING=1MetadataGraph::from_project_indexbut does not open or write a databaseBaseline measurements
Same candidate binary and corpus, sequential runs:
Metadata mapping added 995,636 KiB to peak RSS, an 87.3% increase over the parse-and-lineage peak. Both runs produced exactly 169,045 definitions, 180,182 uses, and 400,939 relationships; mapped definitions also totaled 169,045. Global host swap remained below 30 GB of 110.71 GB total and decreased during the experiment.
Root cause
ProjectIndexowns every indexed compilation unit and the complete analyzed lineage.MetadataGraph::from_project_indexmaps all compilation metadata, source facts, value nodes, and relationships into additional owned vectors.publish_index_with_directory_syncbuilds that complete graph from a borrowedProjectIndexat line 517, then retains both while opening staging storage and writing at line 536.write_metadatawrites in bounded database batches, but those batches do not bound the already-materialized graph.The causal mechanism is duplicated ownership of large source and lineage representations, not LadybugDB cache allocation: the measured peak occurs when only in-memory mapping is enabled and no database is opened.
Proposed implementation
Preserve
ProjectIndexas the application-domain input and move bounded record generation behind the LadybugDB adapter:Alternatives considered:
Arcsharing across domain and persistence records was prototyped during #37 and did not reduce measured peak RSS; it also expanded API risk.ProjectIndexduring mapping could reduce coexistence but would ripple through the borrowed repository port and still materialize a second complete graph. Streaming typed records offers a bounded-memory contract and retains adapter ownership.Expected benefit, confidence, and risks
Acceptance criteria
Related: #37 reduces semantic-analysis CPU time; this issue independently owns persistence-mapping peak memory.
Correction to the
write_metadatasource link in the issue body:LadybugMetadataStore::write_metadata. The original URL omitted.com.Full-corpus memory evidence after analyzer CPU remediation
The analyzer changes tracked by #37 made it possible to complete the full 1,103-translation-unit in-memory workload under a bounded cgroup. Persistence mapping itself is unchanged.
Environment and procedure
eccc45a8aed2fd9ebe6334b4975d83cdb0751defplus the uncommitted analyzer candidatemelee-event-match6b6f8a8a987c2e871b8b96baa863aaa99e406f33tests/profile_lineage.rswithCLARITREE_PROFILE_METADATA_MAPPING=1MemoryHigh=24G,MemoryMax=32G,MemorySwapMax=8G; command timeout 1,700 sResult
No cgroup swap was observed during polling. Global swap remained below half of host capacity and decreased over the experiment.
This run does not isolate the full-corpus mapping increment because
/usr/bin/timereports one process-wide maximum, but it establishes the operational scale of the current complete-graph design:ProjectIndexand aMetadataGraphcontaining 1.145 million definitions, 1.371 million uses, and 3.598 million relationships coexist, producing a 15,315,736 KiB process peak. The earlier paired 200-TU experiment remains the controlled attribution evidence: mapping added 995,636 KiB (+87.3%) before any database open.Implication
The full workload now fits the approved 32 GiB envelope, but a 14.5 GiB peak for in-memory mapping alone remains too large for routine indexing and leaves database staging/write memory unmeasured. Streaming typed persistence remains justified; capacity expansion is not a substitute. The next validation should compare complete publication before/after streaming with bounded chunks, preserving the existing 1,103-TU counts and atomic rollback behavior.
The profiler now emits parse and analysis completion records before mapping, so a future timeout can be localized without another blind run. This issue should remain open.
Candidate implementation and validation update (uncommitted working tree based on
912603680d):Implementation:
200-TU mapping results:
Comparable end-to-end publication, 12 GiB MemoryMax, 11 GiB MemoryHigh, MemorySwapMax=0:
9126036writer: persistence 375,207 ms; 8,001,724 KiB peak RSS.Validation completed through Nix:
Tests include one-row chunk parity against the full-graph writer, repeated COPY calls, all relationship families, exact fingerprint parity, same-count corruption rejection in writable and read-only modes, source-fact validation, and staging rollback.
Remaining bounded state is the complete SourceFactGraph plus compact lineage endpoint/type/projection indexes. Transient typed lineage database records and relationship CSVs are bounded by configured batch sizes. End-to-end peak is now dominated by the configured 4 GiB LadybugDB buffer pool and persisted source-fact workload rather than full MetadataGraph coexistence.
Implementation candidate status (uncommitted)
The production publication path no longer calls MetadataGraph::from_project_index. It maps revision and source metadata once, then maps and writes value definitions, uses, declaration/ownership relationships, and all six lineage families in configured typed chunks. Expected counts and order-independent, domain-separated SHA-512 multiset fingerprints are accumulated while mapping and independently validated in writable staging and after read-only reopen. Counts remain a separate cardinality check. Tiny-chunk parity, same-count endpoint/certainty corruption, rollback, publication, and complete source-fact tests pass.
Measured first-200-TU results on the pinned melee-event-match corpus:
A review identified that RevisionMetadataGraph still owns the project-sized SourceFactGraph and source identity indexes. This is intentional current scope: the measured persistence duplication has been removed, transient lineage records and COPY files are chunk-bounded, and experiments that skipped source-ID validation did not materially change peak RSS. Further source-fact redesign is not justified without a profile locating material retained cost.
The complete required Nix validation passes: format, Clippy, 273 unit tests plus all integration suites, rustdoc, Windows GNU cross-check, and git diff --check. A compatibility review also removed the new public LadybugAdapterError enum variant; fingerprint mismatches now use the existing typed UnexpectedRow/ValidateMetadata surface.
Implemented and pushed to
mainas654b2fbcf8e3ccc7ce7428b637d8b2736f67de19. The follow-up typed node COPY optimization is delivered as32959f55abed0b7bd92d94670fcf9c9abb452a1eand tracked by #40.Streaming mapping reduced the bounded 200-TU mapping peak from 2,136,228 KiB to a 1,240,200 KiB median while preserving 169,045 definitions, 180,182 uses, and 400,939 relationships. Publication, rollback, fingerprints, read-only reopen validation, tests, documentation, and Windows cross-compilation all passed through Nix.
JayKobe6k referenced this issue2026-09-12 18:18:23 +00:00