Skip to content

Commit 36a8f0d

Browse files
committed
CSHARP-1437: FindAndModify should use WriteConcern from collection settings.
1 parent 81144c3 commit 36a8f0d

File tree

4 files changed

+223
-42
lines changed

4 files changed

+223
-42
lines changed

MongoDB.Driver/Communication/FeatureDetection/FeatureSetDetector.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ internal class FeatureSetDetector
5656

5757
new FeatureSetDependency(
5858
new ServerVersionDependency(2, 7, 5),
59-
FeatureId.ScramSha1)
59+
FeatureId.ScramSha1),
60+
61+
// added in 3.2.0
62+
new FeatureSetDependency(
63+
new ServerVersionDependency(3, 1, 9999),
64+
FeatureId.FindAndModifyWriteConcern)
6065
};
6166

6267
// public methods

MongoDB.Driver/FeatureId.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ public enum FeatureId
4141
/// </summary>
4242
CreateIndexCommand,
4343
/// <summary>
44+
/// The find and modify write concern feature.
45+
/// </summary>
46+
FindAndModifyWriteConcern,
47+
/// <summary>
4448
/// The max time feature.
4549
/// </summary>
4650
MaxTime,

MongoDB.Driver/MongoCollection.cs

Lines changed: 85 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -635,36 +635,58 @@ public virtual FindAndModifyResult FindAndModify(FindAndModifyArgs args)
635635
if (args == null) { throw new ArgumentNullException("args"); }
636636
if (args.Update == null) { throw new ArgumentException("Update is null.", "args"); }
637637

638-
var command = new CommandDocument
639-
{
640-
{ "findAndModify", _name },
641-
{ "query", () => BsonDocumentWrapper.Create(args.Query), args.Query != null }, // optional
642-
{ "sort", () => BsonDocumentWrapper.Create(args.SortBy), args.SortBy != null }, // optional
643-
{ "update", BsonDocumentWrapper.Create(args.Update, true) }, // isUpdateDocument = true
644-
{ "new", () => args.VersionReturned.Value == FindAndModifyDocumentVersion.Modified, args.VersionReturned.HasValue }, // optional
645-
{ "fields", () => BsonDocumentWrapper.Create(args.Fields), args.Fields != null }, // optional
646-
{ "upsert", true, args.Upsert}, // optional
647-
{ "maxTimeMS", () => args.MaxTime.Value.TotalMilliseconds, args.MaxTime.HasValue } // optional
648-
};
649-
try
638+
using (var request = _server.RequestStart(null))
650639
{
651-
return RunCommandAs<FindAndModifyResult>(command);
652-
}
653-
catch (MongoCommandException ex)
654-
{
655-
if (ex.ErrorMessage == "No matching object found")
640+
var serverInstance = _server.RequestConnection.ServerInstance;
641+
642+
var writeConcern = _settings.WriteConcern.ToBsonDocument();
643+
if (writeConcern.ElementCount == 0)
644+
{
645+
writeConcern = null;
646+
}
647+
648+
var command = new CommandDocument
649+
{
650+
{ "findAndModify", _name },
651+
{ "query", () => BsonDocumentWrapper.Create(args.Query), args.Query != null }, // optional
652+
{ "sort", () => BsonDocumentWrapper.Create(args.SortBy), args.SortBy != null }, // optional
653+
{ "update", BsonDocumentWrapper.Create(args.Update, true) }, // isUpdateDocument = true
654+
{ "new", () => args.VersionReturned.Value == FindAndModifyDocumentVersion.Modified, args.VersionReturned.HasValue }, // optional
655+
{ "fields", () => BsonDocumentWrapper.Create(args.Fields), args.Fields != null }, // optional
656+
{ "upsert", true, args.Upsert}, // optional
657+
{ "maxTimeMS", () => args.MaxTime.Value.TotalMilliseconds, args.MaxTime.HasValue }, // optional
658+
{ "writeConcern", writeConcern, writeConcern != null && serverInstance.Supports(FeatureId.FindAndModifyWriteConcern) }
659+
};
660+
try
656661
{
657-
// create a new command result with what the server should have responded
658-
var response = new BsonDocument
662+
var result = RunCommandAs<FindAndModifyResult>(command);
663+
664+
BsonValue writeConcernError;
665+
if (result.Response.TryGetValue("writeConcernError", out writeConcernError))
666+
{
667+
var message = writeConcernError["errmsg"].AsString;
668+
var writeConcernResult = new WriteConcernResult(result.Response);
669+
throw new MongoWriteConcernException(message, writeConcernResult);
670+
}
671+
672+
return result;
673+
}
674+
catch (MongoCommandException ex)
675+
{
676+
if (ex.ErrorMessage == "No matching object found")
677+
{
678+
// create a new command result with what the server should have responded
679+
var response = new BsonDocument
659680
{
660681
{ "value", BsonNull.Value },
661682
{ "ok", true }
662683
};
663684
#pragma warning disable 618
664-
return new FindAndModifyResult(response) { Command = command };
685+
return new FindAndModifyResult(response) { Command = command };
665686
#pragma warning restore
687+
}
688+
throw;
666689
}
667-
throw;
668690
}
669691
}
670692

@@ -689,35 +711,57 @@ public virtual FindAndModifyResult FindAndRemove(IMongoQuery query, IMongoSortBy
689711
public virtual FindAndModifyResult FindAndRemove(FindAndRemoveArgs args)
690712
{
691713
if (args == null) { throw new ArgumentNullException("args"); }
692-
693-
var command = new CommandDocument
694-
{
695-
{ "findAndModify", _name },
696-
{ "query", () => BsonDocumentWrapper.Create(args.Query), args.Query != null }, // optional
697-
{ "sort", () => BsonDocumentWrapper.Create(args.SortBy), args.SortBy != null }, // optional
698-
{ "remove", true },
699-
{ "fields", () => BsonDocumentWrapper.Create(args.Fields), args.Fields != null }, // optional
700-
{ "maxTimeMS", () => args.MaxTime.Value.TotalMilliseconds, args.MaxTime.HasValue } // optional
701-
};
702-
try
703-
{
704-
return RunCommandAs<FindAndModifyResult>(command);
705-
}
706-
catch (MongoCommandException ex)
714+
715+
using (var request = _server.RequestStart(null))
707716
{
708-
if (ex.ErrorMessage == "No matching object found")
717+
var serverInstance = _server.RequestConnection.ServerInstance;
718+
719+
var writeConcern = _settings.WriteConcern.ToBsonDocument();
720+
if (writeConcern.ElementCount == 0)
709721
{
710-
// create a new command result with what the server should have responded
711-
var response = new BsonDocument
722+
writeConcern = null;
723+
}
724+
725+
var command = new CommandDocument
726+
{
727+
{ "findAndModify", _name },
728+
{ "query", () => BsonDocumentWrapper.Create(args.Query), args.Query != null }, // optional
729+
{ "sort", () => BsonDocumentWrapper.Create(args.SortBy), args.SortBy != null }, // optional
730+
{ "remove", true },
731+
{ "fields", () => BsonDocumentWrapper.Create(args.Fields), args.Fields != null }, // optional
732+
{ "maxTimeMS", () => args.MaxTime.Value.TotalMilliseconds, args.MaxTime.HasValue }, // optional
733+
{ "writeConcern", writeConcern, writeConcern != null && serverInstance.Supports(FeatureId.FindAndModifyWriteConcern) }
734+
};
735+
try
736+
{
737+
var result = RunCommandAs<FindAndModifyResult>(command);
738+
739+
BsonValue writeConcernError;
740+
if (result.Response.TryGetValue("writeConcernError", out writeConcernError))
741+
{
742+
var message = writeConcernError["errmsg"].AsString;
743+
var writeConcernResult = new WriteConcernResult(result.Response);
744+
throw new MongoWriteConcernException(message, writeConcernResult);
745+
}
746+
747+
return result;
748+
}
749+
catch (MongoCommandException ex)
750+
{
751+
if (ex.ErrorMessage == "No matching object found")
752+
{
753+
// create a new command result with what the server should have responded
754+
var response = new BsonDocument
712755
{
713756
{ "value", BsonNull.Value },
714757
{ "ok", true }
715758
};
716759
#pragma warning disable 618
717-
return new FindAndModifyResult(response) { Command = command };
760+
return new FindAndModifyResult(response) { Command = command };
718761
#pragma warning restore
762+
}
763+
throw;
719764
}
720-
throw;
721765
}
722766
}
723767

MongoDB.DriverUnitTests/MongoCollectionTests.cs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,56 @@ public void TestFindAndModifyUpsert()
979979
Assert.AreEqual(1, result.ModifiedDocument["count"].AsInt32);
980980
}
981981

982+
[Test]
983+
[RequiresServer(ServerTypes = ServerTypes.ReplicaSetMember)]
984+
public void TestFindAndModifyReplaceWithWriteConcernError()
985+
{
986+
_collection.RemoveAll();
987+
_collection.Insert(new BsonDocument { { "_id", 1 }, { "x", 1 } });
988+
var collectionSettings = new MongoCollectionSettings
989+
{
990+
WriteConcern = new WriteConcern { W = 9 }
991+
};
992+
var collection = _database.GetCollection(_collection.Name, collectionSettings);
993+
var args = new FindAndModifyArgs
994+
{
995+
Query = Query.EQ("_id", 1),
996+
Update = Update.Replace(new BsonDocument { { "_id", 1 }, { "x", 2 } }),
997+
VersionReturned = FindAndModifyDocumentVersion.Modified
998+
};
999+
1000+
var exception = Assert.Throws<MongoWriteConcernException>(() => collection.FindAndModify(args));
1001+
1002+
var commandResult = exception.Result;
1003+
var result = commandResult["value"].AsBsonDocument;
1004+
Assert.That(result, Is.EqualTo(BsonDocument.Parse("{ _id : 1, x : 2 }")));
1005+
}
1006+
1007+
[Test]
1008+
[RequiresServer(ServerTypes = ServerTypes.ReplicaSetMember)]
1009+
public void TestFindAndModifyUpdateWithWriteConcernError()
1010+
{
1011+
_collection.RemoveAll();
1012+
_collection.Insert(new BsonDocument { { "_id", 1 }, { "x", 1 } });
1013+
var collectionSettings = new MongoCollectionSettings
1014+
{
1015+
WriteConcern = new WriteConcern { W = 9 }
1016+
};
1017+
var collection = _database.GetCollection(_collection.Name, collectionSettings);
1018+
var args = new FindAndModifyArgs
1019+
{
1020+
Query = Query.EQ("x", 1),
1021+
Update = Update.Set("x", 2),
1022+
VersionReturned = FindAndModifyDocumentVersion.Modified
1023+
};
1024+
1025+
var exception = Assert.Throws<MongoWriteConcernException>(() => collection.FindAndModify(args));
1026+
1027+
var commandResult = exception.Result;
1028+
var result = commandResult["value"].AsBsonDocument;
1029+
Assert.That(result, Is.EqualTo(BsonDocument.Parse("{ _id : 1, x : 2 }")));
1030+
}
1031+
9821032
private class FindAndModifyClass
9831033
{
9841034
public ObjectId Id;
@@ -1075,6 +1125,30 @@ public void TestFindAndRemoveWithMaxTime()
10751125
}
10761126
}
10771127

1128+
[Test]
1129+
[RequiresServer(ServerTypes = ServerTypes.ReplicaSetMember)]
1130+
public void TestFindAndRemoveWithWriteConcernError()
1131+
{
1132+
_collection.RemoveAll();
1133+
_collection.Insert(new BsonDocument("x", 1));
1134+
var collectionSettings = new MongoCollectionSettings
1135+
{
1136+
WriteConcern = new WriteConcern { W = 9 }
1137+
};
1138+
var collection = _database.GetCollection(_collection.Name, collectionSettings);
1139+
var args = new FindAndRemoveArgs
1140+
{
1141+
Query = Query.EQ("x", 1)
1142+
};
1143+
1144+
var exception = Assert.Throws<MongoWriteConcernException>(() => collection.FindAndRemove(args));
1145+
1146+
var commandResult = exception.Result;
1147+
var result = commandResult["value"].AsBsonDocument;
1148+
Assert.That(result["x"].ToInt32(), Is.EqualTo(1));
1149+
Assert.That(_collection.Count(), Is.EqualTo(0L));
1150+
}
1151+
10781152
[Test]
10791153
public void TestFindNearSphericalFalse()
10801154
{
@@ -2320,6 +2394,23 @@ public void TestInsertDuplicateKey()
23202394
CheckExpectedResult(expectedResult, result);
23212395
}
23222396

2397+
[Test]
2398+
[RequiresServer(ServerTypes = ServerTypes.ReplicaSetMember)]
2399+
public void TestInsertWithWriteConcernError()
2400+
{
2401+
_collection.RemoveAll();
2402+
var document = new BsonDocument { { "_id", 1 }, { "x", 1 } };
2403+
var collectionSettings = new MongoCollectionSettings
2404+
{
2405+
WriteConcern = new WriteConcern { W = 9 }
2406+
};
2407+
var collection = _database.GetCollection(_collection.Name, collectionSettings);
2408+
2409+
Assert.Throws<MongoWriteConcernException>(() => collection.Insert(document));
2410+
2411+
Assert.That(_collection.FindOne(), Is.EqualTo(document));
2412+
}
2413+
23232414
[Test]
23242415
public void TestIsCappedFalse()
23252416
{
@@ -2774,6 +2865,24 @@ public void TestRemoveUnacknowledeged()
27742865
}
27752866
}
27762867

2868+
[Test]
2869+
[RequiresServer(ServerTypes = ServerTypes.ReplicaSetMember)]
2870+
public void TestRemoveWithWriteConcernError()
2871+
{
2872+
_collection.RemoveAll();
2873+
_collection.Insert(new BsonDocument { { "_id", 1 }, { "x", 1 } });
2874+
var collectionSettings = new MongoCollectionSettings
2875+
{
2876+
WriteConcern = new WriteConcern { W = 9 }
2877+
};
2878+
var collection = _database.GetCollection(_collection.Name, collectionSettings);
2879+
var query = Query.EQ("x", 1);
2880+
2881+
Assert.Throws<MongoWriteConcernException>(() => collection.Remove(query));
2882+
2883+
Assert.That(_collection.Count(), Is.EqualTo(0L));
2884+
}
2885+
27772886
[Test]
27782887
public void TestSetFields()
27792888
{
@@ -3032,6 +3141,25 @@ public void TestUpdateUnacknowledged()
30323141
}
30333142
}
30343143

3144+
[Test]
3145+
[RequiresServer(ServerTypes = ServerTypes.ReplicaSetMember)]
3146+
public void TestUpdateWithWriteConcernError()
3147+
{
3148+
_collection.RemoveAll();
3149+
_collection.Insert(new BsonDocument { { "_id", 1 }, { "x", 1 } });
3150+
var collectionSettings = new MongoCollectionSettings
3151+
{
3152+
WriteConcern = new WriteConcern { W = 9 }
3153+
};
3154+
var collection = _database.GetCollection(_collection.Name, collectionSettings);
3155+
var query = Query.EQ("x", 1);
3156+
var update = Update.Set("x", 2);
3157+
3158+
Assert.Throws<MongoWriteConcernException>(() => collection.Update(query, update));
3159+
3160+
Assert.That(_collection.FindOne(), Is.EqualTo(BsonDocument.Parse("{ _id : 1, x : 2 }")));
3161+
}
3162+
30353163
[Test]
30363164
public void TestUpsertExisting()
30373165
{

0 commit comments

Comments
 (0)