Repository navigation
fix(compaction): materialize dedup normalization in temp table to stop bind wedge - #515
Conversation
The compaction manager re-emits each subprocess's stderr into the main log for visibility, but forwarded every line at INFO. A subprocess error (e.g. a DuckDB bind failure) therefore appeared at INF while its embedded JSON read "level":"error". forwardSubprocessLine now parses the structured line's level and re-emits at the matching level; non-JSON lines fall back to INFO and the original line is preserved verbatim. Cosmetic only.
…tions The existing DuckDB-backed regression test only exercised the dedup branch of buildCompactionQuery. The standard branch (nil tagColumns — measurements whose files carry no arc:tags metadata, e.g. pre-dedup or msgpack-columnar) was untested against real DuckDB, despite using a flat SELECT * REPLACE form the dedup-branch comment warns about. This adds a counterpart test that compacts a mixed TIMESTAMPTZ/VARCHAR-time tagless partition and asserts the output binds and normalizes to TIMESTAMP WITH TIME ZONE. Confirms the tagless path is not subject to the time-bind wedge on the shipped DuckDB version.
…p bind wedge The dedup compaction path (measurements with tag metadata) wrapped the #493 time normalization in a CTE that the dedup window read from. On real partitions — many files at the full column width of a typical measurement — DuckDB does not treat that CTE as a materialization barrier: the window's "time" reference binds against the raw read_parquet(union_by_name=true) scan and fails with `Failed to bind column reference "time": TIMESTAMP WITH TIME ZONE != VARCHAR`, EVEN WHEN every file's time is already TIMESTAMPTZ and no VARCHAR file exists (the VARCHAR is a binder artifact). #493's test used a 3-column fixture below the binder's column-count threshold, so this surfaced only on production-shape partitions, which then failed to compact on every cycle and accumulated files. Verified empirically against Arc's linked DuckDB on real files: OFFSET 0 and plain-CTE forms fail; the flat no-CTE form binds but silently under-dedups (QUALIFY runs over raw time). The only form that both binds and dedups correctly is CREATE OR REPLACE TEMP TABLE AS <normalized read> followed by a COPY that runs the dedup window over the table. buildCompactionQuery now returns []string: 1 statement (standard, unchanged) or 2 (dedup: temp table + COPY). The caller pins both to one connection via db.Conn (a DuckDB temp table is connection-local; database/sql does not guarantee two ExecContext calls share a pooled connection) and DROPs the temp table before releasing the connection (the driver does not reset sessions, so a leftover would retain a partition's rows on a pooled connection). Tests: TestDedupCompaction_RealPartition (opt-in, ARC_REPRO_DIR) is the faithful regression — synthetic fixtures do not reproduce the all-TIMESTAMPTZ phantom-VARCHAR bind, documented on the synthetic scale test. Live: a manual compaction cycle that previously wedged now reports 269/269 succeeded.
|
@gemini-code-assist please review Focus areas for this fix:
|
There was a problem hiding this comment.
Code Review
This pull request resolves a critical issue where deduplication compaction would wedge on wide, many-file partitions in DuckDB. The fix replaces the previous CTE-based materialization with a temporary table materialization pinned to a single connection, ensuring proper column binding and preventing silent under-deduplication. Additionally, subprocess log lines are now forwarded at their original severity. The reviewer's feedback correctly identifies a potential resource leak where the temporary table might not be dropped if the job's context is cancelled, and suggests using a detached background context for the cleanup query.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Code Review
This pull request refactors the dedup compaction path to use a connection-local DuckDB temporary table instead of a CTE, resolving a plan-binding error on wide, many-file partitions. It also updates subprocess log forwarding to preserve the original log severity. The review feedback correctly identifies a risk where the temporary table cleanup in the defer block could be skipped if the context is cancelled, and suggests using a detached context with a timeout to ensure reliable execution.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
The deferred DROP TABLE ran on the job's ctx, so a cancelled or timed-out compaction would skip cleanup and leave the temp table on the pooled connection (the DuckDB driver does no session reset → retained memory on any reused *sql.DB). Run the DROP on an independent context.Background() with a short timeout so cleanup always executes regardless of the job ctx state. Mirrored in the execCompaction test helper. Addresses Gemini review on #515.
|
Addressed the review finding (commit 8ba5258). Temp-table cleanup now runs on a detached context. The deferred @gemini-code-assist please review |
There was a problem hiding this comment.
Code Review
This pull request addresses a DuckDB plan-binding error during dedup compaction on wide, many-file partitions by replacing the CTE-based materialization of the normalized time column with a temporary table materialization. The statements are pinned to a single connection to ensure visibility of the connection-local temp table, which is then explicitly dropped to prevent memory leaks. Additionally, subprocess log forwarding has been improved to preserve and re-emit logs at their original severity level instead of flattening them to INFO. I have no feedback to provide as there are no review comments to assess.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Summary
INTERNAL Error: Failed to bind column reference "time": TIMESTAMP WITH TIME ZONE != VARCHAR.timenormalization in a CTE the dedup window reads from. On real partitions (many files at the full column width of a typical measurement, e.g.host, time, value, cpu_idle, cpu_user), DuckDB does not treat the CTE as a materialization barrier — the window'stimereference binds against the rawread_parquet(union_by_name=true)scan and fails, even when every file'stimeis alreadyTIMESTAMP WITH TIME ZONEand noVARCHARfile exists anywhere (theVARCHARin the error is a binder artifact, not a real column type).Root cause (verified empirically against Arc's linked DuckDB, v1.5.01, on the real wedged files)
REPLACE(what #493 shipped)OFFSET 0* REPLACE … QUALIFYCREATE OR REPLACE TEMP TABLE AS+ COPYThe fix
buildCompactionQuerynow returns[]string: 1 statement (standard path — query text unchanged) or 2 (dedup: temp-table materialization + COPY running the dedup window over the table).db.Conn(a DuckDB TEMP table is connection-local;database/sqldoes not guarantee twoExecContextcalls share a pooled connection) and DROPs the temp table before releasing the connection (the DuckDB driver implements no session reset, so a leftover temp table would retain a partition's rows on a pooled connection).(tags, time)) are unchanged.Test plan
go build ./cmd/... ./internal/...,go vet -tags duckdb_arrow ./internal/compaction/,gofmt -l— all cleango test ./internal/compaction/(default tag) — passgo test -tags duckdb_arrow ./internal/compaction/— passTestDedupCompaction_RealPartition(opt-in viaARC_REPRO_DIR) — the faithful regression against real Arc-written files; verified the old query fails on these exact files and the new one binds + dedups (6000 rows, injected VARCHAR duplicate collapses to 1)TestBuildCompactionQuery_DedupMixedTimeAtScalekept but honestly documented as a weaker guard (synthetic files don't reproduce the all-TZ binder quirk)Review notes
Internal review found and this PR addresses: (1) the temp-table connection-locality hazard (pinned connection), (2) a retained-memory leak from the temp table persisting on a pooled connection after
Close()(explicitDROP), (3) staleOFFSET 0comments corrected, (4) release-notestime-type entry corrected (the CTE materialization claim did not hold at scale).🤖 Generated with Claude Code