簡體   English   中英

即使在記錄日志后,InsertOneAsync()之后也立即無法始終運行ReplaceOneAsync()

[英]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個文檔的速度插入。 在1,000至15,000個調用ReplaceOneAsync(d => d.Id == document.Id && d.DateModified == oldDateModified, ...)失敗,因為它返回的ModifiedCount為0。失敗率取決於我們是否運行Debug或Release版本並且是否連接調試器:提高速度意味着出現更多錯誤。

顯示的代碼代表我無法輕易更改的內容。 當然,我寧願執行一系列Update.Set()調用,但是現在這實際上不是一個選擇。 某種存儲庫模式抽象了InsertOneAsync() ReplaceOneAsync()后的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返回0(因為未進行任何更改)。

在我看來,如果您的測試執行得足夠快,則對DateTime.Now的各種調用可能會返回相同的值,因此您可能會將完全相同的文檔傳遞給InsertOneAsync和ReplaceOneAsync。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM