Skip to content

feat(trigger): run Event-Plane trigger bodies as one system transaction - #252

Open
EnRaiha wants to merge 1 commit into
NodeDB-Lab:mainfrom
EnRaiha:feat/trigger-body-system-txn
Open

feat(trigger): run Event-Plane trigger bodies as one system transaction#252
EnRaiha wants to merge 1 commit into
NodeDB-Lab:mainfrom
EnRaiha:feat/trigger-body-system-txn

Conversation

@EnRaiha

@EnRaiha EnRaiha commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes epic #165 (deferred actions — last unchecked box). A trigger body that fails mid-block leaves earlier statements applied, and its async retry repeats them. The Event-Plane fire path now stages every fired body into a SystemTxnScope: all statements commit together or roll back together.

Option B′ (scope-bound executor), per the Phase-1 recon: bodies are procedural control flow (If/While/For), so pre-planning the whole body (Option A) is impossible; instead each statement's tasks route into the scope as they are planned.

Gap hardening (review items 1–3)

  1. Lease-scope lifetimeSystemTxnScope::retain_lease_scope holds each statement's Arc<QueryLeaseScope> until COMMIT's version fence.
  2. Cascade sharingsystem_scope threaded through FireTriggersParams + all FireAfter*Params; StatementExecutor::with_system_scope; nested (cascade) triggers inherit the caller's scope — one failure rolls back every body the cascade touched.
  3. DDL fail-fastis_system_ddl rejects the one DDL-shaped Data-Plane op (MetaOp::ConvertCollection) with SystemTxnError::Ddl before staging.

Mechanics

  • push_task_into_scope (statement-serial staging, extracted from run_tasks_atomically; the batch function is refactored onto it — behavior identical)
  • commit_scope / rollback_scope exported helpers
  • dispatcher/single.rs: one scope per fired event (async onlyEventSource::User); row + statement fires share it; commit on FireReport::has_failure() == false, rollback otherwise
  • Sync / DEFERRED / BEFORE / INSTEAD paths explicitly system_scope: None — never double-wrapped (they already run inside the client transaction)
  • New non-consuming FireReport::has_failure()

Verification (solve-first)

  • cargo check -p nodedb --lib --tests — EXIT 0, zero warnings
  • control::system_txn — 1/1 (DDL gate)
  • control::trigger — 68/68 (no regression from the threading)
  • event::trigger — 12/12 (dispatcher wiring)

Known follow-ups (tracked, not blockers): retry-path atomicity wiring in retry_action.rs (currently None — retries run the failed body non-atomically until it lands), cluster-tests stage-2 coverage (kill mid-trigger → retry/DLQ), deferred batch.rs path.

Closes epic NodeDB-Lab#165 (deferred actions). A trigger body that failed mid-block
left earlier statements applied, and its retry repeated them. The async
Event-Plane fire path now stages every fired body into a SystemTxnScope:
all statements commit together or roll back together.

Hardened design per review (Option B', gaps 1-3):
- push_task_into_scope: statement-serial staging extracted from
  run_tasks_atomically; run_tasks_atomically refactored onto it
  (system_txn/run.rs)
- Gap 1: SystemTxnScope::retain_lease_scope keeps each statement's
  Arc<QueryLeaseScope> alive until COMMIT's version fence
- Gap 2: system_scope threaded through FireTriggersParams + all
  FireAfter*Params; executor.with_system_scope; cascade fires inherit
  the caller's scope (all-or-nothing across nested triggers)
- Gap 3: is_system_ddl gate rejects ConvertCollection before staging
  (SystemTxnError::Ddl), fail-fast
- commit_scope / rollback_scope helpers; dispatcher/single.rs begins
  one scope per fired event (async only; sync/deferred untouched) and
  commits or rolls back by FireReport::has_failure
- Sync/deferred/instead/before paths explicitly pass system_scope: None

Verified solve-first: cargo check EXIT 0 (no warnings); system_txn 1/1,
control::trigger 68/68, event::trigger 12/12.
Copilot AI lite review requested due to automatic review settings August 25, 2026 09:14

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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