Acquire the change-stream updateLookup target collection upfront, tolerating existence changes across restore

XMLWordPrintableJSON

    • Type: New Feature
    • Resolution: Won't Fix
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: None
    • None
    • Query Execution
    • QE 2026-07-06
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Context

      SERVER-129529 routes collection-level change-stream updateLookup through an SBE executor
      that compiles a parameterized plan once and reuses it across getMores. That plan caches a
      CollectionAcquisition inside sbe::ScanStageBase::_coll, captured once at prepare() time and
      held for the plan's whole cached lifetime -- many getMores, not one.

      Problem

      OnDemandCollectionAcquirer's acquisition is backed by TransactionResources scoped to a single
      OperationContext. Since ScanStageBase::_coll deliberately outlives that opCtx (that's the whole
      point of caching the plan), the underlying TransactionResources gets torn down while a live
      reference to it is still held, tripping the ~CollectionsInUse / held storage-admission-ticket
      invariants (server crash: leftoverCollections == 0 or !_ticket->valid()).

      Fix

      Acquire the watched (updateLookup target) collection upfront, alongside the oplog, in
      OplogAggCatalogState -- via a new buildChangeStreamAcquisitionRequests() helper -- so its
      TransactionResources are owned by the pipeline's cursor-scoped stasher instead of any single
      operation. A plan-cached reference into cursor-scoped storage is safe by construction; there's
      no operation whose teardown it needs to survive.

      Because the watched collection is a dynamic user collection (may be renamed, dropped, or
      recreated while the stream runs), add an opt-in AcquisitionPrerequisites flag
      tolerateCollectionExistenceChangeOnRestore: a kRead acquisition with this flag set is
      re-acquired in its current state across yield/restore instead of killing the query with
      QueryPlanKilled. Default false, so existing acquisitions are unaffected.

      Scope

      This ticket lands the acquisition infrastructure only:
      * tolerateCollectionExistenceChangeOnRestore in shard_role.h/.cpp, transaction_resources.h
      * buildChangeStreamAcquisitionRequests() (new file, commands/query_cmd/)
      * Wiring into OplogAggCatalogState (aggregation_execution_state.cpp)

      Consuming this via bindCatalogInfo and PreAcquiredCollectionAcquirer for the SBE
      updateLookup executor is tracked in SERVER-129529.

            Assignee:
            Denis Grebennicov
            Reporter:
            Denis Grebennicov
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: