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

Session migration from moveChunk can lead to higher 'n' and 'nModified' for retryable updates by _id

    • Type: Icon: Bug Bug
    • Resolution: Unresolved
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: None
    • Component/s: Sharding
    • Cluster Scalability
    • ALL
    • v8.0
    • Hide
      python buildscripts/resmoke.py run --suite=sharding repro_retryable_update_by_id_nmodified.js
      
      repro_retryable_update_by_id_nmodified.js
      (function() {
      "use strict";
      
      load("jstests/sharding/libs/create_sharded_collection_util.js");
      
      const st = new ShardingTest({mongos: 1, config: 1, shards: 2, rs: {nodes: 1}});
      
      const db = st.s.getDB("test");
      const collection = db.getCollection("mycoll");
      CreateShardedCollectionUtil.shardCollectionWithChunks(collection, {x: 1}, [
          {min: {x: MinKey}, max: {x: 0}, shard: st.shard0.shardName},
          {min: {x: 0}, max: {x: 10}, shard: st.shard0.shardName},
          {min: {x: 10}, max: {x: 20}, shard: st.shard1.shardName},
          {min: {x: 20}, max: {x: MaxKey}, shard: st.shard1.shardName},
      ]);
      
      assert.commandWorked(collection.insert({_id: 0, x: 5, counter: 0}));
      
      const sessionCollection = st.s.startSession({causalConsistency: false, retryWrites: false})
                                    .getDatabase(db.getName())
                                    .getCollection(collection.getName());
      
      // Updates by _id are broadcasted to all shards which own chunks for the collection. After the
      // session information is migrated to shard1 from the moveChunk command, both shard0 and shard1
      // will report {n: 1, nModified: 1} for stmtId=0.
      const updateCmd = {
          updates: [
              {q: {_id: 0}, u: {$inc: {counter: 1}}},
              {q: {_id: 10000}, u: {$inc: {counter: 1}}},
          ],
          txnNumber: NumberLong(0),
      };
      
      const firstRes = sessionCollection.runCommand("update", updateCmd);
      assert.eq({n: firstRes.n, nModified: firstRes.nModified}, {n: 1, nModified: 1});
      
      assert.commandWorked(
          db.adminCommand({moveChunk: collection.getFullName(), find: {x: 5}, to: st.shard1.shardName}));
      
      const secondRes = sessionCollection.runCommand("update", updateCmd);
      assert.eq({n: secondRes.n, nModified: secondRes.nModified}, {n: 1, nModified: 1});
      
      st.stop();
      })();
      
      Show
      python buildscripts/resmoke.py run --suite=sharding repro_retryable_update_by_id_nmodified.js repro_retryable_update_by_id_nmodified.js ( function () { "use strict" ; load( "jstests/sharding/libs/create_sharded_collection_util.js" ); const st = new ShardingTest({mongos: 1, config: 1, shards: 2, rs: {nodes: 1}}); const db = st.s.getDB( "test" ); const collection = db.getCollection( "mycoll" ); CreateShardedCollectionUtil.shardCollectionWithChunks(collection, {x: 1}, [ {min: {x: MinKey}, max: {x: 0}, shard: st.shard0.shardName}, {min: {x: 0}, max: {x: 10}, shard: st.shard0.shardName}, {min: {x: 10}, max: {x: 20}, shard: st.shard1.shardName}, {min: {x: 20}, max: {x: MaxKey}, shard: st.shard1.shardName}, ]); assert.commandWorked(collection.insert({_id: 0, x: 5, counter: 0})); const sessionCollection = st.s.startSession({causalConsistency: false , retryWrites: false }) .getDatabase(db.getName()) .getCollection(collection.getName()); // Updates by _id are broadcasted to all shards which own chunks for the collection. After the // session information is migrated to shard1 from the moveChunk command, both shard0 and shard1 // will report {n: 1, nModified: 1} for stmtId=0. const updateCmd = { updates: [ {q: {_id: 0}, u: {$inc: {counter: 1}}}, {q: {_id: 10000}, u: {$inc: {counter: 1}}}, ], txnNumber: NumberLong(0), }; const firstRes = sessionCollection.runCommand( "update" , updateCmd); assert.eq({n: firstRes.n, nModified: firstRes.nModified}, {n: 1, nModified: 1}); assert.commandWorked( db.adminCommand({moveChunk: collection.getFullName(), find: {x: 5}, to: st.shard1.shardName})); const secondRes = sessionCollection.runCommand( "update" , updateCmd); assert.eq({n: secondRes.n, nModified: secondRes.nModified}, {n: 1, nModified: 1}); st.stop(); })();

      Mongos combining the write results from multiple shards can lead to ambiguity due to how shards may report 'n' and 'nModified' for the same statements.

      [js_test:repro_retryable_update_by_id_nmodified] 2021-01-24T15:46:37.631+0000 s20023| {"t":{"$date":"2021-01-24T15:46:37.631+00:00"},"s":"I",  "c":"COMMAND",  "id":51803,   "ctx":"conn6","msg":"Slow query","attr":{"type":"command","ns":"test.mycoll","appName":"MongoDB Shell","command":{"update":"mycoll","txnNumber":0,"lsid":{"id":{"$uuid":"5bb5fa37-09a3-4036-9442-83fa45776750"}},"$clusterTime":{"clusterTime":{"$timestamp":{"t":1611503197,"i":122}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"0"}},"keyId":0}},"$db":"test"},"nShards":2,"nMatched":2,"nModified":2,"numYields":0,"reslen":185,"remote":"127.0.0.1:53194","protocol":"op_msg","durationMillis":2}}
      [js_test:repro_retryable_update_by_id_nmodified] 2021-01-24T15:46:37.632+0000 uncaught exception: Error: [{ "n" : 2, "nModified" : 2 }] != [{ "n" : 1, "nModified" : 1 }] are not equal :
      

            Assignee:
            Unassigned Unassigned
            Reporter:
            max.hirschhorn@mongodb.com Max Hirschhorn
            Votes:
            1 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated: