【问题标题】:ReplaceOneAsync() immediately after InsertOneAsync() not always working, even when journaled在 InsertOneAsync() 之后立即 ReplaceOneAsync() 并不总是有效,即使在记录时也是如此
【发布时间】:2018-04-20 00:56:49
【问题描述】:

在单实例 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 个文档的速度插入。大约 1,000-15,000 次对 ReplaceOneAsync(d =&gt; d.Id == document.Id &amp;&amp; d.DateModified == oldDateModified, ...) 的调用失败,因为它返回的 ModifiedCount 为 0。失败率取决于我们是否运行 Debug 或 Release 构建以及是否连接了调试器:更快的速度意味着更多的错误。

显示的代码代表了我无法轻易更改的内容。当然,我宁愿执行一系列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 中的问题,还是我们做错了(或假设)?

或者,实际的最终目标,我如何确保插入可以通过更新立即检索?

【问题讨论】:

  • 我尝试了您发布的代码,但似乎无法重现您看到的情况。你用的C#驱动是什么版本的?
  • @Kevin 很好,它发生在驱动程序 2.3.0 和 2.4.4 上。我当前机器(MongoDB 3.4.3)上这些驱动程序的错误率是 2500 次插入中的 1 次。我不必让工具运行很长时间就能看到打印的错误。
  • @KevinAdistambha,我也能够使用 Mongo 3.4.4 和 3.6.6-rc3、驱动程序 2.4.4 和 C# 4.6.1 重现它
  • @KevinAdistambha,此外,我注意到当我增加服务器的延迟时,问题变得不那么重要了。 11ms 似乎完全否定了这个问题(但它可能会更少)。

标签: c# mongodb journaling


【解决方案1】:

如果新文档与旧文档相同,ReplaceOneAsync 返回 0(因为没有任何变化)。

在我看来,如果您的测试执行得足够快,对 DateTime.Now 的各种调用可能会返回相同的值,因此您可能会将完全相同的文档传递给 InsertOneAsync 和 ReplaceOneAsync。

【讨论】:

  • 对,所以我们应该检查MatchedCount,而不是ModifiedCount。我可以发誓,我们不会在未对文档进行任何更改时尝试替换文档,但这听起来是围绕该代码添加更多单元测试的理想机会。
猜你喜欢
  • 2013-12-24
  • 2019-05-14
  • 2017-01-04
  • 2020-06-16
  • 2011-12-06
  • 1970-01-01
  • 1970-01-01
  • 2013-06-18
  • 1970-01-01
相关资源
最近更新 更多