--- title: Runbook — MongoDB unknown transaction commit result category: mongodb severity: P1 owner: oncall last_updated: 2026-08-13 status: active --- # Runbook: unknown transaction commit result Design §16, decision D-10, scenario `UNKNOWN_TRANSACTION_COMMIT_RESULT`. `MongoExecutionOutcome.TRANSACTION_COMMIT_UNKNOWN` means the commit **may have applied**. It is not a failure and must never be reported to a caller as one. The single worst response is to re-run the transaction body: if the commit did apply, the body applies a second time. ## Symptoms - `MongoTransactionCommitUnknownException` in logs. - Metric `failureCategory=TRANSACTION_COMMIT_UNKNOWN`. - Usually accompanies a primary election — see [failover.md](failover.md). - Downstream reports of duplicated effects (double charge, double increment) are the symptom of this being handled wrongly, not of the condition itself. ## Diagnosis 1. **Confirm the platform did the right thing automatically.** `MongoTransactionRetryCoordinator` retries the *commit only*, on the same session, within `MongoRetryBudget`. A commit retry against an already-committed transaction is a no-op by design. Most occurrences resolve here and never reach a human. 2. **If the budget was exhausted, determine the actual state.** The commit either applied or it did not; you must find out which, not guess. - If the transaction body wrote a deterministic marker (an idempotency key, a business id, a revision), read it back. That is exactly what `MongoCommitReconciler` does, and it is the reason the design requires transactions to write one. - If there is no marker: reconstruct from a downstream artefact — an outbox row, an audit record, an external side effect. If nothing exists to compare against, the transaction was not designed to be reconcilable and that is the finding to record. 3. **Check whether the body was replayed.** Grep for a second execution with the same operation name and correlation id. If the body ran twice, the effects need reversing, and the code path that replayed it is a defect: an ambiguous commit is `COMMIT_ONLY` scope (`MongoRetryScope.COMMIT_ONLY`), never `BODY`. ## Action **Commit applied.** Nothing to do. Record the reconciliation. **Commit did not apply.** Re-run the whole operation from the top — a new session, a new body attempt. This is safe precisely because you established the previous attempt left no trace. **Cannot determine.** Do not retry. Escalate. A blind retry here is a coin flip between "no effect" and "duplicate effect", and duplicates in a financial or notification path are worse than a delay. Freeze the affected entity if the domain supports it, and hand off with: operation name, correlation id, document id, the time window, and what you checked. **Recurring.** More than one an hour means the commit path is racing something structural — a `maxCommitTime` shorter than the observed election duration, an oversized transaction, or an undersized retry budget. Fix the budget or the transaction shape; do not raise the retry count and call it resolved. ## Prevention - Every transaction body writes a deterministic marker that identifies its own commit. - `maxCommitTime` exceeds the observed p99 election duration. - Callers surface the ambiguity to their own callers rather than mapping it to a generic 500 — an ambiguous outcome reported as a failure invites the caller to retry, which is the one thing that must not happen. - Prefer a single-document atomic operation (D-09). A transaction that exists only to wrap one document write has invented this failure mode for nothing. ## Escalation - Always P1 when the state cannot be determined and the operation has an external effect. - Page the service owner immediately; the database owner only if elections are the trigger. ## Verification ```bash cd src ./gradlew :adapter:outbound:persistence-mongo:mongoFailoverTest --console=plain ``` `MongoFailoverScenario.UNKNOWN_TRANSACTION_COMMIT_RESULT` runs this path against a real three-node set, and the coordinator test asserts the body is never replayed after a commit ambiguity.