[SERVER-22895] mongos has inconsistent behavior for replacement-style single-update with empty query Created: 29/Feb/16  Updated: 12/Dec/23

Status: Backlog
Project: Core Server
Component/s: Sharding
Affects Version/s: 3.3.2
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Esha Maharishi (Inactive) Assignee: Backlog - Cluster Scalability
Resolution: Unresolved Votes: 0
Labels: AdiZ
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to SERVER-15551 Add a mongod mode to simulate sharded... Backlog
Assigned Teams:
Cluster Scalability
Operating System: ALL
Steps To Reproduce:

var st = new ShardingTest({shards: 2, mongos: 1, other: { noAutoSplit: ""}}); var dbName = 'test';
var collNS = dbName + '.foo';
var numShardKeys = 3;
 
// Enable sharding.
assert.commandWorked(st.s.adminCommand({ enableSharding: dbName }));
st.ensurePrimaryShard(dbName, st._shardNames[0]);
assert.commandWorked(st.s.adminCommand({ shardCollection: collNS, key: { x: 1 }}));
 
// Insert some documents.
for (var i=0; i<numShardKeys; i++) {
    assert.writeOK(st.s.getCollection(collNS).insert({ x: i }));
}
 
jsTest.log("All documents before update\n" + tojson(st.s.getCollection(collNS).find().toArray()));
 
// updating x:0 will succeed, x:2 will fail, because x:0 is the first document encountered by mongod.
//jsTest.log("Update result\n" + st.s.getCollection(collNS).update({}, { x: 0, fieldToUpdate : 1 }));
jsTest.log("Update result\n" + st.s.getCollection(collNS).update({}, { x: 2, fieldToUpdate : 1 }));
 
jsTest.log("Find() after update returned\n" + tojson(st.s.getCollection(collNS).find().toArray()));

Participants:

 Description   

Mongos allows a replacement-style single-update with an empty query but an update containing the shard key to be executed on the shards.

If the first document the update encountered by mongod contains the shard key, the update will be executed successfully.

Otherwise, the mongod will return the following write error to mongos, which is passed to the client:

[js_test:test] 2016-02-29T11:52:11.314-0500 assert: write failed with error: {
[js_test:test] 2016-02-29T11:52:11.314-0500 	"nMatched" : 0,
[js_test:test] 2016-02-29T11:52:11.314-0500 	"nUpserted" : 0,
[js_test:test] 2016-02-29T11:52:11.315-0500 	"nModified" : 0,
[js_test:test] 2016-02-29T11:52:11.315-0500 	"writeError" : {
[js_test:test] 2016-02-29T11:52:11.315-0500 		"code" : 66,
[js_test:test] 2016-02-29T11:52:11.315-0500 		"errmsg" : "After applying the update to the document {x: 5.0 , ...}, the (immutable) field 'x' was found to have been altered to x: 0.0"
[js_test:test] 2016-02-29T11:52:11.315-0500 	}
[js_test:test] 2016-02-29T11:52:11.315-0500 }
[js_test:test] 2016-02-29T11:52:11.315-0500 _getErrorWithCode@src/mongo/shell/utils.js:23:13
[js_test:test] 2016-02-29T11:52:11.315-0500 doassert@src/mongo/shell/assert.js:13:14
[js_test:test] 2016-02-29T11:52:11.315-0500 assert.writeOK@src/mongo/shell/assert.js:388:9
[js_test:test] 2016-02-29T11:52:11.315-0500 @test.js:31:1

Therefore, the behavior of the update on a mongos depends on the order of documents seen by the mongod.



 Comments   
Comment by Spencer Brody (Inactive) [ 07/Mar/16 ]

I feel like this is probably "works as designed", but we can discuss at our next triage meeting.

Comment by Esha Maharishi (Inactive) [ 29/Feb/16 ]

Since replacement-style updates with an empty query are allowed on a single mongod, we can either:

  • close this as "works as designed." Both a fresh and stale mongos, if the first document they find matches the shard key in the update, will allow the write. Otherwise, both will allow the write initially, but once it sees that the update changed the shard key, will reject the write. Thus, the write being accepted or not will depend on the order of documents (hard to reason about).

or

  • change this to "improvement" and make the mongos reject the update as invalid before even sending it to shards (aka require the query to specify the shard key).
Comment by Esha Maharishi (Inactive) [ 29/Feb/16 ]

I think it may be due to update processing on a mongod itself?

On 2.6, 3.0, 3.2, and master, a replacement-style update with an empty query and upsert=false overwrites the first document it encounters with the replacement doc:

var numDocs = 3;
for (var i = 0; i < numDocs; i++) {
    db.foo.insert({ x: i });
}
 
printjson(db.foo.find().toArray());
 
db.foo.update({}, { x: 100 }, { upsert: false });
 
printjson(db.foo.find().toArray());

gives:

> load('test2.js')
[
	{
		"_id" : ObjectId("56d4c5ef444b5010a48d4675"),
		"x" : 0
	},
	{
		"_id" : ObjectId("56d4c5ef444b5010a48d4676"),
		"x" : 1
	},
	{
		"_id" : ObjectId("56d4c5ef444b5010a48d4677"),
		"x" : 2
	}
]
[
	{
		"_id" : ObjectId("56d4c5ef444b5010a48d4675"),
		"x" : 100
	},
	{
		"_id" : ObjectId("56d4c5ef444b5010a48d4676"),
		"x" : 1
	},
	{
		"_id" : ObjectId("56d4c5ef444b5010a48d4677"),
		"x" : 2
	}
]
true

Comment by Andy Schwerin [ 29/Feb/16 ]

More importantly, it looks like for replacement-type updates the targeter targets based on the replacement document, but then the entity the invokes the targeter looks like it doesn't change its filter to include the shard key – if that even makes sense. Possible regression?

Comment by Andy Schwerin [ 29/Feb/16 ]

Since we target on the replacement document, I think the only error here is that we didn't internally get "stale shard version" on the stale mongos, and refresh the chunk metadata.

Generated at Thu Feb 08 04:01:44 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.