-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: None
-
None
-
Query Execution
-
Fully Compatible
-
ALL
-
QE 2026-03-30
-
200
-
None
-
None
-
None
-
None
-
None
-
None
-
None
If the update operation runs into a write conflict, it will be retried: we will be forced to read the index and the document again.
In PlanExecutorImpl it is handled externally: classic UpdateStage will just throw WriteConflict exception:
This exception will be caught by writeConflictRetry function:
Because of this, the PlanExecutor that threw the exception will be destroyed and it's statistics are discarded.
Here is an example "Slow query" log:
{<..>,"msg":"Slow query","attr":{,"command":{"q":{"a":5},"u":{"$inc":{"u":1},"$push":{"updates":"a"}},"multi":false,"upsert":false},"planSummary":"IXSCAN { a: 1 }","keysExamined":1,"docsExamined":1,"nMatched":1,"nModified":1,"writeConflicts":4,<..>,"durationMillis":4}}
However, if the same happens in PlanExecutorExpress, it handles the error internally and retries inside the same executor, so the statistics are kept intact:
{<..>,"msg":"Slow query","attr":{<..>,"command":{"q":{"_id":5},"u":{"$inc":{"u":1},"$push":{"updates":"_id"}},"multi":false,"upsert":false},"planSummary":"EXPRESS_IXSCAN { _id: 1 },EXPRESS_UPDATE","keysExamined":5,"docsExamined":5,"nMatched":1,"writeConflicts":4,<..>,"durationMillis":7}}
I think the behavior of EXPRESS executor is just a side effect of optimization, but it is actually nice and reflect the performance of the operation better than regular PlanExecutor.
We should consider which behavior we prefer and make it the same across express and regular executors.
- causes
-
SERVER-123068 Fix ttl clustered index test
-
- Closed
-
- depends on
-
SERVER-121829 Track extra document reads in write_stage_common::ensureStillMatches
-
- Closed
-
- related to
-
SERVER-134502 write_conflict_preserves_plan_stats.js assertion depends on a process-wide failpoint budget
-
- Closed
-