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

Sharding an unsharded collection fails if a stale unique index remains on the DB-primary shard

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 8.2.0-rc0
    • Affects Version/s: 8.1.0-rc0, 8.0.0
    • Component/s: Catalog, Sharding
    • None
    • Catalog and Routing
    • Fully Compatible
    • ALL
    • v8.1, v8.0
    • Hide

      With moveCollection:

      > db.xyzzy.createIndex({a: 1}, {unique: true});
      > db.adminCommand({moveCollection: "test.xyzzy", toShard: "nonPrimary"})
      > db.xyzzy.dropIndex({a: 1})
      > sh.shardCollection("test.xyzzy", {z: 1})
      MongoServerError[InvalidOptions]: can't shard collection 'test.xyzzy' with unique index on { a: 1 } and proposed shard key { z: 1 }. Uniqueness can't be maintained unless shard key is a prefix
      

      With unshardCollection:

      > sh.shardCollection("test.xyzzy", {a: 1})
      > db.xyzzy.createIndex({a: 1, b: 1}, {unique: true})
      > sh.unshardCollection("test.xyzzy", "nonPrimary")
      > db.xyzzy.dropIndex({a: 1, b: 1})
      > sh.shardCollection("test.xyzzy", {z: 1});
      MongoServerError[InvalidOptions]: can't shard collection 'test.xyzzy' with unique index on { a: 1, b: 1 } and proposed shard key { z: 1 }. Uniqueness can't be maintained unless shard key is a prefix
      

      With shardCollection creating the stale index on the DBPrimary (rather than moveCollection):

      > db.xyzzy.insertOne({x: 1})
      > db.adminCommand({moveCollection: "test.xyzzy", toShard: "nonPrimary"})
      > db.xyzzy.createIndex({a: 1}, {unique: true})
      > sh.shardCollection("test.xyzzy", {z: 1})
      > db.xyzzy.dropIndex({a: 1})
      > sh.shardCollection("test.xyzzy", {z: 1})
      MongoServerError[InvalidOptions]: can't shard collection 'test.xyzzy' with unique index on { a: 1 } and proposed shard key { z: 1 }. Uniqueness can't be maintained unless shard key is a prefix
      
      Show
      With moveCollection : > db.xyzzy.createIndex({a: 1}, {unique: true}); > db.adminCommand({moveCollection: "test.xyzzy", toShard: "nonPrimary"}) > db.xyzzy.dropIndex({a: 1}) > sh.shardCollection("test.xyzzy", {z: 1}) MongoServerError[InvalidOptions]: can't shard collection 'test.xyzzy' with unique index on { a: 1 } and proposed shard key { z: 1 }. Uniqueness can't be maintained unless shard key is a prefix With unshardCollection : > sh.shardCollection("test.xyzzy", {a: 1}) > db.xyzzy.createIndex({a: 1, b: 1}, {unique: true}) > sh.unshardCollection("test.xyzzy", "nonPrimary") > db.xyzzy.dropIndex({a: 1, b: 1}) > sh.shardCollection("test.xyzzy", {z: 1}); MongoServerError[InvalidOptions]: can't shard collection 'test.xyzzy' with unique index on { a: 1, b: 1 } and proposed shard key { z: 1 }. Uniqueness can't be maintained unless shard key is a prefix With shardCollection creating the stale index on the DBPrimary (rather than moveCollection ): > db.xyzzy.insertOne({x: 1}) > db.adminCommand({moveCollection: "test.xyzzy", toShard: "nonPrimary"}) > db.xyzzy.createIndex({a: 1}, {unique: true}) > sh.shardCollection("test.xyzzy", {z: 1}) > db.xyzzy.dropIndex({a: 1}) > sh.shardCollection("test.xyzzy", {z: 1}) MongoServerError[InvalidOptions]: can't shard collection 'test.xyzzy' with unique index on { a: 1 } and proposed shard key { z: 1 }. Uniqueness can't be maintained unless shard key is a prefix
    • CAR Team 2025-03-17, CAR Team 2025-03-31
    • 2
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Since MongoDB v8.0, it is possible for an unsharded collection to be placed in any shard via the moveCollection command or the unshardCollection command. Under the hood, both commands create an unsplittable collection which is technically similar to a sharded collection with a single chunk. However, an unsplittable collection can be sharded via shardCollection (unlike an already sharded collection, for which reshardCollection must be used).

      When an unsplittable collection is created either via moveCollection or unshardCollection, all its data is moved to its recipient shard, however, the shards that used to own data for that collection, including the DB-primary shard, still maintain an instance of the collection on their local catalog, but this instance is not updated by operations such as createIndexes or dropIndexes, so its can become stale.

      This is problematic because shardCollection checks that all unique indexes for the collection to be sharded are prefixes of the shard key (as otherwise, the uniqueness constraint can not be enforced). However, currently, shardCollection first adds any additional indexes back to the DB-primary and then it runs the check against the instance of the collection on the DB-primary. However, the index sync process does not drop any surplus indexes that only the DB-primary has. Therefore, it will still find the unique index that the DB-primary used to contain which was then dropped after the collection was moved off of the DB-primary. This will make the shardCollection operation fail spuriously, giving as the reason an index that has already been dropped from the user point of view (see "Steps to Reproduce").

      shardCollection should drop any surplus indexes on the DB-primary shard (as part of the SyncIndexesOnCoordinator phase) to ensure stale indexes can not block sharding.

      As a workaround the collection can be moved back to the DB-primary shard before sharding it.

            Assignee:
            joan.bruguera-mico@mongodb.com Joan Bruguera Micó
            Reporter:
            joan.bruguera-mico@mongodb.com Joan Bruguera Micó
            Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

              Created:
              Updated:
              Resolved:
              None
              None
              None
              None