Skip to content

fix(sql): inefficient commit batching during parquet exports#6596

Merged
bluestreak01 merged 3 commits intomasterfrom
nw_copy_perf
Jan 5, 2026
Merged

fix(sql): inefficient commit batching during parquet exports#6596
bluestreak01 merged 3 commits intomasterfrom
nw_copy_perf

Conversation

@nwoolmer
Copy link
Copy Markdown
Contributor

@nwoolmer nwoolmer commented Jan 4, 2026

When copying data to tables during the parquet export process, each inserted row was a single-row commit, due to a logic bug.

@nwoolmer nwoolmer added Bug Incorrect or unexpected behavior SQL Issues or changes relating to SQL execution labels Jan 4, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 4, 2026

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The PR modifies batch size condition checking in copy operations from batchSize != -1 to batchSize > 0, ensuring only strictly positive batch sizes trigger ordered batching. It adds handling for non-batched paths and includes a regression test validating parquet export commit behavior.

Changes

Cohort / File(s) Summary
Batch size logic refactoring
core/src/main/java/io/questdb/griffin/SqlCompilerImpl.java, core/src/main/java/io/questdb/griffin/engine/ops/InsertAsSelectOperationImpl.java
Changed batchSize condition from batchSize != -1 to batchSize > 0 for ordered-batched copy path selection. Added assertion enforcing batchSize > 0 at call sites. Introduced alternate path for batchSize <= 0 using Long.MAX_VALUE with zero batch count for non-batched behavior. Removed unused SqlExecutionCircuitBreaker import.
Regression test addition
core/src/test/java/io/questdb/test/griffin/CopyExportTest.java
Added new test method testParquetExportDoesNotCommitPerRow that validates parquet export does not perform per-row commits using TableWriterMetrics to verify commit count remains below threshold.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • bluestreak01

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: addressing inefficient commit batching during parquet exports, which aligns with the core changes across all modified files.
Description check ✅ Passed The description is directly related to the changeset, explaining the logic bug that caused per-row commits during parquet exports, which matches the changes made in the PR.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nwoolmer
Copy link
Copy Markdown
Contributor Author

nwoolmer commented Jan 5, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
core/src/main/java/io/questdb/griffin/engine/ops/InsertAsSelectOperationImpl.java (1)

144-165: Insert‑as‑select batching fix looks good; consider aligning o3MaxLag usage

The new logic:

  • Keeps unordered copy for non‑timestamp inserts.
  • For timestamped inserts, uses ordered batched copy only when batchSize > 0, respecting the new precondition in SqlCompilerImpl.copyOrderedBatched.
  • For batchSize <= 0, calls copyOrderedBatched(..., Long.MAX_VALUE, 0), which in practice disables intermediate ic() calls and prevents per‑row commits.

Functionally this achieves the desired fix. One small nit: in SqlCompilerImpl.copyTableData the non‑batched path uses Long.MAX_VALUE but still passes through the configured o3MaxLag, whereas here you hard‑code 0. With current copyOrderedBatched semantics that makes no difference (no ic() calls are triggered), but for consistency and future‑proofing you may want to pass o3MaxLag here as well.

core/src/test/java/io/questdb/test/griffin/CopyExportTest.java (1)

1876-1920: Well-structured regression test with minor documentation improvements suggested.

The test effectively validates that parquet exports use batched commits rather than per-row commits. The approach of comparing commit counts before/after is sound, and the threshold of 100 commits provides adequate margin to detect the regression while allowing for legitimate commit operations (temp table creation, status log updates, etc.).

A few optional improvements:

  1. Clarify the commit threshold rationale (line 1912-1913): Consider adding a brief comment explaining that the threshold accounts for temp table operations and status updates, making it clearer why 100 is appropriate.

  2. Metric isolation consideration: The TableWriterMetrics.getCommitCount() returns global commit counts across all tables. While assertMemoryLeak() should provide isolation, the test assumes no other concurrent operations are contributing commits during the export. This is likely fine in the test environment but worth noting if debugging future flakiness.

  3. Row count verification: The test correctly generates 10,000 rows (time span of 9,999 seconds inclusive of start/end), matching the rowCount variable. Nice attention to detail.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 729bf3b and 9af0e03.

📒 Files selected for processing (3)
  • core/src/main/java/io/questdb/griffin/SqlCompilerImpl.java
  • core/src/main/java/io/questdb/griffin/engine/ops/InsertAsSelectOperationImpl.java
  • core/src/test/java/io/questdb/test/griffin/CopyExportTest.java
🧰 Additional context used
🧬 Code graph analysis (1)
core/src/test/java/io/questdb/test/griffin/CopyExportTest.java (1)
core/src/main/java/io/questdb/cairo/TableWriterMetrics.java (1)
  • TableWriterMetrics (32-109)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (34)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-other)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-pgwire)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-cairo-sub)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-cairo-root)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-fuzz2)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-fuzz1)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-griffin-sub)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-griffin-root)
  • GitHub Check: New pull request (Rust Test and Lint on linux-jdk17)
  • GitHub Check: New pull request (Hosted Running tests on windows-other-2)
  • GitHub Check: New pull request (Hosted Running tests on windows-other-1)
  • GitHub Check: New pull request (Hosted Running tests on windows-pgwire)
  • GitHub Check: New pull request (Hosted Running tests on windows-cairo-2)
  • GitHub Check: New pull request (Hosted Running tests on windows-cairo-1)
  • GitHub Check: New pull request (Hosted Running tests on windows-fuzz2)
  • GitHub Check: New pull request (Hosted Running tests on windows-fuzz1)
  • GitHub Check: New pull request (Hosted Running tests on windows-griffin-sub)
  • GitHub Check: New pull request (Hosted Running tests on windows-griffin-base)
  • GitHub Check: New pull request (Hosted Running tests on mac-other)
  • GitHub Check: New pull request (Hosted Running tests on mac-pgwire)
  • GitHub Check: New pull request (Hosted Running tests on mac-cairo-fuzz)
  • GitHub Check: New pull request (Hosted Running tests on mac-cairo)
  • GitHub Check: New pull request (SelfHosted Other tests on linux-x64-zfs)
  • GitHub Check: New pull request (SelfHosted Other tests on linux-x86-graal)
  • GitHub Check: New pull request (SelfHosted Other tests on linux-arm64)
  • GitHub Check: New pull request (Hosted Running tests on mac-griffin)
  • GitHub Check: New pull request (SelfHosted Griffin tests on linux-x64-zfs)
  • GitHub Check: New pull request (SelfHosted Griffin tests on linux-arm64)
  • GitHub Check: New pull request (SelfHosted Griffin tests on linux-x86-graal)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-x86-graal)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-x64-zfs)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-arm64)
  • GitHub Check: New pull request (Trigger Enterprise CI Trigger Enterprise Pipeline)
  • GitHub Check: New pull request (Check Changes Check changes)
🔇 Additional comments (3)
core/src/main/java/io/questdb/griffin/SqlCompilerImpl.java (2)

294-317: Assert on batchSize > 0 is well-protected by call-site guards

All direct callers of copyOrderedBatched(...) already enforce the batchSize > 0 precondition before invoking the method:

  • InsertAsSelectOperationImpl guards with if (batchSize > 0) or passes Long.MAX_VALUE
  • SqlCompilerImpl guards with else if (batchSize > 0) or passes Long.MAX_VALUE

The assert provides valuable defensive documentation and will catch any future violations introduced by new call sites, though the current code respects the precondition throughout.


3473-3495: Logic correctly handles non-positive batchSize by disabling intermediate commits

The updated logic:

  • Uses unordered copying when there is no designated timestamp (timestampIndex == -1), ignoring batchSize.
  • For timestamped tables with batchSize > 0, calls copyOrderedBatched with the actual batch size as the commit interval.
  • For batchSize <= 0, calls copyOrderedBatched with Long.MAX_VALUE, which ensures the condition rowCount >= commitTarget never triggers during data ingestion, effectively disabling intermediate ic() calls so that only the outer writer.commit() runs.

This correctly avoids single-row commits for non-positive batch sizes during data copy operations while preserving ordered ingestion semantics.

core/src/test/java/io/questdb/test/griffin/CopyExportTest.java (1)

30-30: LGTM: Import added for commit metrics validation.

The TableWriterMetrics import is necessary for the regression test and correctly placed.

@glasstiger
Copy link
Copy Markdown
Contributor

[PR Coverage check]

😍 pass : 3 / 3 (100.00%)

file detail

path covered line new line coverage
🔵 io/questdb/griffin/SqlCompilerImpl.java 2 2 100.00%
🔵 io/questdb/griffin/engine/ops/InsertAsSelectOperationImpl.java 1 1 100.00%

@bluestreak01 bluestreak01 merged commit cf87730 into master Jan 5, 2026
43 checks passed
@bluestreak01 bluestreak01 deleted the nw_copy_perf branch January 5, 2026 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Incorrect or unexpected behavior SQL Issues or changes relating to SQL execution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants