Skip to content

fix: r_cte wrong/flaky results under concurrency#19439

Open
KKould wants to merge 7 commits intodatabendlabs:mainfrom
KKould:fix/r_cte_random_result
Open

fix: r_cte wrong/flaky results under concurrency#19439
KKould wants to merge 7 commits intodatabendlabs:mainfrom
KKould:fix/r_cte_random_result

Conversation

@KKould
Copy link
Member

@KKould KKould commented Feb 10, 2026

I hereby agree to the terms of the CLA available at: https://docs.databend.com/dev/policies/cla/

Summary

  • fixes: Tracking: Recursive CTE result may be random #19398

  • Fix flaky / non-deterministic results in WITH RECURSIVE ... UNION ALL ... caused by cross-query interference on recursive CTE internal MEMORY tables.

  • Make the recursive CTE internal table names query-unique (prefix with query id) so concurrent queries (sqllogictest parallelism in CI) can no longer create/drop/recreate the same internal table name and corrupt each other’s recursion state.

  • Add a deterministic regression test hook and a stable repro test that simulates the interference between step=0 and step=1.

Root Cause

Recursive CTE is executed in steps. In step=0, Databend creates internal Engine=Memory tables (one per RecursiveCteScan) and writes prepared blocks keyed by exec_id. In step>=1 it reads from the same internal MEMORY table to continue recursion.

Previously, those internal MEMORY tables were created in the current database using stable names taken directly from the recursive scan name / CTE alias (e.g. lines, paths). This makes the internal tables globally visible by (tenant, catalog, database, table_name) and not query-private.

In CI, sqllogictests frequently runs with --parallel > 1 against a single databend-query instance. Multiple concurrent sqllogictest queries can therefore execute recursive CTEs that share common aliases like lines. Because each recursive CTE run also drops its internal tables at the end, one
query can drop/recreate the same internal table name while another query is between step=0 and step=1, causing step=1 to see an empty/replaced table and terminate early (e.g. returning seed-only results such as 1 instead of the correct 1000). This appears as a rare, timing-dependent “random
result” in CI.

CI Execution Chain

  • scripts/ci/ci-run-sqllogic-tests.sh
    • TEST_PARALLEL=${TEST_PARALLEL:-8} (default parallelism)
    • runs databend-sqllogictests ... --enable_sandbox --parallel ${TEST_PARALLEL}
  • .github/actions/test_sqllogic_standalone_linux/action.yml
    • env: TEST_PARALLEL: ${{ inputs.parallel }} (workflow overrides the script default)
    • run: bash ./scripts/ci/ci-run-sqllogic-tests.sh ${{ inputs.dirs }}
  • .github/workflows/reuse.sqllogic.yml
    • standalone job passes parallel: ${{ matrix.tests.parallel }} (empty => falls back to script default)
    • cluster job has matrix entries that explicitly set parallel: "2" for some suites
  • scripts/ci/ci-run-sqllogic-tests-without-sandbox.sh
    • runs databend-sqllogictests ... --parallel 1 (no --enable_sandbox, parallel fixed at 1)
  • .github/actions/test_sqllogic_iceberg_tpch/action.yml
    • uses bash ./scripts/ci/ci-run-sqllogic-tests-without-sandbox.sh ... (parallel=1, no sandbox)
  • .github/actions/test_sqllogic_stage/action.yml
    • uses bash ./scripts/ci/ci-run-sqllogic-tests-without-sandbox.sh ... (parallel=1, no sandbox)
  • scripts/ci/deploy/databend-query-standalone.sh
    • starts databend-query ... --internal-enable-sandbox-tenant (enables SET sandbox_tenant=... used by sqllogictests)

Tests

  • Unit Test
  • Logic Test
  • Benchmark Test
  • No Test - Explain why

Type of change

  • Bug Fix (non-breaking change which fixes an issue)
  • New Feature (non-breaking change which adds functionality)
  • Breaking Change (fix or feature that could cause existing functionality not to work as expected)
  • Documentation Update
  • Refactoring
  • Performance Improvement
  • Other (please describe):

This change is Reviewable

@KKould KKould requested a review from SkyFan2002 February 10, 2026 11:59
@KKould KKould self-assigned this Feb 10, 2026
@github-actions github-actions bot added the pr-bugfix this PR patches a bug in codebase label Feb 10, 2026
@KKould KKould marked this pull request as ready for review February 11, 2026 08:47
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f6e0c6ce82

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@KKould KKould marked this pull request as draft February 11, 2026 12:18
@KKould KKould marked this pull request as ready for review February 13, 2026 17:33
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 24cf459611

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +224 to +225
if short.len() >= 32 {
break;

Choose a reason for hiding this comment

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

P1 Badge Preserve full query-id entropy in RCTE table prefix

make_rcte_prefix stops after collecting 32 alphanumeric characters, so two concurrent queries whose IDs share the same first 32 alnum chars will still map to the same internal __rcte_* table names. That reintroduces the same cross-query interference this patch is trying to eliminate (wrong/flaky recursive CTE results) for clients that provide custom/long query IDs. Generate the prefix from the full query ID (e.g., full sanitized ID or a hash of it) instead of truncating here.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-bugfix this PR patches a bug in codebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tracking: Recursive CTE result may be random

2 participants