soa_verbatim: pin the physical layout, correct the compression claim, prove it over S3 - #907
Conversation
…surement
The plan (.claude/plans/idle-flush-dataset-eviction-v1.md) is a PROPOSAL whose
own §9.1 names the first task: the §4 verification gate — 'cheap local version
read, ASSUMED, UNCHECKED'. This probe measures it instead of assuming it, plus
three other inputs the plan grades as unmeasured:
(1) §4 gate — Dataset::open vs latest_version_id, three sizes, warm.
lance 9 documents latest_version_id as a fast path; whether
the ratio is DECISIVE is what the gate actually asks.
(2) §1 cost — file count + wall time per hydration. The plan states its own
cost model omits request count because 'a dataset is a
multi-file directory'; this counts the files.
(3) §0/§5 — the ~1.4 s rehydration figure is graded a single observation.
Three sizes so it lands on a curve, not a point.
(4) T10 — flush -> rehydrate -> read equality, by full-scan checksum
(a truncated hydration cannot pass a row-count check).
Storage options are built explicitly: object_store reads AWS_ENDPOINT while
this environment sets AWS_ENDPOINT_URL, so from_env would silently address AWS
proper and fail as if it were a credential problem.
Measures only. Implements no eviction and authorizes none; the policy stays a
proposal. Removes only the bytes it wrote, and says so for the remote ones.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
…against size-weighted eviction Ran crates/lance-graph/examples/hydration_probe.rs against the configured endpoint (lance 9.0.0, 0.3-33.5 MB, single-fragment). §4 GATE CLOSES, and it had two ways to pass — both hold. latest_version_id() resolves a manifest LOCATION in 8-11 us, no manifest read, no data read, FLAT across a 100x size span; Dataset::open is 0.23-0.29 ms, also flat. So the version read needs no open AND the fallback would have sufficed (1,000 candidates in 0.27 s). §9.1's blocker is discharged. THE CORRECTION FROM THE SAME RUN. Hydration is ~2.63 s fixed + ~0.021 s/MB: 100x the bytes costs 1.26x the time. §2 declined a size-weighted eviction key because 'rehydration cost is also size-proportional, so it preferentially evicts what is most expensive to get back'. In the measured range that is false — the cost is fixed-dominated. This removes one ARGUMENT, not the decision; §0's row now reads 'CONJECTURE, and its stated ARGUMENT is refuted' rather than being promoted either way. Also: T10 green at all three sizes by full-scan checksum (a row count would have passed a truncated hydration); request count measured at 3 remote objects per dataset, which bounds the small case only. The probe's own error direction is stated in its module doc: every timing flatters the fast path, so a marginal result would have been a FAILED gate. Board hygiene in the same commit: EPIPHANIES prepend (E-HYDRATION-IS-FIXED-COST-NOT-SIZE-COST-1), plan §0/§8a/§9 updated, and the plan header no longer claims 'nothing here is measured'. Adds lab/s3rm.py — s3put.py could only write, so the probe had no way to remove its own remote scratch; it now leaves none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
…E the audit runs Written ahead of the measurement, deliberately: the audit agents load these, so the question and its decision table exist before any answer does. .claude/knowledge/lance-cache-surface.md — the operator's actual question (S3 sink-in to disk vs "radikal moka flushen": which mechanism bounds the RAM a Railway deployment is billed for), an evidence table with eight FINDINGs already measured this session (moka unconditional in lance-core; aws gates only the provider; with_capacity is byte-weighed; no_cache exists; CacheBackend is a trait with only moka shipped; lance mmaps nothing; no alignment guarantee on the read path; try_from_iter copies where from_vec adopts), three OPEN probes (P-CACHE-1 contents, P-CACHE-2 reachability, P-CACHE-3 empirical), and the traps already paid for so they are not re-paid. .claude/agents/lance-cache-contents-auditor.md (Opus) — classify the cache by its INSERTS, never by its name; verdict vocabulary DATA-CACHED/METADATA-ONLY/ MIXED. Grounded in the a27b06a incident: a capacity lever proves nothing about what it levers. .claude/agents/lance-cache-cartographer.md (Opus) — a knob exists only if a consumer can turn it without forking; verdict per knob as a public call chain hop-by-hop or UNREACHABLE with the break point. Grounded in the same-day S3 provider trap, and fenced by E-LANCE-IS-UPSTREAM-AUTHORITATIVE-1: an unreachable knob is an upstream ask, never a fork. Findings land in a follow-up commit once the audit and the empirical probe have run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
…able header Answers the operator's requirement directly: "nur das 512-Byte-SoA-Schema muss dann in den table headers vermutlich einmalig sauber persistiert werden." The header carries the contract as Arrow schema metadata (which Lance persists in its manifest) — envelope_layout_version, row_stride, row_carving, endianness, classid, slab_digest, source — every load-bearing value IMPORTED from lance-graph-contract (ENVELOPE_LAYOUT_VERSION, NODE_ROW_STRIDE), never restated, so it cannot drift from the compiled contract the way canonical_node.rs's own doc comment already drifted once this session (claiming arrow-rs allocates 64-byte aligned, which the S3 read path measured false). Same call for local and s3://, per the earlier finding that there is no export step — WriteParams::store_params carries S3 credentials when the URI is remote, None when it is not. Zero-copy import, asserted not assumed: Buffer::from_vec adopts the Vec's allocation (pointer-identity checked against the pre-move address) rather than FixedSizeBinaryArray::try_from_iter's chunk-by-chunk copy — the same distinction measured and then reverted out of this branch once already (commit a27b06a's history). Row column written with lance-encoding:compression=none — a documented passthrough — because an uncompressed fixed-width column should land as a verbatim byte run in the .lance data file, which is the precondition for mmap-based serving (pattern b in the operator's a/b/ split). CLAIMED, not trusted: for a local uri the binary scans its own written data file for the slab's first 4 KiB and reports whether the run is contiguous — this is P-CACHE-4 in .claude/knowledge/lance-cache-surface.md, added to the probe queue there in a follow-up commit. Verification on write: re-opens what it just wrote and asserts the read-back layout version and digest match what was written, before printing success — the same field-isolation discipline as the rest of this branch's Lance work. Build in progress against the full lancedb/datafusion tree; results and any fixes follow once it completes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
… prove it over S3
The verbatim-slab claim needed a test and a false doc corrected:
- crates/lance-graph/tests/soa_verbatim.rs: physical-layout assertions (byte-
contiguous, per-row addressing, bounded footer), an anti-vacuity companion,
a weaker round-trip check, and a header-contract check — plus two new
falsifiers:
- the_narrow_column_falsifier: proves the byte search can actually detect
compression by dropping below Lance's mini-block cutoff, where the
lance-encoding:compression metadata IS honoured.
- a_slab_is_written_verbatim_to_s3_too: the same physical-layout assertion
run against the real S3-compatible store, using the same AWS_* variable
names a Railway deployment already sets (AWS_S3_BUCKET_NAME included) —
no new environment key invented.
- Corrected a false claim in soa_to_lance.rs and soa_verbatim.rs: the
lance-encoding:compression = "none" field metadata is NOT what keeps the
512-byte row column verbatim. Measured: removing it, or setting "zstd",
leaves the file byte-identical. Root cause, read from lance-encoding 9.0.0
source: a 512-byte value clears the mini-block "narrow" cutoff (256 bytes)
and takes the full-zip path, whose FixedWidth per-value compressor ignores
field metadata unconditionally. The stride is what buys the verbatim write;
the metadata line is kept as a documented backstop, not the cause.
- EPIPHANIES.md: recorded the correction and the S3 proof.
|
Warning Review limit reached
Next review available in: 16 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (8)
📝 WalkthroughWalkthroughThe PR adds Lance cache investigation instructions and knowledge, measures local and S3 hydration behavior, adds a SoA-to-Lance executable, and adds tests for contiguous storage, compression behavior, metadata, and round-trip integrity. ChangesCache and SoA verification
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant HydrationProbe
participant S3Endpoint
participant FreshLocalDirectory
HydrationProbe->>S3Endpoint: Upload generated dataset
HydrationProbe->>S3Endpoint: Load remote dataset
S3Endpoint-->>HydrationProbe: Return dataset batches
HydrationProbe->>FreshLocalDirectory: Write hydrated batches
HydrationProbe->>FreshLocalDirectory: Scan rows and compute checksum
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
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 |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_7f46e11f-b0d7-4849-9129-0c05eca2af2d) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1a24a47a7f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (6)
.claude/board/EPIPHANIES.md (1)
38-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMake the hydration finding a separate Markdown heading.
Line [38] is a paragraph, not a heading like
### E-COMPRESSION-META-INERT-AT-512-STRIDE-1. This makes the hydration finding harder to identify in the board and in heading-based tooling. Add the repository’s canonical heading marker.Proposed fix
- — E-HYDRATION-IS-FIXED-COST-NOT-SIZE-COST-1 — the idle-flush plan's §4 blocker closes, and the argument it used against size-weighted eviction is refuted by the same probe +### E-HYDRATION-IS-FIXED-COST-NOT-SIZE-COST-1 — the idle-flush plan's §4 blocker closes, and the argument it used against size-weighted eviction is refuted by the same probe🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.claude/board/EPIPHANIES.md at line 38, Convert the hydration finding beginning with E-HYDRATION-IS-FIXED-COST-NOT-SIZE-COST-1 into a separate Markdown heading using the repository’s canonical heading level and marker, matching the format of nearby epiphany headings such as E-COMPRESSION-META-INERT-AT-512-STRIDE-1.crates/lance-graph/examples/soa_to_lance.rs (2)
173-176: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winThe carving string restates the stride.
The module doc states the load-bearing values are imported, not restated.
value:32..512hard-codes 512. IfNODE_ROW_STRIDEchanges, the assertion at line 238 still passes while the persisted carving is wrong.♻️ Proposed fix
- ( - K_CARVING.to_string(), - "key:0..16|edges:16..32|value:32..512".to_string(), - ), + ( + K_CARVING.to_string(), + format!("key:0..16|edges:16..32|value:32..{NODE_ROW_STRIDE}"), + ),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/lance-graph/examples/soa_to_lance.rs` around lines 173 - 176, The carving configuration for K_CARVING must derive its value range from NODE_ROW_STRIDE instead of hard-coding 512. Update the surrounding SoA carving setup so changes to the imported stride automatically produce the correct persisted carving while preserving the existing key and edge ranges.
99-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
envands3_optionsin the writer and the test. Both files define the same credential-trimming helper and the sameobject_storeoption map, including the load-bearingaws_endpoint←AWS_ENDPOINT_URLmapping. One shared helper removes the drift risk between the write path and the verification path.
crates/lance-graph/examples/soa_to_lance.rs#L99-L120: remove the localenvands3_optionsand call the shared helper.crates/lance-graph/tests/soa_verbatim.rs#L77-L103: remove the localenvands3_optionsand call the same shared helper.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/lance-graph/examples/soa_to_lance.rs` around lines 99 - 120, The writer and verification test duplicate the credential-trimming and S3 option-building helpers, risking configuration drift. In crates/lance-graph/examples/soa_to_lance.rs#L99-L120, remove the local env and s3_options functions and use the shared helper; make the same change in crates/lance-graph/tests/soa_verbatim.rs#L77-L103, preserving the shared AWS_ENDPOINT_URL-to-aws_endpoint mapping and other option behavior.crates/lance-graph/tests/soa_verbatim.rs (3)
224-235: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueComment claims a uniqueness check the code does not perform.
The comment states the slab appears "exactly once".
positionreturns the first match and stops. Nothing rejects a second copy. Assertion (3) bounds the total overhead, which is the actual defence against a duplicate copy. Align the wording with the code.✏️ Proposed wording
- // (1) The slab appears in the file, exactly once, as one unbroken run. + // (1) The slab appears in the file as one unbroken run. Assertion (3) + // bounds the overhead, which rules out a second stored copy.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/lance-graph/tests/soa_verbatim.rs` around lines 224 - 235, Update the assertion (1) comment above the file.windows search to remove the claim that the slab appears exactly once; describe only that the slab appears as one unbroken contiguous run, matching the behavior of position. Leave the uniqueness/duplicate protection to assertion (3).
143-166: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win
schema_forwrites a header stride that can contradict the column.
soa:row_strideis alwaysNODE_ROW_STRIDE, but the field uses thestrideargument. Inthe_narrow_column_falsifierthe header records 512 while the column isFixedSizeBinary(64). No assertion reads that header today, so nothing fails. The helper still produces a self-contradicting contract.♻️ Proposed fix
- table_meta.insert("soa:row_stride".into(), NODE_ROW_STRIDE.to_string()); + table_meta.insert("soa:row_stride".into(), stride.to_string());
the_soa_contract_survives_in_the_table_headerstill passes, becausewrite_slabusesNODE_ROW_STRIDE.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/lance-graph/tests/soa_verbatim.rs` around lines 143 - 166, Update schema_for so the "soa:row_stride" table metadata is derived from the stride argument instead of the fixed NODE_ROW_STRIDE constant, keeping the header consistent with the FixedSizeBinary field while leaving the standard NODE_ROW_STRIDE callers unchanged.
538-559: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winThe header-contract test covers three of the seven persisted keys.
crates/lance-graph/examples/soa_to_lance.rspersistssoa:envelope_layout_version,soa:row_stride,soa:row_carving,soa:endianness,soa:classid,soa:slab_digest, andsoa:source.schema_forin this file writes only the first, the second, and the fourth. A reader that requiressoa:row_carvingtherefore has no regression test. Consider addingsoa:row_carvingtoschema_forand asserting it here, so the two writers agree on the mandatory subset.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/lance-graph/tests/soa_verbatim.rs` around lines 538 - 559, Update schema_for to persist the mandatory soa:row_carving metadata key alongside the existing header keys, using the same value expected by readers. Extend the_soa_contract_survives_in_the_table_header to assert that meta contains the corresponding row-carving value, keeping the writer and header-contract test aligned.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/lance-graph/examples/hydration_probe.rs`:
- Line 263: Update crates/lance-graph/examples/hydration_probe.rs at lines
263-263 to include a per-run nonce in the remote prefix, preventing PID-reuse
collisions. Update lines 370-374 to remove the probe’s remote prefix during
cleanup on both success and failure paths. Update
.claude/plans/idle-flush-dataset-eviction-v1.md at lines 438-439 to state that
remote objects remain until cleanup is implemented.
- Around line 73-82: Add a #[cfg(test)] module alongside the helper
implementations in hydration_probe.rs with focused unit tests covering env’s
quoted-value stripping and empty-value filtering, storage-option mapping,
generated batch shape, and nested dir_stats total calculations. Reuse the
existing helper functions and assert their observable outputs without changing
production behavior.
- Around line 168-187: Update checksum in
crates/lance-graph/examples/hydration_probe.rs:168-187 to produce a stream-safe
digest or full equality check covering values and null state for every schema
field, not only the id column; preserve row-count validation. Update
.claude/plans/idle-flush-dataset-eviction-v1.md:491-493 to keep T10 marked
unresolved until verification covers all columns.
- Around line 146-165: Update dir_stats in
crates/lance-graph/examples/hydration_probe.rs (lines 146-165) to identify its
file count as a local dataset file-count proxy, not a remote request count. In
crates/lance-graph/examples/hydration_probe.rs (line 219), add remote-prefix
listing and request instrumentation before reporting remote counts. In
.claude/plans/idle-flush-dataset-eviction-v1.md (lines 458-462), downgrade the
remote-object/request-count claim until that measurement exists.
- Around line 221-239: Update the hydration probe around the warm Dataset and
latest_version_id timing to use a URI-level version API for a true no-open
measurement, or explicitly label it as already-open Dataset timing. In
.claude/plans/idle-flush-dataset-eviction-v1.md at lines 29, 441-456, and
501-504, remove the “needs no dataset open” claim and limit each gate conclusion
to the lifecycle actually measured.
In `@crates/lance-graph/examples/soa_to_lance.rs`:
- Around line 199-221: Resolve S3 options once before constructing WriteParams
when is_remote is true, and fail immediately with a clear message if
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, or AWS_ENDPOINT_URL is missing. Reuse
the resolved options for both store_params and
DatasetBuilder::with_storage_options, and remove the now-unused store_params
function.
In `@crates/lance-graph/tests/soa_verbatim.rs`:
- Around line 429-455: Restructure the cleanup in the remote-object verification
test so every validation failure still removes root before propagating the
assertion panic. Capture the results of the byte-search, row-offset checks, and
overhead check before calling store.remove_dir_all(root), then panic or assert
with the preserved diagnostics after cleanup; keep the existing validation
behavior and messages.
- Around line 4-8: In a_slab_is_written_verbatim_and_contiguously, add an
assertion immediately after computing the slab offset off that it is aligned to
NODE_ROW_STRIDE (or the NodeRow alignment requirement). Preserve the existing
contiguous-byte verification and keep the documentation claim unchanged.
---
Nitpick comments:
In @.claude/board/EPIPHANIES.md:
- Line 38: Convert the hydration finding beginning with
E-HYDRATION-IS-FIXED-COST-NOT-SIZE-COST-1 into a separate Markdown heading using
the repository’s canonical heading level and marker, matching the format of
nearby epiphany headings such as E-COMPRESSION-META-INERT-AT-512-STRIDE-1.
In `@crates/lance-graph/examples/soa_to_lance.rs`:
- Around line 173-176: The carving configuration for K_CARVING must derive its
value range from NODE_ROW_STRIDE instead of hard-coding 512. Update the
surrounding SoA carving setup so changes to the imported stride automatically
produce the correct persisted carving while preserving the existing key and edge
ranges.
- Around line 99-120: The writer and verification test duplicate the
credential-trimming and S3 option-building helpers, risking configuration drift.
In crates/lance-graph/examples/soa_to_lance.rs#L99-L120, remove the local env
and s3_options functions and use the shared helper; make the same change in
crates/lance-graph/tests/soa_verbatim.rs#L77-L103, preserving the shared
AWS_ENDPOINT_URL-to-aws_endpoint mapping and other option behavior.
In `@crates/lance-graph/tests/soa_verbatim.rs`:
- Around line 224-235: Update the assertion (1) comment above the file.windows
search to remove the claim that the slab appears exactly once; describe only
that the slab appears as one unbroken contiguous run, matching the behavior of
position. Leave the uniqueness/duplicate protection to assertion (3).
- Around line 143-166: Update schema_for so the "soa:row_stride" table metadata
is derived from the stride argument instead of the fixed NODE_ROW_STRIDE
constant, keeping the header consistent with the FixedSizeBinary field while
leaving the standard NODE_ROW_STRIDE callers unchanged.
- Around line 538-559: Update schema_for to persist the mandatory
soa:row_carving metadata key alongside the existing header keys, using the same
value expected by readers. Extend the_soa_contract_survives_in_the_table_header
to assert that meta contains the corresponding row-carving value, keeping the
writer and header-contract test aligned.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6c7f3ddd-10e1-4d4c-b588-fe3a46917233
📒 Files selected for processing (8)
.claude/agents/lance-cache-cartographer.md.claude/agents/lance-cache-contents-auditor.md.claude/board/EPIPHANIES.md.claude/knowledge/lance-cache-surface.md.claude/plans/idle-flush-dataset-eviction-v1.mdcrates/lance-graph/examples/hydration_probe.rscrates/lance-graph/examples/soa_to_lance.rscrates/lance-graph/tests/soa_verbatim.rs
…onest claims
Codex and CodeRabbit findings on this PR's own diff, fixed:
- hydration_probe.rs: hydration was a scan-into-Arrow-then-rewrite through a
second Dataset::write, which (a) silently drops non-row artifacts a real
dataset can carry (versions, deletion vectors, indexes) and (b) measured
remote-decode-plus-local-re-encode time, not a file transfer. Replaced with
a genuine byte copy: list every object under the remote root
(ObjectStore::list, which recurses), read each raw, write it to the same
relative path locally. T10 is now a byte-for-byte comparison of every copied
object, not a partial `id`-column checksum (a corrupted float or a dropped
file previously could not have failed it).
Getting there took two follow-up fixes, both real:
- the "local" and "remote" datasets were two INDEPENDENT `Dataset::write`
calls, which mint different transaction UUIDs — never byte-comparable.
Upload is now a raw byte mirror of the already-written local directory,
not a second write of the same batch.
- `Path::join(&str)` treats its argument as one segment and percent-encodes
embedded `/` (produced literal `_transactions%2F0-<uuid>.txn` objects).
Multi-segment relative paths now go through `Path::from`, which parses
`/` as a separator. `object_store` (already resolved transitively at
0.13.2) is now a direct dev-dependency for this.
- Verified end-to-end against the real endpoint: all three probed sizes
(10k/200k/1M rows) now report EQUAL.
Also: PID-only remote prefix (collides after PID reuse) → PID + per-run
nanosecond stamp; remote objects were never cleaned up → removed
unconditionally, on both success and failure paths; `dir_stats()` was
printed under headings that read as a remote request count when it is a
local pre-upload file count → relabeled, and the plan's §1 "request count"
gap is now explicitly reported as still open rather than closed; the §4
gate's "needs no dataset open" framing overstated what was measured
(`latest_version_id()` ran on an already-open handle) → corrected to
"amortized per-candidate cost" throughout this file, EPIPHANIES.md, and the
plan doc. Added #[cfg(test)] unit tests for the pure helpers.
- New `src/dev_s3_env.rs`: the one shared reading of the S3 environment
(`env`/`s3_options`), replacing three independently-typed copies in
soa_to_lance.rs, hydration_probe.rs, and soa_verbatim.rs — the writer and
its own verification test previously carried separate copies of the
AWS_ENDPOINT_URL mapping, a drift risk on the exact line that determines
which endpoint a write lands on.
- soa_to_lance.rs: resolve S3 options once, fail fast (before writing) if a
remote uri lacks credentials, instead of writing with default credential
discovery and only panicking on re-open; K_CARVING no longer hardcodes 512,
it's formatted from NODE_ROW_STRIDE like the rest of the header.
- soa_verbatim.rs: added an explicit 64-byte alignment assertion on the
discovered slab offset (the mmap-cast premise needs alignment, not just
contiguity); corrected the "(1) exactly once" comment to describe what
`position` actually checks; fixed `schema_for` writing NODE_ROW_STRIDE into
soa:row_stride regardless of the requested column stride (a real
self-contradiction at the narrow-column control shape); added
soa:row_carving to schema_for and to the header-contract test; the S3 test
now cleans up its remote prefix even when an assertion fails
(catch_unwind + resume_unwind), not only on success.
- EPIPHANIES.md + the idle-flush plan: corrected in place rather than
silently rewritten — every claim this PR's own diff got wrong is now
labelled as a correction, not erased.
|
Pushed Real fixes (not just re-wording):
Added Generated by Claude Code |
…evel The same three questions recurred across this arc (64 bit or 64 byte? does align(64) fragment the row? do half-lanes exist?), and three separate AI research dumps each answered them partially wrong in a DIFFERENT way. This primer settles the questions once, names the specific errors so the next person consulting the same tools can grade them, and labels every claim [MEASURED] / [SOURCE file:line] / [ARITHMETIC]. Core content: the three-numbers table (64-bit metadata word vs 64-byte cache line/type alignment vs 512-byte natural row alignment); alignment constrains the START address, never granularity; the straddle arithmetic (a 512-aligned 512-byte row cannot cross a 16 B lane, 64/128 B line, or 4 KiB sector — and alignment of row 0 propagates through the stride to every row forever); why Lance versioning cannot shift data bytes (immutable data files, separate manifest objects — both measured); the per-file-not-per-row cost bound; and the table mapping each property to the red-turning assertion in tests/soa_verbatim.rs that keeps it true. Deliberately shipped THROUGH the PR review loop: if any claim is wrong, CodeRabbit/Codex attack it here — the same loop that produced eight real findings on #907. A claim in a reviewed doc beats a claim in a chat. Cross-linked from docs/SOA_BAKE_DEPLOYMENT.md header. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
Summary
crates/lance-graph/tests/soa_verbatim.rs: physical-layout assertions for the disk-sink-in deployment pattern (a 512-byte SoA slab lands byte-for-byte, contiguously, at a known offset in a Lance data file), an anti-vacuity companion, a deliberately weaker round-trip check, and a header-contract check.the_narrow_column_falsifier— proves the byte search can actually detect compression (drops below Lance's mini-block cutoff, wherelance-encoding:compressionmetadata is honoured;"none"keeps rows verbatim,"zstd"makes them vanish).a_slab_is_written_verbatim_to_s3_too— the same physical-layout assertion run against the real S3-compatible object store, using the sameAWS_*variable names (AWS_S3_BUCKET_NAMEincluded) a Railway deployment already sets. No new environment key invented.soa_to_lance.rsand the new test's doc comments:lance-encoding:compression = "none"field metadata is not what keeps the 512-byte row column verbatim. Measured: removing the key, or setting it to"zstd", leaves the written file byte-identical. Root cause, read fromlance-encoding9.0.0 source: a 512-byte value clears the mini-block "narrow" cutoff (256 bytes) and takes the full-zip path, whoseFixedWidthper-value compressor ignores field metadata unconditionally. The canonical row stride is what buys the verbatim write; the metadata line is kept as a documented backstop for if that cutoff ever moves, not credited as the cause.EPIPHANIES.md: records the correction and the S3 proof.Test plan
cargo test -p lance-graph --test soa_verbatim— 6/6 passing (includes the live S3 arm, not skipped)cargo clippy -p lance-graph --tests --examples -- -D warnings— cleancargo fmt -p lance-graphGenerated by Claude Code
Summary by CodeRabbit