Repository navigation
perf(load): stream a csv overwrite's source by partition and check uniqueness as the rows drain - #1158
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
…iqueness as the rows drain
95532fe to
7998999
Compare
|
Request changes. WhyA WhatThe overwrite plans from the metadata probe. Each parse partition reads its own byte range in 8 MiB pieces. Each read is a conditional GET with The two decisionsSingle-partition fallback. A partitioned refusal sets Boundary alignment. Partition 0 starts at the records. A later partition starts after the first raw Blocking
Optional
TradeoffThis removes the whole-object buffer, the serial cut, and the post-drain merge of per-partition uniqueness maps. It adds range reads, a boundary guard, a one-shot fallback, and one shared sharded map sized from the first slice. The fallback keeps one numbering owner. That is the right trade on a path that runs only when the load fails.
Open pull requests: #1150 and #1130 both edit ValidationReviewed head Local, isolated checkout of that head:
CI on this commit: The three GQT files do not execute on I did not reproduce the SF30 timings. Inline comments
The DST golden does not match this commit.
This sentence is right for the default slice. The Limits section below still says the loader reads the object once per statement and reuses that capture on every reprepare, and that a stream claims the next 8 MiB slice. Overwrite probes once, then each partition reads its range. A refusal reads the ranges again. Append and merge still capture once.
A partition no longer claims the next slice. Each partition owns a byte range and reads that range in pieces.
Empty lines disagree too. |
|
Request changes. DeltaHead Blocking
Optional
Other checks on this head
This commit does not touch files the other open pull requests edit. TradeoffThe golden update records the extra GET and removes the false red on the cost contract. It adds no mechanism. The published operator text still states the old read, so the guides and the code disagree. That is the remaining liability. ValidationReviewed head Local, isolated checkout of that head, from
Result: I did not reproduce the SF30 timings. I did not rerun the two |
A
load csvoverwrite captures its whole source into one buffer before the plan exists and cuts the slices on one thread, although the planner needs only the object's size and a partition only its own bytes: 34 s of capture and 15 s of serial cutting in SF30's 155 s load after #1147. The uniqueness check then merges eight per-partition hash tables on one thread after the last batch, 11 s on the Comment file.The loader now plans from the size probe, and each parse partition of an overwrite range-scans its own byte range in 8 MiB pieces through conditional GETs with an
If-Matchprecondition on the probe's ETag. Partitions align on record boundaries by bytes, each starting at the first line end past its range start and the one before it stopping at the first record end its parse meets past that byte; the guard is that the two offsets agree, and a mismatch, like any refusal of the partitioned run, falls back to the same plan as a single partition. The uniqueness check's first holders become one sharded hash table shared by every partition, pre-sized from a cardinality estimate off the first slice, so a repeat is judged at insert and nothing merges after the drain; append and merge keep the whole-object capture.SF30, 31 overwrite statements on a fresh graph, release builds of
7711e4a3and this branch, two interleaved rounds, medians: load 154.9 → 110.5 s, Comment 46.4 → 18.6 s, write 45.7 → 47.3 s.For review: the decision to challenge is the single-partition fallback, which re-reads a refused source once more rather than numbering records across partitions; a refusal the fallback does not reproduce would make it wrong, and
load_csv_partitioned_refusal_names_the_record.gqtandload_csv_partitioned_quoted_line_break_at_the_boundary.gqtpin the two shapes. Least sure: the alignment reads line ends without regard to quoting, so an empty line at a boundary costs a fallback, never wrong rows; no case pins it. The boundary cases cut partitions at 4 KiB through a test-only# csv_slice_bytesheader, refused on a server target. An overwrite now reads its first record from the source's first 8 MiB and refuses a longer one (docs, Rust test); a memory refusal'sactualis the first piece over the limit, not the whole source. The receipt'sslicesgainread_ns, additive,openapi.jsonregenerated; the DST cost golden'sload csvscenarios move from one whole-object GET to the head-range GET plus one GET per piece; release notechangelog.d/load-csv-partitioned-read.performance.md.