在 InsertOneAsync() 之后立即替换 OneAsync() 并不总是有效,即使在记录日志时也是如此
ReplaceOneAsync() immediately after InsertOneAsync() not always working, even when journaled
在单实例 MongoDB 服务器上,即使将客户端上的写入问题设置为已记录,每几千个文档中就有一个在插入后无法立即替换。
我的印象是,一旦记录下来,文档就可以立即供查询。
下面的代码插入一个文档,然后更新文档的 DateModified
属性 并尝试根据文档的 Id 和 属性 的旧值更新文档.
public class MyDocument
{
public BsonObjectId Id { get; set; }
public DateTime DateModified { get; set; }
}
static void Main(string[] args)
{
var r = Task.Run(MainAsync);
Console.WriteLine("Inserting documents... Press any key to exit.");
Console.ReadKey(intercept: true);
}
private static async Task MainAsync()
{
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("updateInsertedDocuments");
var concern = new WriteConcern(journal: true);
var collection = database.GetCollection<MyDocument>("docs").WithWriteConcern(concern);
int errorCount = 0;
int totalCount = 0;
do
{
totalCount++;
// Create and insert the document
var document = new MyDocument
{
DateModified = DateTime.Now,
};
await collection.InsertOneAsync(document);
// Save and update the modified date
var oldDateModified = document.DateModified;
document.DateModified = DateTime.Now;
// Try to update the document by Id and the earlier DateModified
var result = await collection.ReplaceOneAsync(d => d.Id == document.Id && d.DateModified == oldDateModified, document);
if (result.ModifiedCount == 0)
{
Console.WriteLine($"Error {++errorCount}/{totalCount}: doc {document.Id} did not have DateModified {oldDateModified.ToString("yyyy-MM-dd HH:mm:ss.ffffff")}");
await DoesItExist(collection, document, oldDateModified);
}
}
while (true);
}
代码以每秒大约 250 个文档的速度插入。对 ReplaceOneAsync(d => d.Id == document.Id && d.DateModified == oldDateModified, ...)
的大约 1,000-15,000 次调用中有一次失败,因为它 returns 的 ModifiedCount
为 0。失败率取决于我们 运行 是调试构建还是发布构建以及使用是否附加调试器:更快的速度意味着更多的错误。
显示的代码代表了一些我无法轻易更改的内容。当然,我更愿意执行一系列 Update.Set()
调用,但现在这不是一个真正的选择。 InsertOneAsync()
后跟 ReplaceOneAsync()
是由某种通过引用更新实体的存储库模式抽象的。这些方法的非异步对应物显示相同的行为。
插入和替换之间的简单 Thread.Sleep(100)
可以缓解该问题。
当查询失败时,我们稍等片刻,然后尝试在下面的代码中再次查询文档,每次都会找到。
private static async Task DoesItExist(IMongoCollection<MyDocument> collection, MyDocument document, DateTime oldDateModified)
{
Thread.Sleep(500);
var fromDatabaseCursor = await collection.FindAsync(d => d.Id == document.Id && d.DateModified == oldDateModified);
var fromDatabaseDoc = await fromDatabaseCursor.FirstOrDefaultAsync();
if (fromDatabaseDoc != null)
{
Console.WriteLine("But it was found!");
}
else
{
Console.WriteLine("And wasn't found!");
}
}
发生这种情况的版本:
- MongoDB 社区服务器 3.4.0、3.4.1、3.4.3、3.4.4 和 3.4.10,全部在 WiredTiger 存储引擎上
- 服务器 运行 Windows,其他操作系统
- C# Mongo 驱动程序 2.3.0 和 2.4.4
这是 MongoDB 中的一个问题,还是我们做(或假设)出了什么问题?
或者,实际的最终目标是,我如何确保更新可以立即检索到插入内容?
ReplaceOneAsync returns 0 如果新文档与旧文档相同(因为没有任何变化)。
在我看来,如果您的测试执行得足够快,对 DateTime.Now 的各种调用可能会 return 相同的值,因此您可能将完全相同的文档传递给 InsertOneAsync 和ReplaceOneAsync.
在单实例 MongoDB 服务器上,即使将客户端上的写入问题设置为已记录,每几千个文档中就有一个在插入后无法立即替换。
我的印象是,一旦记录下来,文档就可以立即供查询。
下面的代码插入一个文档,然后更新文档的 DateModified
属性 并尝试根据文档的 Id 和 属性 的旧值更新文档.
public class MyDocument
{
public BsonObjectId Id { get; set; }
public DateTime DateModified { get; set; }
}
static void Main(string[] args)
{
var r = Task.Run(MainAsync);
Console.WriteLine("Inserting documents... Press any key to exit.");
Console.ReadKey(intercept: true);
}
private static async Task MainAsync()
{
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("updateInsertedDocuments");
var concern = new WriteConcern(journal: true);
var collection = database.GetCollection<MyDocument>("docs").WithWriteConcern(concern);
int errorCount = 0;
int totalCount = 0;
do
{
totalCount++;
// Create and insert the document
var document = new MyDocument
{
DateModified = DateTime.Now,
};
await collection.InsertOneAsync(document);
// Save and update the modified date
var oldDateModified = document.DateModified;
document.DateModified = DateTime.Now;
// Try to update the document by Id and the earlier DateModified
var result = await collection.ReplaceOneAsync(d => d.Id == document.Id && d.DateModified == oldDateModified, document);
if (result.ModifiedCount == 0)
{
Console.WriteLine($"Error {++errorCount}/{totalCount}: doc {document.Id} did not have DateModified {oldDateModified.ToString("yyyy-MM-dd HH:mm:ss.ffffff")}");
await DoesItExist(collection, document, oldDateModified);
}
}
while (true);
}
代码以每秒大约 250 个文档的速度插入。对 ReplaceOneAsync(d => d.Id == document.Id && d.DateModified == oldDateModified, ...)
的大约 1,000-15,000 次调用中有一次失败,因为它 returns 的 ModifiedCount
为 0。失败率取决于我们 运行 是调试构建还是发布构建以及使用是否附加调试器:更快的速度意味着更多的错误。
显示的代码代表了一些我无法轻易更改的内容。当然,我更愿意执行一系列 Update.Set()
调用,但现在这不是一个真正的选择。 InsertOneAsync()
后跟 ReplaceOneAsync()
是由某种通过引用更新实体的存储库模式抽象的。这些方法的非异步对应物显示相同的行为。
插入和替换之间的简单 Thread.Sleep(100)
可以缓解该问题。
当查询失败时,我们稍等片刻,然后尝试在下面的代码中再次查询文档,每次都会找到。
private static async Task DoesItExist(IMongoCollection<MyDocument> collection, MyDocument document, DateTime oldDateModified)
{
Thread.Sleep(500);
var fromDatabaseCursor = await collection.FindAsync(d => d.Id == document.Id && d.DateModified == oldDateModified);
var fromDatabaseDoc = await fromDatabaseCursor.FirstOrDefaultAsync();
if (fromDatabaseDoc != null)
{
Console.WriteLine("But it was found!");
}
else
{
Console.WriteLine("And wasn't found!");
}
}
发生这种情况的版本:
- MongoDB 社区服务器 3.4.0、3.4.1、3.4.3、3.4.4 和 3.4.10,全部在 WiredTiger 存储引擎上
- 服务器 运行 Windows,其他操作系统
- C# Mongo 驱动程序 2.3.0 和 2.4.4
这是 MongoDB 中的一个问题,还是我们做(或假设)出了什么问题?
或者,实际的最终目标是,我如何确保更新可以立即检索到插入内容?
ReplaceOneAsync returns 0 如果新文档与旧文档相同(因为没有任何变化)。
在我看来,如果您的测试执行得足够快,对 DateTime.Now 的各种调用可能会 return 相同的值,因此您可能将完全相同的文档传递给 InsertOneAsync 和ReplaceOneAsync.