parallel_export_parquet landed via #329/#331. The blocker found in review (a
cross-backend snapshot handoff that duplicated the table and dropped the caller's
uncommitted rows) was fixed and I verified it against the original reproduction.
These six were raised in that review, judged non-blocking, and are now on main.
Filing so they are not lost with the closed PR. Roughly in order of how much I
would care.
1. Partitioned export re-derives the leaf list from the live catalog. Each
worker enumerates leaves itself rather than receiving the set the dispatcher
computed. A concurrent ATTACH or DETACH CONCURRENTLY is not blocked by the
dispatcher's AccessShareLock, so workers can disagree about the relation set.
Note the snapshot fix does not cover this: an MVCC snapshot governs row
visibility, not which relations are enumerated. The fix is to ship the actual leaf
OIDs in the DSM rather than a recipe for recomputing them, which also makes the
split self-describing instead of positional.
2. No cleanup of partial output on failure. The require-empty-directory guard
protects entry but not exit. A worker failing mid-export leaves part files that
pgcolumnar.read_parquet will union as if the export were complete, and the
directory then fails the empty check on retry. Silent wrong answers on read-back
is the bad shape here.
3. ColumnarParquetCheckExportable validates the parent's tupdesc, the writer
runs per leaf. A leaf whose column shape differs from the parent passes the
fail-fast pre-check and then fails or misbehaves inside a worker, which is the
worst place to discover it.
4. Workers register as BgWorkerStart_RecoveryFinished. A read-only export
cannot run on a hot standby, which is exactly where you would want to run one.
BgWorkerStart_ConsistentState is the plausible value.
5. No dsm-detach callback. A FATAL in the dispatcher orphans the workers and
drops the snapshot's horizon protection while they are still reading.
6. ColumnarWriteParquetFile frees none of its per-call buffers, and the
partitioned worker calls it in a loop, so memory grows with the partition count
rather than staying flat.
Testing note, since it is the reason the original blocker survived to review: every
fixture in test/parallel_export_parquet.sh ran the export as a standalone
autocommit statement against a quiet table, which is the one configuration where
the snapshot handoff is a no-op. The in-transaction case is covered now. Items 1
and 2 above have the same property: neither is reachable by a fixture that does not
have something else happening at the same time, or a failure injected mid-run.
parallel_export_parquetlanded via #329/#331. The blocker found in review (across-backend snapshot handoff that duplicated the table and dropped the caller's
uncommitted rows) was fixed and I verified it against the original reproduction.
These six were raised in that review, judged non-blocking, and are now on main.
Filing so they are not lost with the closed PR. Roughly in order of how much I
would care.
1. Partitioned export re-derives the leaf list from the live catalog. Each
worker enumerates leaves itself rather than receiving the set the dispatcher
computed. A concurrent
ATTACHorDETACH CONCURRENTLYis not blocked by thedispatcher's
AccessShareLock, so workers can disagree about the relation set.Note the snapshot fix does not cover this: an MVCC snapshot governs row
visibility, not which relations are enumerated. The fix is to ship the actual leaf
OIDs in the DSM rather than a recipe for recomputing them, which also makes the
split self-describing instead of positional.
2. No cleanup of partial output on failure. The require-empty-directory guard
protects entry but not exit. A worker failing mid-export leaves part files that
pgcolumnar.read_parquetwill union as if the export were complete, and thedirectory then fails the empty check on retry. Silent wrong answers on read-back
is the bad shape here.
3.
ColumnarParquetCheckExportablevalidates the parent's tupdesc, the writerruns per leaf. A leaf whose column shape differs from the parent passes the
fail-fast pre-check and then fails or misbehaves inside a worker, which is the
worst place to discover it.
4. Workers register as
BgWorkerStart_RecoveryFinished. A read-only exportcannot run on a hot standby, which is exactly where you would want to run one.
BgWorkerStart_ConsistentStateis the plausible value.5. No dsm-detach callback. A FATAL in the dispatcher orphans the workers and
drops the snapshot's horizon protection while they are still reading.
6.
ColumnarWriteParquetFilefrees none of its per-call buffers, and thepartitioned worker calls it in a loop, so memory grows with the partition count
rather than staying flat.
Testing note, since it is the reason the original blocker survived to review: every
fixture in
test/parallel_export_parquet.shran the export as a standaloneautocommit statement against a quiet table, which is the one configuration where
the snapshot handoff is a no-op. The in-transaction case is covered now. Items 1
and 2 above have the same property: neither is reachable by a fixture that does not
have something else happening at the same time, or a failure injected mid-run.