Skip to content

refactor: implement runtime-owned table DDL - #925

Merged
jiangzhe merged 2 commits into
mainfrom
runtime-table-ddl
Aug 2, 2026
Merged

refactor: implement runtime-owned table DDL#925
jiangzhe merged 2 commits into
mainfrom
runtime-table-ddl

Conversation

@jiangzhe

@jiangzhe jiangzhe commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Closes #924

Summary by CodeRabbit

  • New Features

    • CREATE TABLE and DROP TABLE now offer stronger validation, locking, progress tracking, and cancellation handling.
    • Table operations provide clearer progress and failure diagnostics.
  • Bug Fixes

    • Improved recovery for abandoned, cancelled, interrupted, or failed table operations.
    • Added safeguards for lifecycle transitions, lock ownership, cleanup, and engine health.
  • Tests

    • Expanded coverage for DDL supervision, cancellation, locking, lifecycle blocking, panic recovery, and cleanup.

@jiangzhe jiangzhe self-assigned this Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 654eac94-7cc7-4795-9513-2112df6151aa

📥 Commits

Reviewing files that changed from the base of the PR and between fb4d070 and 8248def.

⛔ Files ignored due to path filters (3)
  • docs/backlogs/000171-exact-family-lock-system-redesign.md is excluded by none and included by none
  • docs/rfcs/0026-engine-owned-mandatory-background-runtime.md is excluded by none and included by none
  • docs/tasks/000249-runtime-owned-table-ddl.md is excluded by none and included by none
📒 Files selected for processing (2)
  • doradb-storage/src/catalog/table.rs
  • doradb-storage/src/table/layout.rs

📝 Walkthrough

Walkthrough

CREATE TABLE and DROP TABLE now run as prepared mandatory-runtime operations. The change adds scoped lock ownership, prepared catalog-write authority, lifecycle-aware transaction handling, phase-specific cleanup, panic supervision, and tests for cancellation, abandonment, poisoning, and shutdown.

Changes

Table DDL runtime migration

Layer / File(s) Summary
DDL preparation and operation ownership
doradb-storage/src/session.rs
Session code validates CREATE and DROP requests, acquires canonical lock scopes, retains exact operation entries, and submits accepted work to mandatory runtime execution.
Prepared catalog authority and transaction lifecycle
doradb-storage/src/trx/mod.rs, doradb-storage/src/trx/stmt.rs, doradb-storage/src/trx/sys.rs
Catalog statements use prepared lock authority. Mandatory private transactions now support installation, checkout, completion, cleanup, and terminal publication.
Mandatory runtime supervision and diagnostics
doradb-storage/src/runtime/mandatory.rs, doradb-storage/src/completion.rs
Table-specific task metadata, guard-based submission, panic handling, completion bridging, and poison diagnostics were updated.
CREATE and DROP execution state machines
doradb-storage/src/catalog/table.rs
CREATE and DROP use prepared and accepted execution carriers with phase-tracked progress, catalog commit ordering, runtime installation or retention, and failure cleanup.
DDL supervision and lifecycle tests
doradb-storage/src/catalog/table.rs, doradb-storage/src/engine.rs, doradb-storage/src/table/*, doradb-storage/src/table/persistence.rs
Per-engine fault controllers and synchronization cover failures, panics, lock ownership, lifecycle blocking, abandoned observers, purge processing, and cleanup.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Session
  participant MandatoryRuntime
  participant CatalogTransaction
  participant TableRuntime
  Session->>MandatoryRuntime: submit accepted CREATE or DROP
  MandatoryRuntime->>CatalogTransaction: stage and commit catalog DDL
  CatalogTransaction-->>MandatoryRuntime: return commit result
  MandatoryRuntime->>TableRuntime: install or retain runtime state
  MandatoryRuntime-->>Session: publish completion or retained failure
Loading

Possibly related PRs

  • jiangzhe/doradb#923: Provides the mandatory runtime and operation-ownership APIs used by this migration.
  • jiangzhe/doradb#915: Introduces related session-operation ownership and lifecycle coordination.
  • jiangzhe/doradb#682: Adds transaction stable-entry and operation-lifecycle mechanisms extended by this change.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: moving table DDL ownership into the runtime.
Linked Issues check ✅ Passed The changes implement runtime-owned CREATE/DROP TABLE execution, lock and authority transfer, lifecycle handling, panic supervision, cleanup, and deterministic tests.
Out of Scope Changes check ✅ Passed The supporting transaction, lock, runtime, and test changes directly enable runtime-owned table DDL, while index DDL remains outside the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch runtime-table-ddl

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.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 7 complexity · 0 duplication

Metric Results
Complexity 7
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown

Greptile Summary

The PR moves accepted CREATE TABLE and DROP TABLE execution onto the engine-owned mandatory runtime while retaining cancellable validation and lock preparation on the caller.

  • Adds prepared-to-accepted DDL ownership transfer with completion observation and panic retention.
  • Introduces operation-owned catalog lock authority for nested private transactions.
  • Preserves CREATE compensation and DROP lifecycle/retention behavior under the new execution model.
  • Expands lifecycle, cancellation, locking, panic, and cleanup tests and documentation.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains within the eligible follow-up review scope.

Important Files Changed

Filename Overview
doradb-storage/src/catalog/table.rs Refactors CREATE and DROP into prepared and accepted execution carriers with explicit progress, compensation, panic, and retention handling.
doradb-storage/src/session.rs Adds caller-side DDL preparation, owned lock scopes, mandatory-operation transitions, and terminal settlement.
doradb-storage/src/runtime/mandatory.rs Updates mandatory execution supervision and completion ownership for accepted table DDL.
doradb-storage/src/trx/mod.rs Extends mandatory operations to own nested private transactions and retain transaction state after supervised panics.
doradb-storage/src/trx/stmt.rs Adds prepared catalog-write authority and panic-safe settlement of catalog statements.
doradb-storage/src/trx/sys.rs Integrates nested mandatory transactions with transaction-system completion and cleanup paths.
doradb-storage/src/lock/mod.rs Removes the superseded foreground CREATE lock helper in favor of operation-owned prepared lock scopes.
doradb-storage/src/completion.rs Promotes exclusive completion consumption APIs from reserved code to production mandatory-runtime use.

Reviews (2): Last reviewed commit: "fix issues and resolve task" | Re-trigger Greptile

@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.60610% with 76 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.32%. Comparing base (f97c320) to head (8248def).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
doradb-storage/src/catalog/table.rs 94.76% 41 Missing ⚠️
doradb-storage/src/session.rs 93.67% 16 Missing ⚠️
doradb-storage/src/runtime/mandatory.rs 91.05% 11 Missing ⚠️
doradb-storage/src/trx/mod.rs 94.52% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #925      +/-   ##
==========================================
+ Coverage   93.27%   93.32%   +0.04%     
==========================================
  Files         148      148              
  Lines      123127   123953     +826     
==========================================
+ Hits       114845   115677     +832     
+ Misses       8282     8276       -6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🧹 Nitpick comments (6)
doradb-storage/src/trx/mod.rs (2)

1836-1840: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Validate the state inside publish_mandatory_terminal.

publish_mandatory_terminal writes Terminal unconditionally. The validation lives in assert_mandatory_finish_ready, which a caller must invoke separately. MandatoryOperationGuard::finish in doradb-storage/src/session.rs (Lines 1388-1397) calls finish_mandatory without asserting readiness itself; the assertion runs earlier in AcceptedTableDdlScope::mark_terminal_ready. Calling the check inside the publication keeps the two steps inseparable.

♻️ Proposed change
     pub(crate) fn publish_mandatory_terminal(&self) {
-        self.inner.lock().state = SessionOperationState::Terminal;
+        let mut inner = self.inner.lock();
+        assert!(
+            inner.state == SessionOperationState::Mandatory(None)
+                && inner.trx_id.is_none()
+                && inner.trx_inner.is_none(),
+            "mandatory terminal publication requires empty accepted authority: key={}, state={}",
+            self.key,
+            inner.state.label()
+        );
+        inner.state = SessionOperationState::Terminal;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doradb-storage/src/trx/mod.rs` around lines 1836 - 1840, Update
publish_mandatory_terminal to invoke assert_mandatory_finish_ready immediately
before setting the state to Terminal, keeping validation and publication
inseparable while preserving the existing terminal-state assignment.

1769-1783: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Make the unreachable arm explicit instead of returning None.

Lines 1766-1768 already take the transaction core and clear trx_id before this match. completion_owned is true only for Mandatory(Some(Completing)), Voluntary(Some(Completing)), or Completing, so _ => None is unreachable. If a future state variant reaches it, the entry loses its payload while the caller observes "no completion" and never finalizes the operation. Panic with the observed state instead.

♻️ Proposed change
-            _ => None,
+            state => panic!(
+                "owned transaction completion requires a completing state: key={}, state={}",
+                self.key,
+                state.label()
+            ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doradb-storage/src/trx/mod.rs` around lines 1769 - 1783, Update the match in
the transaction completion logic around completion_owned to replace the
unreachable _ => None arm with an explicit panic that includes the observed
state, ensuring unexpected variants cannot silently report no completion after
trx_id is cleared.
doradb-storage/src/session.rs (1)

1962-1984: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a debug assertion that entry is the active slot entry.

accept_mandatory and finish_mandatory take the lifecycle lock but do not check that entry is pointer-identical to the entry stored in SessionOperationSlot::Active. The doc comment states this invariant. A debug_assert! makes the invariant executable and protects future callers.

🛡️ Proposed debug assertion
     fn accept_mandatory(&self, entry: &Arc<SessionOperationEntry>) {
         let lifecycle = self.lifecycle.lock();
+        debug_assert!(
+            matches!(&lifecycle.slot, SessionOperationSlot::Active(active) if Arc::ptr_eq(active, entry)),
+            "mandatory acceptance requires the exact active slot entry: key={}",
+            entry.key()
+        );
         entry.accept_mandatory();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doradb-storage/src/session.rs` around lines 1962 - 1984, Add debug-only
pointer-identity assertions in accept_mandatory and finish_mandatory while
holding the lifecycle lock, verifying that the supplied entry matches the entry
stored in the active SessionOperationSlot state. Keep the existing ownership,
publication, and notification behavior unchanged.
doradb-storage/src/trx/stmt.rs (1)

892-909: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the two authority checks into one branch.

The method tests self.prepared_catalog_write twice, once at Line 892 and once at Line 905, separated only by validation. A single branch that acquires both locks, or asserts once, states the policy in one place. catalog_delete_primary_key_mvcc_inner at Lines 946-963 repeats the same shape.

♻️ Proposed change
-        if let Some(authority) = self.prepared_catalog_write {
-            authority.assert_table_write(table_id);
-        } else {
-            self.acquire_table_write_metadata_lock(table_id)
-                .await
-                .attach_with(|| format!("operation={OPERATION}, table_id={table_id}"))?;
-        }
         if !self.disable_dml_validation {
             DmlValidator::new(table.metadata())
                 .validate_full_row(&cols)
                 .change_context(OperationError::InvalidDmlInput)
                 .attach_with(|| format!("operation={OPERATION}, table_id={table_id}"))?;
         }
-        if self.prepared_catalog_write.is_none() {
+        if let Some(authority) = self.prepared_catalog_write {
+            authority.assert_table_write(table_id);
+        } else {
+            self.acquire_table_write_metadata_lock(table_id)
+                .await
+                .attach_with(|| format!("operation={OPERATION}, table_id={table_id}"))?;
             self.acquire_table_write_data_lock(table_id)
                 .await
                 .attach_with(|| format!("operation={OPERATION}, table_id={table_id}"))?;
         }

Note: this moves validation before lock acquisition. If the existing order (metadata lock, then validation, then data lock) is deliberate, keep the order and store the authority in a local instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doradb-storage/src/trx/stmt.rs` around lines 892 - 909, Collapse the
duplicate self.prepared_catalog_write checks in the current method into one
branch while preserving the existing metadata-lock, validation, and data-lock
order; store the authority state in a local if needed rather than moving
validation. Apply the same consolidation to
catalog_delete_primary_key_mvcc_inner, keeping prepared-authority assertion
versus lock acquisition behavior unchanged.
doradb-storage/src/catalog/table.rs (2)

4311-4311: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Derive the expected lock counts from the write-target arrays.

The counts 9 and 12 are hardcoded. They equal 1 + 2 * create_table_catalog_write_targets().len() and 2 + 2 * drop_table_catalog_write_targets().len(). If a catalog table is added to either array, the test fails with an opaque number instead of pointing at the cause.

♻️ Proposed change
-            assert_eq!(lock_entry_count(&engine, create_owner), 9);
+            let expected_create_locks = 1 + 2 * create_table_catalog_write_targets().len();
+            assert_eq!(lock_entry_count(&engine, create_owner), expected_create_locks);

Also applies to: 4372-4372

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doradb-storage/src/catalog/table.rs` at line 4311, Update the lock-count
assertions in the affected table catalog test to derive expected values from
create_table_catalog_write_targets().len() and
drop_table_catalog_write_targets().len(), preserving the existing 1/2 base
offsets and 2x multipliers instead of hardcoding 9 and 12.

37-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use named catalog table constants instead of raw slot indices.

The arrays encode the catalog write set as slot numbers. The relationship between each slot and the catalog table written by execute_create_table_catalog_staging and execute_drop_table_catalog_cascade is implicit. Named constants make the lock coverage auditable when the cascade changes.

♻️ Example shape
-const DROP_TABLE_CATALOG_WRITE_TARGETS: [TableID; 5] = [
-    catalog_table_id_from_slot(0),
-    catalog_table_id_from_slot(1),
-    catalog_table_id_from_slot(2),
-    catalog_table_id_from_slot(3),
-    catalog_table_id_from_slot(4),
-];
+const DROP_TABLE_CATALOG_WRITE_TARGETS: [TableID; 5] = [
+    TABLE_ID_TABLES,
+    TABLE_ID_COLUMNS,
+    TABLE_ID_INDEXES,
+    TABLE_ID_INDEX_COLUMNS,
+    TABLE_ID_TABLE_REPLAY_SILENT_WATERMARKS,
+];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doradb-storage/src/catalog/table.rs` around lines 37 - 49, Replace the raw
slot indices in CREATE_TABLE_CATALOG_WRITE_TARGETS and
DROP_TABLE_CATALOG_WRITE_TARGETS with the named catalog table constants
corresponding to the tables written by execute_create_table_catalog_staging and
execute_drop_table_catalog_cascade. Preserve the existing target sets and
ordering while making each lock target explicit and auditable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@doradb-storage/src/catalog/table.rs`:
- Around line 1375-1388: Preserve the primary failure when cleanup also fails in
both accepted DDL state machines: in
doradb-storage/src/catalog/table.rs:1375-1388, capture the begin_private_trx
error’s debug string and attach it as source_error to the provisional-file
cleanup error; in doradb-storage/src/catalog/table.rs:1683-1694, capture the
start_drop_lifecycle error and attach it to the rollback_catalog_ddl error.
Follow the existing abort_before_catalog_commit pattern so the original
rejection reason remains available.

In `@doradb-storage/src/table/layout.rs`:
- Around line 295-310: In the test flow around create_table2_for_test and the
purge_event_rx loop, explicitly request a purge cycle by sending
Purge::FullObservation (or the appropriate existing purge-request variant)
before awaiting CycleCompleted. Keep the existing CommittedRecorded gating and
event handling unchanged.

In `@doradb-storage/src/trx/sys.rs`:
- Around line 1130-1142: Add the explicit-session-lock validation to both index
DDL flows, create_index_for_session and drop_index_for_session, immediately
around their begin_private_trx setup. Call
reject_table_ddl_explicit_session_lock with the target table_id and
operation_lock_owner(), preserving the existing transaction behavior while
rejecting explicitly locked tables.

---

Nitpick comments:
In `@doradb-storage/src/catalog/table.rs`:
- Line 4311: Update the lock-count assertions in the affected table catalog test
to derive expected values from create_table_catalog_write_targets().len() and
drop_table_catalog_write_targets().len(), preserving the existing 1/2 base
offsets and 2x multipliers instead of hardcoding 9 and 12.
- Around line 37-49: Replace the raw slot indices in
CREATE_TABLE_CATALOG_WRITE_TARGETS and DROP_TABLE_CATALOG_WRITE_TARGETS with the
named catalog table constants corresponding to the tables written by
execute_create_table_catalog_staging and execute_drop_table_catalog_cascade.
Preserve the existing target sets and ordering while making each lock target
explicit and auditable.

In `@doradb-storage/src/session.rs`:
- Around line 1962-1984: Add debug-only pointer-identity assertions in
accept_mandatory and finish_mandatory while holding the lifecycle lock,
verifying that the supplied entry matches the entry stored in the active
SessionOperationSlot state. Keep the existing ownership, publication, and
notification behavior unchanged.

In `@doradb-storage/src/trx/mod.rs`:
- Around line 1836-1840: Update publish_mandatory_terminal to invoke
assert_mandatory_finish_ready immediately before setting the state to Terminal,
keeping validation and publication inseparable while preserving the existing
terminal-state assignment.
- Around line 1769-1783: Update the match in the transaction completion logic
around completion_owned to replace the unreachable _ => None arm with an
explicit panic that includes the observed state, ensuring unexpected variants
cannot silently report no completion after trx_id is cleared.

In `@doradb-storage/src/trx/stmt.rs`:
- Around line 892-909: Collapse the duplicate self.prepared_catalog_write checks
in the current method into one branch while preserving the existing
metadata-lock, validation, and data-lock order; store the authority state in a
local if needed rather than moving validation. Apply the same consolidation to
catalog_delete_primary_key_mvcc_inner, keeping prepared-authority assertion
versus lock acquisition behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e9cf194-dbf7-403f-acd2-83ae0d261475

📥 Commits

Reviewing files that changed from the base of the PR and between f97c320 and fb4d070.

⛔ Files ignored due to path filters (8)
  • docs/benchmark-tool.md is excluded by none and included by none
  • docs/engine-component-lifetime.md is excluded by none and included by none
  • docs/lock-system.md is excluded by none and included by none
  • docs/public-error-audit.csv is excluded by !**/*.csv and included by none
  • docs/table-file.md is excluded by none and included by none
  • docs/tasks/000249-runtime-owned-table-ddl.md is excluded by none and included by none
  • docs/tasks/next-id is excluded by none and included by none
  • docs/transaction-system.md is excluded by none and included by none
📒 Files selected for processing (11)
  • doradb-storage/src/catalog/table.rs
  • doradb-storage/src/completion.rs
  • doradb-storage/src/engine.rs
  • doradb-storage/src/lock/mod.rs
  • doradb-storage/src/runtime/mandatory.rs
  • doradb-storage/src/session.rs
  • doradb-storage/src/table/layout.rs
  • doradb-storage/src/table/persistence.rs
  • doradb-storage/src/trx/mod.rs
  • doradb-storage/src/trx/stmt.rs
  • doradb-storage/src/trx/sys.rs
💤 Files with no reviewable changes (2)
  • doradb-storage/src/completion.rs
  • doradb-storage/src/lock/mod.rs

Comment thread doradb-storage/src/catalog/table.rs
Comment thread doradb-storage/src/table/layout.rs
Comment thread doradb-storage/src/trx/sys.rs
@jiangzhe
jiangzhe merged commit af6d7d4 into main Aug 2, 2026
11 of 12 checks passed
@jiangzhe
jiangzhe deleted the runtime-table-ddl branch August 2, 2026 06:27
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.

Task: Runtime-Owned Table DDL

1 participant