Skip to content

[master][DB-2195] Reduce temp space needed for secondary index migration (#5692) - #5732

Open
timothycoleman wants to merge 3 commits into
masterfrom
timothycoleman/reduce-duck-migration-temp-space
Open

timothycoleman wants to merge 3 commits into
masterfrom
timothycoleman/reduce-duck-migration-temp-space

Conversation

@timothycoleman

Copy link
Copy Markdown
Contributor

Cherry picked from eb42bd7

  • Reduce temp space needed for secondary index migration

The V1 migration added idx_all.record_id with DEFAULT ''::BLOB. The cast makes
the default a non-constant expression, which sends DuckDB down its workaround
path and rewrites the statement into ADD COLUMN plus an UPDATE of every row
(see transform_alter_table.cpp). That UPDATE needs ~20 bytes of transaction
local memory per row rather than ~2, so on a large index it can exhaust
max_temp_directory_size.

Dropping the cast keeps the same value and the same backfill while taking the
cheap metadata path.

  • V1 is frozen as V1A: internal, unregistered, and kept only so tests can
    reproduce the database state it left behind. It must not be edited.
  • V1B replaces it at version 1, identical except for the bare DEFAULT ''.

Also identified other small descrepancies:

  • V2 gives record_id the shape a fresh install creates, not null with no
    default, on idx_all and on every user index table. DuckDB cannot add a
    column and a constraint in one statement, so a migrated column is
    necessarily nullable with a default until this runs. Both statements are
    catalog only and leave the database file unchanged.

MigrationTests.SchemaParity asserts that a migrated schema matches what
1_Schema.sql and CreateUserIndex produce, via V1A and V1B paths,
comparing every table including user index tables.

Another test also checks that the migrations don't do something surprisingly expensive

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

timothycoleman and others added 2 commits September 17, 2026 08:29
Cherry picked from eb42bd7

* Reduce temp space needed for secondary index migration

The V1 migration added idx_all.record_id with DEFAULT ''::BLOB. The cast makes
the default a non-constant expression, which sends DuckDB down its workaround
path and rewrites the statement into ADD COLUMN plus an UPDATE of every row
(see transform_alter_table.cpp). That UPDATE needs ~20 bytes of transaction
local memory per row rather than ~2, so on a large index it can exhaust
max_temp_directory_size.

Dropping the cast keeps the same value and the same backfill while taking the
cheap metadata path.

- V1 is frozen as V1A: internal, unregistered, and kept only so tests can
  reproduce the database state it left behind. It must not be edited.
- V1B replaces it at version 1, identical except for the bare DEFAULT ''.

Also identified other small descrepancies:

- V2 gives record_id the shape a fresh install creates, not null with no
  default, on idx_all and on every user index table. DuckDB cannot add a
  column and a constraint in one statement, so a migrated column is
  necessarily nullable with a default until this runs. Both statements are
  catalog only and leave the database file unchanged.

MigrationTests.SchemaParity asserts that a migrated schema matches what
1_Schema.sql and CreateUserIndex produce, via V1A and V1B paths,
comparing every table including user index tables.

Another test also checks that the migrations don't do something surprisingly expensive

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 17, 2026 08:45
@timothycoleman
timothycoleman requested a review from a team as a code owner September 17, 2026 08:45
@linear-code

linear-code Bot commented Sep 17, 2026

Copy link
Copy Markdown

DB-2195

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Reduce DuckDB migration temp usage and align index schemas

🐞 Bug fix 🧪 Tests ✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Replaces the V1 cast to avoid row-rewrite temporary-space amplification.
• Adds V2 migration to align migrated record_id columns with fresh schemas.
• Verifies legacy paths, schema parity, backfills, and bounded migration resources.
Diagram

graph TD
  DB["Index database"] --> VERSION{"Schema path"}
  VERSION -->|V0| V1B["V1B migration"] --> V2["V2 alignment"] --> FINAL["Fresh-equivalent schema"]
  VERSION -->|Legacy V1A| V1A["Existing V1A state"] --> V2
  VERSION -->|Empty| FRESH["Fresh schema setup"] --> FINAL
Loading
High-Level Assessment

The chosen approach is appropriate: preserve the shipped V1 implementation as V1A for compatibility testing, register the constant-default V1B for new upgrades, and use a catalog-only V2 migration to reconcile schemas. Directly editing the historical migration would obscure the deployed upgrade path, while batched row updates would remain proportional to index size and retain unnecessary temporary-space risk.

Files changed (10) +484 / -9

Bug fix (4) +126 / -4
UserIndexSql.csValidate discovered user-index table names +13/-1

Validate discovered user-index table names

• Requires discovered user-index tables to satisfy the same identifier validation used during creation. This keeps migration-interpolated DDL limited to safe, producible table names.

src/KurrentDB.SecondaryIndexing/Indexes/User/UserIndexSql.cs

IndexingDbSchema.V1.B.csAdd low-temporary-space V1B migration +56/-0

Add low-temporary-space V1B migration

• Replaces the casted record_id default with a bare empty-string default for the primary and user index tables. DuckDB recognizes the value as constant and avoids its expensive per-row update workaround.

src/KurrentDB.SecondaryIndexing/Storage/IndexingDbSchema.V1.B.cs

IndexingDbSchema.V2.csReconcile migrated record_id column definitions +49/-0

Reconcile migrated record_id column definitions

• Adds a catalog-only migration that drops record_id defaults and applies NOT NULL constraints to the primary and all valid user-index tables. Migrated schemas therefore match fresh installations without rewriting table data.

src/KurrentDB.SecondaryIndexing/Storage/IndexingDbSchema.V2.cs

IndexingDbSchema.Versioning.csRegister V1B and advance schema version to V2 +8/-3

Register V1B and advance schema version to V2

• Raises the target schema version to 2, replaces the registered V1 action with V1B, and registers V2. It also documents the policy for preserving shipped migrations and testing patched variants.

src/KurrentDB.SecondaryIndexing/Storage/IndexingDbSchema.Versioning.cs

Refactor (1) +5 / -1
IndexingDbSchema.V1.A.csFreeze the originally shipped V1 migration as V1A +5/-1

Freeze the originally shipped V1 migration as V1A

• Renames the original V1 migration to V1A and exposes it internally without registering it. This preserves the historical implementation for reproducing already-migrated database states in tests.

src/KurrentDB.SecondaryIndexing/Storage/IndexingDbSchema.V1.A.cs

Tests (4) +352 / -3
MigrationTests.SchemaParity.csValidate migrated schemas against fresh installations +108/-0

Validate migrated schemas against fresh installations

• Adds full information-schema comparisons for migrations beginning at V0 and the legacy V1A state. The comparison includes a production-created user index table to detect schema drift across all table columns.

src/KurrentDB.SecondaryIndexing.Tests/Migration/MigrationTests.SchemaParity.cs

MigrationTests.TempBudget.csEnforce bounded migration resource usage +171/-0

Enforce bounded migration resource usage

• Adds constrained-memory and temporary-space tests over 400,000 seeded rows. It verifies V1B and V2 fit within their budgets, confirms V1A exceeds the limit as a control, and checks migrated data survives unchanged.

src/KurrentDB.SecondaryIndexing.Tests/Migration/MigrationTests.TempBudget.cs

MigrationTests.V1.csVerify V1B record_id backfill semantics +52/-1

Verify V1B record_id backfill semantics

• Corrects the expected user-index table name and adds assertions that V1B backfills record_id with non-null, zero-length BLOB values. The test also guards against an unintended single-NUL-byte representation.

src/KurrentDB.SecondaryIndexing.Tests/Migration/MigrationTests.V1.cs

MigrationTests.csAlign V0 fixtures and add row seeding support +21/-2

Align V0 fixtures and add row seeding support

• Updates the V0 user-index fixture to match production naming and field-column conventions. Adds reusable bulk seeding for migration data and resource-budget tests.

src/KurrentDB.SecondaryIndexing.Tests/Migration/MigrationTests.cs

Other (1) +1 / -1
IndexingDbSchema.Migration.csExpose migration execution for internal testing +1/-1

Expose migration execution for internal testing

• Changes the version-range migration runner from private to internal so tests can execute selected migration actions and capture failures.

src/KurrentDB.SecondaryIndexing/Storage/IndexingDbSchema.Migration.cs

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The tightened table-name predicate is shared by the supposedly frozen V1A migration, so legacy prefixed tables outside the current naming regex can be left unmigrated.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Reduces temporary space used by secondary-index migrations and aligns migrated schemas with fresh installs.

Changes:

  • Replaces V1 with cheaper V1B and adds V2 schema reconciliation.
  • Adds schema-parity, backfill, and temp-budget migration tests.
  • Validates user-index table names before interpolating them into DDL.
File summaries
File Description
IndexingDbSchema.Versioning.cs Registers V1B and V2.
IndexingDbSchema.V1.A.cs Preserves the original V1 migration for tests.
IndexingDbSchema.V1.B.cs Uses the cheaper default expression.
IndexingDbSchema.V2.cs Aligns migrated record_id columns.
IndexingDbSchema.Migration.cs Exposes migration helpers to tests.
UserIndexSql.cs Validates migration table identifiers.
Migration test files Add schema, backfill, and temp-budget coverage.
Review details
  • Files reviewed: 10/10 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/KurrentDB.SecondaryIndexing/Indexes/User/UserIndexSql.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants