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

Failure to update/delete orphan document fails multi write without shard key in transaction

    XMLWordPrintableJSON

Details

    • Sharding NYC
    • ALL
    • Hide

      (function() {
          "use strict";
       
          const dbName = "test";
          const collName = "foo";
          const ns = dbName + "." + collName;
       
          const st = new ShardingTest({shards: 2, config: 1});
       
          // Shard a collection with chunks [minKey, 0), [0, maxKey) on shard0 and shard1, respectively.
       
          assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
          st.ensurePrimaryShard(dbName, st.shard0.shardName);
       
          assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {skey: 1}}));
          assert.commandWorked(st.s.adminCommand({split: ns, middle: {skey: 0}}));
          assert.commandWorked(
              st.s.adminCommand({moveChunk: ns, find: {skey: 5}, to: st.shard1.shardName}));
       
          // Create a unique compound index prefixed on the shard key and insert two orphans with the same
          // shard key but different values for the second field in the index.
       
          const mongosColl = st.s.getDB(dbName)[collName];
          assert.commandWorked(mongosColl.createIndex({skey: 1, uniqueField: 1}, {unique: true}));
       
          assert.writeOK(mongosColl.insert({_id: 1, skey: -5, uniqueField: 0}));
       
          const orphanColl = st.rs1.getPrimary().getDB(dbName)[collName];
          assert.writeOK(orphanColl.insert({_id: 1, skey: -5, uniqueField: 0}));
          assert.writeOK(orphanColl.insert({_id: 2, skey: -5, uniqueField: 1}));
       
          // Start a transaction and send an update without the shard key that will be broadcast to all
          // shards. The write will fail against an orphan because it would violate the unique index,
          // which will abort the entire transaction. Outside a transaction, this write would succeed with
          // a writeError.
       
          const session = st.s.startSession();
          session.startTransaction();
          assert.commandFailedWithCode(session.getDatabase(dbName).runCommand({
              update: collName,
              updates: [{q: {uniqueField: 0}, u: {$set: {uniqueField: 1}}, multi: true}]
          }),
                                       ErrorCodes.DuplicateKey);
       
          // The non-orphan was not updated.
          assert.sameMembers(mongosColl.find().toArray(), [{_id: 1, skey: -5, uniqueField: 0}]);
       
          // Repeat the previous update outside a transaction and the command instead updates the
          // non-orphan with a writeError.
       
          assert.commandWorkedIgnoringWriteErrors(mongosColl.getDB().runCommand({
              update: collName,
              updates: [{q: {uniqueField: 0}, u: {$set: {uniqueField: 1}}, multi: true}]
          }));
       
          // The non-orphan was updated.
          assert.sameMembers(mongosColl.find().toArray(), [{_id: 1, skey: -5, uniqueField: 1}]);
       
          st.stop();
      })();
      

      Show
      (function() { "use strict";   const dbName = "test"; const collName = "foo"; const ns = dbName + "." + collName;   const st = new ShardingTest({shards: 2, config: 1});   // Shard a collection with chunks [minKey, 0), [0, maxKey) on shard0 and shard1, respectively.   assert.commandWorked(st.s.adminCommand({enableSharding: dbName})); st.ensurePrimaryShard(dbName, st.shard0.shardName);   assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {skey: 1}})); assert.commandWorked(st.s.adminCommand({split: ns, middle: {skey: 0}})); assert.commandWorked( st.s.adminCommand({moveChunk: ns, find: {skey: 5}, to: st.shard1.shardName}));   // Create a unique compound index prefixed on the shard key and insert two orphans with the same // shard key but different values for the second field in the index.   const mongosColl = st.s.getDB(dbName)[collName]; assert.commandWorked(mongosColl.createIndex({skey: 1, uniqueField: 1}, {unique: true}));   assert.writeOK(mongosColl.insert({_id: 1, skey: -5, uniqueField: 0}));   const orphanColl = st.rs1.getPrimary().getDB(dbName)[collName]; assert.writeOK(orphanColl.insert({_id: 1, skey: -5, uniqueField: 0})); assert.writeOK(orphanColl.insert({_id: 2, skey: -5, uniqueField: 1}));   // Start a transaction and send an update without the shard key that will be broadcast to all // shards. The write will fail against an orphan because it would violate the unique index, // which will abort the entire transaction. Outside a transaction, this write would succeed with // a writeError.   const session = st.s.startSession(); session.startTransaction(); assert.commandFailedWithCode(session.getDatabase(dbName).runCommand({ update: collName, updates: [{q: {uniqueField: 0}, u: {$set: {uniqueField: 1}}, multi: true}] }), ErrorCodes.DuplicateKey);   // The non-orphan was not updated. assert.sameMembers(mongosColl.find().toArray(), [{_id: 1, skey: -5, uniqueField: 0}]);   // Repeat the previous update outside a transaction and the command instead updates the // non-orphan with a writeError.   assert.commandWorkedIgnoringWriteErrors(mongosColl.getDB().runCommand({ update: collName, updates: [{q: {uniqueField: 0}, u: {$set: {uniqueField: 1}}, multi: true}] }));   // The non-orphan was updated. assert.sameMembers(mongosColl.find().toArray(), [{_id: 1, skey: -5, uniqueField: 1}]);   st.stop(); })();

    Description

      Writes that target more than one shard (including multi=false writes with exact _id queries) are sent with shard versions in transactions, instead of being sent unversioned like they are outside of a transaction. Update and delete do not have a shard filter stage, so if an orphan document matches the write's query but cannot be updated or deleted (e.g. would violate a unique index), the entire operation will fail, whereas outside a transaction the response will contain a writeError, but the non-orphan documents will be successfully modified.

      Attachments

        Activity

          People

            backlog-server-sharding-nyc Backlog - Sharding NYC
            jack.mulrow@mongodb.com Jack Mulrow
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated: