Skip to content

refactor: harden mandatory runtime lifecycle fairness stats and readiness - #932

Merged
jiangzhe merged 3 commits into
mainfrom
runtime-readiness
Aug 3, 2026
Merged

refactor: harden mandatory runtime lifecycle fairness stats and readiness#932
jiangzhe merged 3 commits into
mainfrom
runtime-readiness

Conversation

@jiangzhe

@jiangzhe jiangzhe commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Closes #931

Summary by CodeRabbit

  • New Features

    • Added runtime statistics for operation and transaction-cleanup tasks, including counts, errors, panics, detached observers, active tasks, and timing information.
    • Sessions can now expose mandatory runtime statistics for diagnostics.
    • Runtime statistics types are publicly available for integration and monitoring.
  • Bug Fixes

    • Improved executor fairness during index operations and rollback work, helping cleanup and concurrent operations continue progressing.
  • Documentation

    • Clarified runtime sizing, shutdown behavior, cleanup ownership, and operation-state diagnostics.
  • Diagnostics

    • Shutdown messages now identify the shutdown source and current operation state.

@jiangzhe jiangzhe self-assigned this Aug 3, 2026
@codacy-production

codacy-production Bot commented Aug 3, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 3 medium

Results:
3 new issues

Category Results
Complexity 3 medium

View in Codacy

🟢 Metrics 42 complexity · -4 duplication

Metric Results
Complexity 42
Duplication -4

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.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (4)
  • docs/backlogs/000176-quantify-rollback-saturation-alongside-mandatory-ddl.md is excluded by none and included by none
  • docs/backlogs/next-id 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/000252-mandatory-runtime-lifecycle-fairness-evolution-readiness.md is excluded by none and included by none

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 66d90e5a-2a23-49f5-9897-8b9caa2ab50b

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The mandatory runtime now records operation and transaction-cleanup lifecycle statistics, exposes them through Session, and reports them in benchmarks. Shutdown diagnostics identify origin and operation state. Polling budgets bound index population and undo rollback work.

Changes

Mandatory runtime lifecycle

Layer / File(s) Summary
Runtime metrics and lifecycle instrumentation
doradb-storage/src/runtime/mandatory.rs
The runtime records task counters, timings, outcomes, active counts, and observer detachment for operation and transaction-cleanup tasks. Tests cover successful, failed, detached, saturated, concurrent, and panic outcomes.
Statistics API and benchmark reporting
doradb-storage/src/stats.rs, doradb-storage/src/lib.rs, doradb-storage/src/session.rs, doradb-bench/src/output.rs
Public snapshots and session diagnostics expose mandatory-runtime statistics. Benchmark output emits the new counters and timings, increasing the expected metric count from 73 to 93.
Shutdown origin and operation-state diagnostics
doradb-storage/src/engine.rs
Shutdown paths distinguish explicit shutdown from owner-drop shutdown and include operation-state details in logs, errors, and tests.
Polling budgets and cleanup fairness
doradb-storage/src/runtime/mod.rs, doradb-storage/src/catalog/index.rs, doradb-storage/src/trx/undo/index.rs, doradb-storage/src/trx/undo/row.rs, doradb-storage/src/trx/mod.rs
Index population and undo rollback yield after POLL_BUDGET work items. Tests verify concurrent CREATE/DROP INDEX operations and transaction-cleanup progress with one runner.
Runtime sizing and admission documentation
doradb-storage/src/conf/engine.rs
Configuration documentation describes runner sizing, caller capacity, cleanup admission, shutdown behavior, and startup validation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Session
  participant MandatoryRuntime
  participant OperationTask
  participant CleanupTask
  Session->>MandatoryRuntime: Submit operation
  MandatoryRuntime->>OperationTask: Supervise and record lifecycle
  MandatoryRuntime->>CleanupTask: Submit transaction cleanup
  CleanupTask-->>MandatoryRuntime: Publish completion metrics
  Session->>MandatoryRuntime: Request statistics
  MandatoryRuntime-->>Session: Return runtime snapshot
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive Reviewable code addresses lifecycle statistics, shutdown diagnostics, poll budgets, and fairness tests, but filtered documentation files prevent full verification. Review the filtered documentation files, especially the RFC-0026 amendment and benchmark documentation, to verify all linked issue requirements.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the runtime lifecycle, fairness statistics, and readiness changes.
Out of Scope Changes check ✅ Passed The reviewed changes align with the linked issue objectives and show no unrelated code changes.
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-readiness

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.

@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR expands mandatory-runtime diagnostics and statistics, adds cooperative scheduling points to long-running index and undo work, and improves lifecycle readiness documentation.

  • Exposes engine-global operation and transaction-cleanup statistics through sessions and benchmark output.
  • Records terminal caller statistics before publishing completion to result waiters.
  • Adds bounded cooperative yields to index construction and transaction rollback loops.
  • Improves mandatory-runtime task events, shutdown diagnostics, configuration documentation, and lifecycle tests.

Confidence Score: 5/5

The PR appears safe to merge.

The previously reported completion-ordering defect is fixed: mandatory operation terminal counters are updated before result publication wakes the observer, and no blocking failure remains.

Important Files Changed

Filename Overview
doradb-storage/src/runtime/mandatory.rs Adds fixed-class runtime counters and task events; the current completion path records terminal counters before waking result observers, resolving the prior finding.
doradb-storage/src/stats.rs Defines the public mandatory-runtime snapshot types and documents monotonic versus independently sampled fields.
doradb-storage/src/session.rs Exposes mandatory-runtime statistics through the existing read-only session inspection boundary.
doradb-bench/src/output.rs Adds one engine-global mandatory-runtime snapshot to benchmark internal-stat output without per-session duplication.
doradb-storage/src/catalog/index.rs Adds explicit cooperative scheduling boundaries to batched index construction work.
doradb-storage/src/trx/undo/index.rs Adds bounded cooperative yields while preserving reverse index-undo processing.
doradb-storage/src/trx/undo/row.rs Adds bounded cooperative yields after completed row-undo entries.
doradb-storage/src/engine.rs Enriches shutdown diagnostics with origin and operation-state details while retaining the existing drain behavior.

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

Comment thread doradb-storage/src/runtime/mandatory.rs
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.34426% with 78 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.38%. Comparing base (aa71c52) to head (e38d082).

Files with missing lines Patch % Lines
doradb-storage/src/runtime/mandatory.rs 85.96% 63 Missing ⚠️
doradb-storage/src/engine.rs 57.89% 8 Missing ⚠️
doradb-storage/src/catalog/index.rs 94.26% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #932      +/-   ##
==========================================
+ Coverage   93.36%   93.38%   +0.02%     
==========================================
  Files         149      149              
  Lines      125534   126233     +699     
==========================================
+ Hits       117203   117881     +678     
- Misses       8331     8352      +21     

☔ 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: 6

🧹 Nitpick comments (5)
doradb-storage/src/runtime/mandatory.rs (2)

1583-1598: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated runtime-construction boilerplate into a test helper.

Six tests in this module repeat the same eleven-line block: build RegistryBuilder, build EnginePoisoner, build MandatoryRuntime with a config, build MandatoryRuntimeWorkers, finish, and take the dependency. Codacy reports both new tests over the 50-line limit, and the duplicated setup is the bulk of that length.

Add one helper and call it from each test.

♻️ Proposed helper
async fn build_test_runtime(
    worker_threads: usize,
    concurrency_limit: usize,
) -> (Registry, QuiescentGuard<MandatoryRuntime>) {
    let mut builder = RegistryBuilder::new();
    builder.build::<EnginePoisoner>(()).await.unwrap();
    builder
        .build::<MandatoryRuntime>(
            MandatoryRuntimeConfig::default()
                .worker_threads(worker_threads)
                .concurrency_limit(concurrency_limit),
        )
        .await
        .unwrap();
    builder.build::<MandatoryRuntimeWorkers>(()).await.unwrap();
    let registry = builder.finish();
    let mandatory = registry.dependency::<MandatoryRuntime>();
    (registry, mandatory)
}
-            let mut builder = RegistryBuilder::new();
-            builder.build::<EnginePoisoner>(()).await.unwrap();
-            builder
-                .build::<MandatoryRuntime>(
-                    MandatoryRuntimeConfig::default()
-                        .worker_threads(1)
-                        .concurrency_limit(1),
-                )
-                .await
-                .unwrap();
-            builder.build::<MandatoryRuntimeWorkers>(()).await.unwrap();
-            let registry = builder.finish();
-            let mandatory = registry.dependency::<MandatoryRuntime>();
+            let (registry, mandatory) = build_test_runtime(1, 1).await;

As per coding guidelines: "Follow the project's unit-testing guidance from docs/process/unit-test.md when adding or modifying Rust tests."

Also applies to: 1642-1657

🤖 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/runtime/mandatory.rs` around lines 1583 - 1598, Extract
the repeated RegistryBuilder setup into an async build_test_runtime helper
accepting worker_threads and concurrency_limit, returning the Registry and
QuiescentGuard<MandatoryRuntime>. Replace the duplicated construction blocks in
all six affected tests, including the tests around
ordinary_error_and_observer_detach_are_counted_by_outcome and the additional
referenced range, while preserving each test’s configuration values and
behavior.

Sources: Coding guidelines, Linters/SAST tools


1132-1140: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Store these nanosecond counters as u64.

duration_nanos converts u128 nanoseconds to usize, so the value is truncated before it reaches admission_wait_nanos, queue_wait_nanos, and execution_nanos. Use u64 counters to avoid changing the field type and make the intended 292-year accumulator range explicit.

🤖 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/runtime/mandatory.rs` around lines 1132 - 1140, Update
elapsed_nanos and duration_nanos to return u64, converting Duration::as_nanos()
to u64 so admission_wait_nanos, queue_wait_nanos, and execution_nanos retain
their existing field type without usize truncation.
doradb-bench/src/output.rs (2)

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

Mark the absolute metric so consumers do not read it as a delta.

Every other metric this function emits is a before/after delta. active_count is the after value. The exported name gives no hint of the difference, so a CSV consumer comparing mandatory.operation.submitted_count against mandatory.operation.active_count will apply the wrong interpretation. Add a code comment, and consider a distinguishing suffix such as active_count_current.

🤖 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-bench/src/output.rs` around lines 602 - 606, Update the metric
emission around push_metric for after.active_count to clearly identify it as an
absolute after/current value rather than a before/after delta. Rename the
exported metric to a distinguishing name such as active_count_current, and add a
code comment documenting that this metric is absolute.

563-622: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Collapse the ten repeated push_metric calls into a table.

Nine of the ten calls follow the same shape: a name suffix plus a delta of the same field on after and before. Codacy reports the function at 60 lines against a 50-line limit. A slice of suffix-and-accessor pairs removes the repetition and keeps active_count as the one explicit special case.

♻️ Proposed refactor
 fn push_mandatory_task_metrics(
     metrics: &mut Vec<Metric>,
     prefix: &str,
     before: MandatoryTaskStats,
     after: MandatoryTaskStats,
 ) {
-    push_metric(
-        metrics,
-        &format!("{prefix}.submitted_count"),
-        delta(after.submitted_count, before.submitted_count),
-    );
-    // ... eight more delta metrics ...
+    let cumulative: [(&str, usize, usize); 9] = [
+        ("submitted_count", after.submitted_count, before.submitted_count),
+        ("started_count", after.started_count, before.started_count),
+        ("completed_count", after.completed_count, before.completed_count),
+        ("error_count", after.error_count, before.error_count),
+        ("panic_count", after.panic_count, before.panic_count),
+        (
+            "detached_observer_count",
+            after.detached_observer_count,
+            before.detached_observer_count,
+        ),
+        ("admission_wait_nanos", after.admission_wait_nanos, before.admission_wait_nanos),
+        ("queue_wait_nanos", after.queue_wait_nanos, before.queue_wait_nanos),
+        ("execution_nanos", after.execution_nanos, before.execution_nanos),
+    ];
+    for (suffix, after_value, before_value) in cumulative {
+        push_metric(
+            metrics,
+            &format!("{prefix}.{suffix}"),
+            delta(after_value, before_value),
+        );
+    }
+    // Active work is current state, not a cumulative counter.
     push_metric(
         metrics,
         &format!("{prefix}.active_count"),
         after.active_count as u128,
     );
 }

The emitted metric names and their order stay unchanged, so the test at Lines 877-912 still passes.

🤖 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-bench/src/output.rs` around lines 563 - 622, Refactor
push_mandatory_task_metrics to emit the nine delta-based metrics through a table
of suffix/accessor pairs, iterating over that table to call push_metric while
preserving the existing names and order. Keep active_count as the sole explicit
push_metric call using after.active_count.

