Repository navigation
perf(api): read import sources in place instead of copying them into the session - #1923
Conversation
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Warning Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…truncation typed, open sources once (#1898) Review of #1923. An initial bulk build restarts unless every in-place source's digest is already recorded, so a stop between pinning the encoded inventory and recording the digests can no longer pair one file's graph with another's digest. A read that fails because the file changed under it reports that change. Registration opens the file once without following links and records from that handle. The receipt, the digest and the claim-order docs now state what each guarantees: content read under the identity pin, not a defence against a rewrite that preserves it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…g them (#1898) register_parquet records the canonical path, file identity, size, mtime and footer of a source and writes nothing under the session. Every read re-checks that identity, and the build's read pass computes the source SHA-256, recorded in the manifest and the import receipt. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…bytes (#1898) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…1898) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…1898) The bulk builder decodes row groups in parallel, so an ordered SHA-256 cannot be folded from its reads without holding the workers' lead in memory or reading most of the file again (measured 90%/96% re-read at S20/S22 with a 64 MiB bound). One thread per source reads it front to back concurrently instead. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…1898) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A static split starts one worker at each of several far-apart positions of a source, so a consumer that needs the bytes in file order (the whole-file digest of a registered source) must hold or re-read most of the file. Workers now take the lowest unclaimed task, so the reads of a source begin in file order. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… historical sessions working (#1898) The bulk build digested each in-place source with a thread that read the whole file a second time. Its tasks now read through a ChunkReader that offers every range to the source's digest, tasks are claimed in file order so the lead over the hashed prefix is bounded, and the digest reads only the bytes no decode asks for. Sessions an earlier version began (manifest format 2, a copy under sources/ and no external identity) resume, validate and append as before. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…cal sessions (#1898) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…truncation typed, open sources once (#1898) Review of #1923. An initial bulk build restarts unless every in-place source's digest is already recorded, so a stop between pinning the encoded inventory and recording the digests can no longer pair one file's graph with another's digest. A read that fails because the file changed under it reports that change. Registration opens the file once without following links and records from that handle. The receipt, the digest and the claim-order docs now state what each guarantees: content read under the identity pin, not a defence against a rewrite that preserves it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… consumed source is no longer read (#1898) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…1898) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…read units; an in-place import holds no input copy (#1898) The certify runner rejected the new receipt fields. It now accepts exactly construction.source_provenance (closed source kind, lowercase-hex SHA-256 digests, no paths or names) and the region work units observed_bytes and reread_bytes, in both the validator and the evidence schema. The ownership-growth check classed the import session as a data owner whose bytes follow the input, which was true only while the input was copied; it now requires that owner to grow by less than half of proportionally, and refuses a copy of the input. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
74c4b92 to
e348fc0
Compare
Closes #1898
Slice of #1881, decision 2.
register_parquetno longer copies or fsyncs the source into the import session. It records the canonical path, device/inode, size, mtime and Parquet footer identity, and writes nothing underimport-sessions/<uuid>/sources/. Both construction paths read the file where it is.What changed
GF_NOT_FOUND) or a replaced, resized or modified one (GF_IDENTITY_CONFLICT). Existing registration refusals (symlink,.., non-regular, limits) are unchanged.ChunkReaderoffers every range the decoder asks for (footer included) to the source's SHA-256 as it is read. In-order bytes are hashed at once, early bytes are held (bounded; runs coalesce), and only bytes no decode asks for (the offset index) are read afterwards. The digest therefore names the bytes that were decoded. It is recorded in the session manifest and the receipt (ImportConstructionEvidence::source_provenance, newImportSourceProvenance).graphforge-storage): workers now take the lowest unclaimed task (claim_in_order) in the node, edge and scratch-edge passes instead of a static split. A static split starts each worker at a far-apart offset, so an in-order consumer would have to hold or re-read most of the file. Results are returned in task order. This replaces an earlier revision of this branch that digested the bulk path with a separate thread reading the whole file a second time.sources/, no external identity or digest) still resumes, validates and appends, reading the copy it owns. Registering an in-place source into a format-2 session raises it to 3.docs/book/architecture/resumable-import.mdupdated.Evidence
each_source_byte_is_read_once_for_hashing_and_decoding(public facade, both routes) asserts that the digest's own read (reread_bytes) equals exactly the unread index bytes computed from the Parquet metadata, and the decode'sobserved_bytesis within 2% of the file size. Mutation: makingobservea no-op fails it and the staged unit test.registering_and_building_write_no_source_bytesuses the region write counters (registration wrote < 128 KiB for 5 MB of source; Arrow control writes the input into the session) and a content scan of the session tree. The same assertion run againstorigin/mainfails:registering 5462214 bytes wrote 5463904.tasks_are_claimed_in_index_order_on_every_passfails against a static split (first starts were[0, 32, 16, 24, ...]).Review follow-up (P1 findings, each verified by a test first)
Bulk resume, digest/graph mismatch: confirmed. A stop after the encoded inventory is pinned and before the digests are recorded, then a rewrite that keeps inode, size, mtime and footer, published graph A with the digest of B. Fix: an initial bulk build restarts (epic(storage): build initial graphs in parallel passes derived from the published generation #1881 decision 1) unless the digest of every in-place source is already recorded; a sealed construction session without one is discarded and rebuilt. A reused inventory keeps its recorded digests and the file is not hashed again.
Staged resume, prefix/suffix mix: confirmed only for a rewrite that preserves the whole pin. A normal edit between stop and resume (deleted, replaced, truncated, appended, touched) is already refused before any staged or encoded progress is reused, on both routes: now covered by
a_normal_edit_between_a_crash_and_resume_is_refused_before_progress_is_reused. The pin-preserving rewrite is not detected by design (pin = detector, digest = provenance); the receipt field and docs now say exactly that.Digest coverage: confirmed. A range the held-byte bound drops, and bytes no decode asks for, are read from the file when the digest completes.
ImportSourceProvenance::sha256, the module docs andresumable-import.mdnow state the guarantee: SHA-256 of the file content read under the identity pin.Should-fix: registration opens the file once without following links and records from that handle (no symlink swap window); a read that fails because the file changed under it keeps the typed
GF_IDENTITY_CONFLICT/GF_NOT_FOUNDerror; the held-byte bound claim was wrong (a slow task lets the others run ahead), so the claim-order andpending_limitdocs now say it helps in the usual case and the bound is enforced by dropping ranges.Re-review residue. A source is pinned until it is fully consumed: the frame that marks a source staged also carries its complete SHA-256, so a fully staged source edited or deleted before resume publishes its original rows with its original digest, and
commitaftervalidatereads no source (tests for both routes). A footer corrupted after the open is reported asGF_IDENTITY_CONFLICTwhen the pin no longer matches.CI follow-up. Windows compiles (the footer-corruption test no longer uses
std::os::unix). The certify runner accepts exactly the new receipt contract:construction.source_provenance(closed source kind, lowercase-hex SHA-256 digests, no path or name; nested or unknown shapes still refused) and the region work unitsobserved_bytesandreread_bytes, in the validator andcertification-evidence.json. The ownership-growth check treated the import session as a data owner that follows the input, true only while the input was copied; it now requires that owner to grow by less than half of proportionally and refuses a copy of the input.S20 / S22 measurements (all CONTENDED)
Full commands, binary sha256s, loads and logs: #1898 (comment). Base
c3551e1c4vs candidate7cfafbf20, separate release builds (cmpdiffers), inputs regenerated with the Graph500 generator (digests match the earlier recorded ones), two alternating pairs per scale.import-sessions/held 822,338,612 bytes after registration on base and 1,539 on the candidate. The candidate's digest read 157,752 bytes of 822,337,680 itself.register-parquet(nodes+edges), contended: S20 4.290 -> 0.102 s and 2.935 -> 0.045 s; S22 27.065 -> 0.075 s and 14.472 -> 0.051 s.commitcode varied 87-156 s between base runs) and are not attributable beyond the registration saving.