refactor: extract pure flush_one_column() from the flush loop (#445 slice 1) - #589
Conversation
…dprompt#445 slice 1) Slice 1 of the commandprompt#445 in-COPY parallelism design (commandprompt#588): move the per-column flush body of pgcolumnar_flush_row_group into a standalone flush_one_column(inputs) -> {chunk, descriptor, codec, zonemap, bloom} that reads only its arguments, no writeState reach-through. The backend assembles the column chunks into the stripe in column order and keeps all I/O and catalog writes. No workers yet; output is byte-identical to the serial path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017N82wDmsawqSWoWkmxtHmW
jdatcmd
left a comment
There was a problem hiding this comment.
Approve — byte-identical verified (PG18)
Reviewed as the author of the #588 design this implements, and verified rather than trusted the "byte-identical" claim.
Storage is byte-identical to main, not just read-equivalent. Same 400k-row text-heavy load (FSST-eligible URL + query columns) on this branch and on clean f20c811:
| #589 | main | |
|---|---|---|
| pg_total_relation_size | 3915776 | 3915776 |
| content md5 | 84c76b03… | 84c76b03… |
| encoding descriptors md5 | 64e2fe13… | 64e2fe13… |
| chunk bytes | 3874704 | 3874704 |
The descriptor hash matching is the one that matters — it proves the per-column codec/encoder decisions are identical, so the FSST verdict, block codec, and D4 selector all came out the same after the extraction.
Correctness: differential, native_writer, native_zonemap, native_bloom, native_dml, write_fsst_compressed, encode_effort, native_encoding all pass. Warning-clean build.
The extraction is clean: flush_one_column reads only its arguments — no writeState reach-through — and the backend loop keeps all I/O and catalog writes. The #472 verdict cache is threaded through def exactly as the design's slice-1 note said it had to be (it is the one non-pure piece, handled correctly). Memory looks right: validity/fsstTable/corpus freed, returned buffers consumed by the caller.
Good foundation for slices 2–4. Approving; merge is jd's per the no-self-merge rule.
#445 slice 1: extract a pure
flush_one_column()— no behavior changeFirst slice of the in-COPY parallelism design (#588), per the owner go-ahead on that thread. Pure mechanical refactor; output is byte-identical to the serial path. No workers, no dsm, no GUC yet — this is the foundation the later slices build on, and it settles the whole codec/encoder correctness surface serially.
What changed
pgcolumnar_flush_row_group's per-column loop body is extracted into a standalonethat reads only its arguments — no
writeStatereach-through — and returns{chunk bytes, descriptor, block codec, zone-map rows, bloom row}. The function produces its column-chunk bytes ([validity][encoded/compressed]) into its own buffer instead of appending to the shared stripedata; the backend loop assembles the chunks in column order and keeps all I/O and catalog writes exactly where they were. The FSST verdict cache is carried through thedefpointer argument.This is the unit of parallelism the design identifies (a column chunk at flush time, each independent), isolated behind a clean boundary while still running serially in the backend.
Why it's byte-identical
The extraction is line-for-line: the FSST decision, per-vector encode, zone-map/bloom construction, and block codec are unchanged; only
writeState->X→ parameter and the twoappendBinaryStringInfo(data, …)→ the localchunkbuffer, with the backend appending that chunk todataat the same point.chunkOffset/chunkLengthbookkeeping moved to the caller and computes the same values.Verification (prove-not-trust)
differential.shPASSES.native_writer,native_dml,write_fsst_compressed,write_minmax_fastpath,encode_invariants,encode_effort,corruption,native_reclaim,native_roundtripall PASS.--enable-cassert) — no TRAP/Assert/crash.-Wshadow=compatible-localclean.Not in this slice
dsm plumbing (slice 2), the worker pool with serial degradation on slot starvation (slice 3), and the GUC + measurement vs the ~17% ceiling (slice 4). The default flip to parallel-by-default is a later change gated on proving slot-safe degradation, per the plan on #588.
🤖 Generated with Claude Code
https://claude.ai/code/session_017N82wDmsawqSWoWkmxtHmW