Source: Linters/SAST tools

doradb-storage/src/stats.rs (1)

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

Document that two fields stay zero for the cleanup task class.

MandatoryTaskStats is shared by operation and transaction_cleanup. error_count and detached_observer_count can only become non-zero for operation. Internal cleanup tasks return no result, so they have no ordinary error outcome and no observer. The current doc text says "caller tasks" and "caller observers", which hints at this but does not state it. State it directly so public consumers do not treat a zero as a signal.

📝 Proposed doc change
-    /// Number of accepted caller tasks that returned an ordinary error.
+    /// Number of accepted caller tasks that returned an ordinary error.
+    ///
+    /// This field is always zero for the `transaction_cleanup` class, because
+    /// internal cleanup tasks publish no ordinary result.
     pub error_count: usize,
     /// Number of tasks whose supervised execution panicked.
     pub panic_count: usize,
-    /// Number of caller observers dropped without consuming their result.
+    /// Number of caller observers dropped without consuming their result.
+    ///
+    /// This field is always zero for the `transaction_cleanup` class, because
+    /// internal cleanup tasks have no observer.
     pub detached_observer_count: usize,
🤖 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/stats.rs` around lines 29 - 34, Update the rustdoc for
MandatoryTaskStats fields error_count and detached_observer_count to state
explicitly that they remain zero for the transaction_cleanup task class because
cleanup tasks return no result and have no observer; preserve the existing
descriptions for operation statistics and leave panic_count unchanged.
🤖 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/index.rs`:
- Around line 2814-2897: Update
test_terminal_cleanup_progresses_during_accepted_index_ddl_on_one_runner so the
rollback future is pinned and polled until transaction cleanup submission is
observed, then release the CreateHotBuildBatchComplete gate before awaiting
rollback and CREATE INDEX completion. Add a bounded timeout or equivalent
completion signal around this scheduling sequence to prevent regressions from
hanging the test suite, while preserving the existing cleanup and operation
count assertions.

In `@doradb-storage/src/engine.rs`:
- Around line 456-458: Update each ShutdownBusy diagnostic construction in the
lifecycle shutdown paths to include origin=explicit in the attached context
string, and update the four corresponding expected assertion strings to match.
Preserve the existing diagnostic fields and ordering otherwise.
- Around line 445-455: Update the busy calculation in the shutdown-finish
logging path to include mandatory_callers and mandatory_internal, matching every
blocker checked by the shutdown condition. Ensure busy is non-zero whenever
strong_refs, operation_blocked, mandatory_callers, or mandatory_internal
prevents shutdown, while preserving the existing log fields and behavior.

In `@doradb-storage/src/runtime/mandatory.rs`:
- Around line 1001-1008: Move
self.transaction_cleanup_counters.record_submitted(0) in the submit path before
executor.spawn(...).detach(), ensuring the submission count is recorded before
supervise_internal can start or complete and preserving the existing counter
value and task behavior.
- Around line 1409-1429: Replace the blocking self.barrier.wait() in
OverlapAccepted::execute with a shared async rendezvous that does not block
executor threads, using registration state such as AtomicUsize plus an awaitable
notification triggered once both tasks arrive. Update the surrounding
OverlapAccepted test setup and shared state so execute awaits completion of both
registrations, preserving the concurrency assertion without an indefinite
deadlock.

In `@doradb-storage/src/session.rs`:
- Around line 5735-5745: Before reading the terminal stats snapshot for
mandatory1, await engine.inner().mandatory_runtime.drain_callers() to ensure
completion accounting and permit cleanup have finished. Keep the existing
assertions unchanged after the drain, rather than relaxing their expected
counts.

---

Nitpick comments:
In `@doradb-bench/src/output.rs`:
- Around line 602-606: Update the metric emission around push_metric for
after.active_count to clearly identify it as an absolute after/current value
rather than a before/after delta. Rename the exported metric to a distinguishing
name such as active_count_current, and add a code comment documenting that this
metric is absolute.
- Around line 563-622: Refactor push_mandatory_task_metrics to emit the nine
delta-based metrics through a table of suffix/accessor pairs, iterating over
that table to call push_metric while preserving the existing names and order.
Keep active_count as the sole explicit push_metric call using
after.active_count.

In `@doradb-storage/src/runtime/mandatory.rs`:
- Around line 1583-1598: Extract the repeated RegistryBuilder setup into an
async build_test_runtime helper accepting worker_threads and concurrency_limit,
returning the Registry and QuiescentGuard<MandatoryRuntime>. Replace the
duplicated construction blocks in all six affected tests, including the tests
around ordinary_error_and_observer_detach_are_counted_by_outcome and the
additional referenced range, while preserving each test’s configuration values
and behavior.
- Around line 1132-1140: Update elapsed_nanos and duration_nanos to return u64,
converting Duration::as_nanos() to u64 so admission_wait_nanos,
queue_wait_nanos, and execution_nanos retain their existing field type without
usize truncation.

In `@doradb-storage/src/stats.rs`:
- Around line 29-34: Update the rustdoc for MandatoryTaskStats fields
error_count and detached_observer_count to state explicitly that they remain
zero for the transaction_cleanup task class because cleanup tasks return no
result and have no observer; preserve the existing descriptions for operation
statistics and leave panic_count 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: 235e2eb9-70ce-444d-bac9-0ed2785cbcfd

📥 Commits

Reviewing files that changed from the base of the PR and between aa71c52 and e369712.

⛔ Files ignored due to path filters (7)
  • 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/public-error-audit.csv is excluded by !**/*.csv and included by none
  • docs/rfcs/0026-engine-owned-mandatory-background-runtime.md is excluded by none and included by none
  • docs/tasks/000252-mandatory-runtime-lifecycle-fairness-evolution-readiness.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 (12)
  • doradb-bench/src/output.rs
  • doradb-storage/src/catalog/index.rs
  • doradb-storage/src/conf/engine.rs
  • doradb-storage/src/engine.rs
  • doradb-storage/src/lib.rs
  • doradb-storage/src/runtime/mandatory.rs
  • doradb-storage/src/runtime/mod.rs
  • doradb-storage/src/session.rs
  • doradb-storage/src/stats.rs
  • doradb-storage/src/trx/mod.rs
  • doradb-storage/src/trx/undo/index.rs
  • doradb-storage/src/trx/undo/row.rs

Comment thread doradb-storage/src/catalog/index.rs
Comment thread doradb-storage/src/engine.rs
Comment thread doradb-storage/src/engine.rs
Comment thread doradb-storage/src/runtime/mandatory.rs
Comment thread doradb-storage/src/runtime/mandatory.rs
Comment thread doradb-storage/src/session.rs
@jiangzhe
jiangzhe merged commit 1481d63 into main Aug 3, 2026
9 checks passed
@jiangzhe
jiangzhe deleted the runtime-readiness branch August 3, 2026 12:36
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: Lifecycle, Fairness, And Evolution Readiness for Mandatory Runtime

1 participant