Batched-write applyOps packer cannot link a clustered collection's op to its index side writes, allowing torn atomic groups (e.g. timeseries bucket updates during PDIB)

XMLWordPrintableJSON

    • Type: Bug
    • Resolution: Fixed
    • Priority: Major - P3
    • 9.1.0-rc0
    • Affects Version/s: None
    • Component/s: None
    • None
    • Storage Execution
    • Fully Compatible
    • ALL
    • Storage Execution 2026-08-03
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Overview

      The atomic-group packing introduced by SERVER-128133 keeps a record's collection write together with its index-build side-table writes in one applyOps entry. Side writes are stamped with a groupRecordId via BatchedWriteContext::AtomicOperationGroup (index_access_method.cpp), but the collection op itself is never stamped -- the packer links it to the group only through the fallback op.getRecordId() in packOperationsIntoApplyOps (transaction_operations.cpp).

      That fallback is only populated when the collection has recordIdsReplicated:true. Clustered collections are structurally exempt from rid replication (shouldSetRecordIdsReplicated() in database_impl.cpp: "The recordId is the '_id' for clustered collections -- thus explicit recordId replication is unnecessary"), so their collection ops carry no record id, groupId() returns null, and the packer's wouldSplitGroup protection never engages for them. Timeseries bucket collections are clustered, so every timeseries bucket update during a primary-driven index build is affected.

      Impact

      When an entry's count/size boundary lands between a clustered collection op and its side writes, the group tears across applyOps entries. Under kGroupForPossiblyRetryableOperations these entries apply separately on secondaries, so a node can apply the side writes without the corresponding collection write (or vice versa), leaving the draining index inconsistent with the collection. Note SERVER-126257 removed the "tearable side write" redo backstop on the grounds that SERVER-128133 made tearing impossible, so there is currently no safety net.

      Why this is currently masked

      The boundary only lands inside a group when per-record op counts misalign with maxNumberOfBatchedOperationsInSingleOplogEntry. Existing tests (e.g. timeseries_update_group_atomicity.js) happen to produce exactly limit-aligned groups. SERVER-130645 (batched container writes) initially exposed the tear by merging side writes and shifting the arithmetic; it was re-masked by making the packer count an accumulated container op as N writes toward the entry count limit, which restores the pre-existing boundary alignment. That is an alignment guarantee, not a linkage guarantee -- the underlying hole remains.

      Suggested direction

      Stamp the collection op with the group id. For clustered collections the record id is derivable (oldLocation is in hand in collection_write_path.cpp where replicatedRecordId is conditionally set); it is withheld from the ReplOperation only because setRecordId also serializes rid into the oplog entry. The non-durable groupRecordId field exists for exactly this purpose -- e.g. open a per-record AtomicOperationGroup spanning index maintenance and the op-observer call (requires making the group re-entrant for the same record id), or stamp groupRecordId directly on the collection op.

      Interaction with SERVER-130645's packer change

      Whoever fixes this should decide deliberately whether to keep the write-counting semantics in packOperationsIntoApplyOps (an accumulated container op counts as N toward maxNumberOfBatchedOperationsInSingleOplogEntry). With proper group linkage it is no longer needed as tear protection; keeping it preserves the count limit's meaning as a bound on per-entry apply work, while dropping it maximizes accumulation density per entry.

            Assignee:
            Shin Yee Tan
            Reporter:
            Alex Sarkesian
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: