Exception incorrectly thrown when distinct query with subplanning replans

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: None
    • Query Optimization
    • ALL
    • Hide
      
      assert.commandWorked(coll.insert({a: 0, m: {m1: 0}}));
      
      // Make some indexes so the query has multiple solutions and gets cached
      assert.commandWorked(coll.createIndex({a: 1}));
      assert.commandWorked(coll.createIndex({a: 1, b: 1, m: "hashed", "m.m1": 1}));
      assert.commandWorked(coll.createIndex({"$**": 1}, {wildcardProjection: {"m.m1": 1}}));
      assert.commandWorked(coll.createIndex({"m.m2": 1, a: 1}));
      
      // Distinct-eligible, but won't generate a distinct plan, thus using the fallback to distinctEligible=false
      const pipeline = [
          {$match: {$or: [{a: {$eq: 0}}, {"m.m1": {$eq: 0}}]}},
          {$group: {_id: "$a"}},
          {$project: {_id: 0, a: 1}},
      ];
      
      // Cache the solution
      coll.aggregate(pipeline).toArray();
      coll.aggregate(pipeline).toArray();
      
      // Add more documents so the query replans
      const newDocs = [];
      for (let i = 0; i < 10000; i++) {
          newDocs.push({a: 0, m: {m1: 0}});
      }
      assert.commandWorked(coll.insert(newDocs));
      
      coll.aggregate(pipeline).toArray();
      // This query is eligible for distinct at first, but then uses the fallback
      // once the planner realizes it can subplan and has no distinct plans.
      // Then it replans, and an uncaught replanning exception is thrown.
      
      Show
      assert .commandWorked(coll.insert({a: 0, m: {m1: 0}})); // Make some indexes so the query has multiple solutions and gets cached assert .commandWorked(coll.createIndex({a: 1})); assert .commandWorked(coll.createIndex({a: 1, b: 1, m: "hashed" , "m.m1" : 1})); assert .commandWorked(coll.createIndex({ "$**" : 1}, {wildcardProjection: { "m.m1" : 1}})); assert .commandWorked(coll.createIndex({ "m.m2" : 1, a: 1})); // Distinct-eligible, but won't generate a distinct plan, thus using the fallback to distinctEligible= false const pipeline = [ {$match: {$or: [{a: {$eq: 0}}, { "m.m1" : {$eq: 0}}]}}, {$group: {_id: "$a" }}, {$project: {_id: 0, a: 1}}, ]; // Cache the solution coll.aggregate(pipeline).toArray(); coll.aggregate(pipeline).toArray(); // Add more documents so the query replans const newDocs = []; for (let i = 0; i < 10000; i++) { newDocs.push({a: 0, m: {m1: 0}}); } assert .commandWorked(coll.insert(newDocs)); coll.aggregate(pipeline).toArray(); // This query is eligible for distinct at first, but then uses the fallback // once the planner realizes it can subplan and has no distinct plans. // Then it replans, and an uncaught replanning exception is thrown.
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      When a query is eligible for distinct scan, we try planning and if there are no distinct scan plans, we throw an exception and retry planning with distinctEligible=false. However, when we try planning again, we don't check if another exception is thrown. This planning call could throw any of these exceptions: NoQueryExecutionPlans, RetryMultiPlanning, ReplanningRequired

      Since these exceptions aren't caught, the query fails and the exception is returned to the user. The expected behavior is for the query to retry planning again and eventually succeed.

      The fix is to retry planning with the same try/catch that we use in the first attempt. Notice in this try/catch sequence, the other "catch" blocks do not return. Instead they reset plannerParams and allow the outer loop to retry planning under the same try/catch. Distinct incorrectly retries planning immediately.

            Assignee:
            Unassigned
            Reporter:
            Matt Boros
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: