Missing shard filter handling in propagateLimit results in tassert

XMLWordPrintableJSON

    • Query Optimization
    • Fully Compatible
    • ALL
    • v9.0
    • Hide
      /*
       * Repro for a gap in CBR limit propagation: CardinalityEstimator::propagateLimit() has no case for
       * STAGE_SHARDING_FILTER, so it falls into MONGO_UNREACHABLE_TASSERT(12039700). SHARDING_FILTER is
       * otherwise a fully supported node (it is estimated via passThroughNodeCard and is absent from both
       * isNodeUnsupportedByCBR and isNodeUnexpectedByCBR), so nothing bails out before the switch.
       *
       * Preconditions for the crash:
       *   - a sharded collection, so the shard plan contains a SHARDING_FILTER,
       *   - a $limit above it, so estimate(LimitNode) calls propagateLimit,
       *   - the limit must be strictly less than the estimated cardinality at the SHARDING_FILTER, else
       *     propagateLimit returns early at the approxGtEq(limitCE, outCE) check.
       *
       */
      
      import {ShardingTest} from "jstests/libs/shardingtest.js";
      
      const st = new ShardingTest({
          shards: 2,
          mongos: 1,
          rs: {
              setParameter: {
                  featureFlagCostBasedRanker: true,
                  internalQueryPlanRanker: "costBased",
                  internalQueryCBRCEMode: "samplingCE",
              },
          },
      });
      
      const db = st.getDB("test");
      const coll = db[jsTestName()];
      coll.drop();
      
      // Hashed shard key on _id so that predicates on 'a'/'b' cannot be shard-key targeted and the shard
      // plan retains a SHARDING_FILTER stage.
      assert.commandWorked(
          st.s.adminCommand({shardCollection: coll.getFullName(), key: {_id: "hashed"}}),
      );
      
      // Insert enough documents that a small limit is well below the estimated cardinality.
      const bulk = coll.initializeUnorderedBulkOp();
      for (let i = 0; i < 1000; i++) {
          bulk.insert({a: i % 50, b: i % 25});
      }
      assert.commandWorked(bulk.execute());
      
      // Two candidate indexes so CBR has more than one plan to rank.
      assert.commandWorked(coll.createIndexes([{a: 1}, {b: 1}]));
      
      // Limit of 5 against a predicate matching ~all documents. The non-explain query is affected too,
      // not just explain: CBR runs during normal planning.
      const findRes = db.runCommand({
          find: coll.getName(),
          filter: {a: {$gte: 0}, b: {$gte: 0}},
          limit: 5,
      });
      jsTest.log.info("find result", {findRes});
      assert.commandWorked(findRes, "expected the find to succeed");
      
      st.stop();
      
      Show
      /* * Repro for a gap in CBR limit propagation: CardinalityEstimator::propagateLimit() has no case for * STAGE_SHARDING_FILTER, so it falls into MONGO_UNREACHABLE_TASSERT(12039700). SHARDING_FILTER is * otherwise a fully supported node (it is estimated via passThroughNodeCard and is absent from both * isNodeUnsupportedByCBR and isNodeUnexpectedByCBR), so nothing bails out before the switch . * * Preconditions for the crash: * - a sharded collection, so the shard plan contains a SHARDING_FILTER, * - a $limit above it, so estimate(LimitNode) calls propagateLimit, * - the limit must be strictly less than the estimated cardinality at the SHARDING_FILTER, else * propagateLimit returns early at the approxGtEq(limitCE, outCE) check. * */ import {ShardingTest} from "jstests/libs/shardingtest.js" ; const st = new ShardingTest({ shards: 2, mongos: 1, rs: { setParameter: { featureFlagCostBasedRanker: true , internalQueryPlanRanker: "costBased" , internalQueryCBRCEMode: "samplingCE" , }, }, }); const db = st.getDB( "test" ); const coll = db[jsTestName()]; coll.drop(); // Hashed shard key on _id so that predicates on 'a' / 'b' cannot be shard-key targeted and the shard // plan retains a SHARDING_FILTER stage. assert.commandWorked( st.s.adminCommand({shardCollection: coll.getFullName(), key: {_id: "hashed" }}), ); // Insert enough documents that a small limit is well below the estimated cardinality. const bulk = coll.initializeUnorderedBulkOp(); for (let i = 0; i < 1000; i++) { bulk.insert({a: i % 50, b: i % 25}); } assert.commandWorked(bulk.execute()); // Two candidate indexes so CBR has more than one plan to rank. assert.commandWorked(coll.createIndexes([{a: 1}, {b: 1}])); // Limit of 5 against a predicate matching ~all documents. The non-explain query is affected too, // not just explain: CBR runs during normal planning. const findRes = db.runCommand({ find: coll.getName(), filter: {a: {$gte: 0}, b: {$gte: 0}}, limit: 5, }); jsTest.log.info( "find result" , {findRes}); assert.commandWorked(findRes, "expected the find to succeed" ); st.stop();
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Flagged by Claude.
      SERVER-99073 added CE/costing support for the sharding filter stage, but did not add a corresponding case for propagateLimit.

      cardinality_estimator.cpp::propagateLimit does not handle shard filter, so attempting to propagate limit across a shard filter node tasserts.

            Assignee:
            Militsa Sotirova
            Reporter:
            Kartal Kaan Bozdogan
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: