Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-57067

[SBE] Find command unexpectedly returns cursorId when final batch size aligns with result set size

    • ALL
    • Hide

      Run the server with SBE enabled (the default in 5.0.0-rc0). Then:

      (function() {
          "use strict";
          const collname = "cursor_test";
          const coll = db.getCollection(collname);
          coll.drop();
      
          assert.writeOK(coll.insert({_id: 0}));
          assert.writeOK(coll.insert({_id: 1}));
      
          const find_response = coll.runCommand("find", {batchSize: 1});
          const cursor_id = find_response.cursor.id;                                                                                                                                                                                                         
          assert.eq(find_response.cursor.firstBatch.length, 1); 
          assert.neq(cursor_id, 0); 
      
          const getmore_response = coll.runCommand({getMore: cursor_id, collection: collname, batchSize: 1});
          assert.eq(getmore_response.cursor.nextBatch.length, 1); 
          assert.eq(getmore_response.cursor.id, 0, "unexpected nonzero cursor id");
      }());
      
      Show
      Run the server with SBE enabled (the default in 5.0.0-rc0). Then: ( function () { "use strict" ; const collname = "cursor_test" ; const coll = db.getCollection(collname); coll.drop(); assert.writeOK(coll.insert({_id: 0})); assert.writeOK(coll.insert({_id: 1})); const find_response = coll.runCommand( "find" , {batchSize: 1}); const cursor_id = find_response.cursor.id; assert.eq(find_response.cursor.firstBatch.length, 1); assert.neq(cursor_id, 0); const getmore_response = coll.runCommand({getMore: cursor_id, collection: collname, batchSize: 1}); assert.eq(getmore_response.cursor.nextBatch.length, 1); assert.eq(getmore_response.cursor.id, 0, "unexpected nonzero cursor id" ); }());
    • Query Execution 2021-05-31

      In the slot-based execution engine, when running a find or getMore command with a batchSize that exactly matches the number of documents remaining to return, the command response will return a non-zero cursor id:

      > db.twodocs.drop()                                                         
      true                            
      > db.twodocs.insertMany([{_id: 0}, { _id: 1}])
      { "acknowledged" : true, "insertedIds" : [ 0, 1 ] }
      > db.runCommand({find: "twodocs", batchSize: 1})
      {
              "cursor" : {
                      "firstBatch" : [
                              {
                                      "_id" : 0
                              }
                      ],
                      "id" : NumberLong("5005687836528864628"),
                      "ns" : "test.twodocs"
              },
              "ok" : 1
      }
      > db.runCommand({getMore: NumberLong("5005687836528864628"), collection: "twodocs", batchSize: 1})
      {
              "cursor" : {
                      "nextBatch" : [
                              {
                                      "_id" : 1
                              }
                      ],
                      "id" : NumberLong("5005687836528864628"),
                      "ns" : "test.twodocs"
              },
              "ok" : 1
      }
      
      

      Iterating the cursor once more reveals that it is in fact exhausted. This is a regression from the classic engine, as the classic engine would have reported a cursor id of 0 to obviate the need for the final getMore where no results are returned.

      > db.runCommand({getMore: NumberLong("5005687836528864628"), collection: "twodocs", batchSize: 1})
      {
              "cursor" : {
                      "nextBatch" : [ ],
                      "id" : NumberLong(0),
                      "ns" : "test.twodocs"
              },
              "ok" : 1
      }
      

      The problem also manifests itself with just the find command and an exact batch size matching the result set:

      > db.runCommand({find: "twodocs", batchSize: 2})
      {
              "cursor" : {
                      "firstBatch" : [
                              {
                                      "_id" : 0
                              },
                              {
                                      "_id" : 1
                              }
                      ],
                      "id" : NumberLong("9219549160796429098"),
                      "ns" : "test.twodocs"
              },
              "ok" : 1
      }
      > db.runCommand({getMore: NumberLong("9219549160796429098"), collection: "twodocs"})
      {
              "cursor" : {
                      "nextBatch" : [ ],
                      "id" : NumberLong(0),
                      "ns" : "test.twodocs"
              },
              "ok" : 1
      }
      

      Note that if the final getMore does not include a batch size, the problem doesn't manifest itself:

      > db.runCommand({find: "twodocs", batchSize: 1})
      {
              "cursor" : {
                      "firstBatch" : [
                              {
                                      "_id" : 0
                              }
                      ],
                      "id" : NumberLong("1331269379857468674"),
                      "ns" : "test.twodocs"
              },
              "ok" : 1
      }
      > db.runCommand({getMore: NumberLong("1331269379857468674"), collection: "twodocs"})
      {
              "cursor" : {
                      "nextBatch" : [
                              {
                                      "_id" : 1
                              }
                      ],
                      "id" : NumberLong(0),
                      "ns" : "test.twodocs"
              },
              "ok" : 1
      }
      

            Assignee:
            nikita.lapkov@mongodb.com Nikita Lapkov (Inactive)
            Reporter:
            kyle.suarez@mongodb.com Kyle Suarez
            Votes:
            0 Vote for this issue
            Watchers:
            9 Start watching this issue

              Created:
              Updated:
              Resolved: