2017-11-07 5 views
3

シングルインスタンスのMongoDBサーバーでは、クライアントの書き込みがジャーナルに設定されていても、挿入後InsertOneAsync()の直後のReplaceOneAsync()は、ジャーナル化されていても常に動作しない

一度ジャーナリングされると、すぐにドキュメントをクエリできるようになりました。

以下のコードは、ドキュメントを挿入した後、ドキュメントの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); 
} 

コードは、1秒間に約250の文書を挿入します。 ReplaceOneAsync(d => d.Id == document.Id && d.DateModified == oldDateModified, ...)への約1,000〜15,000回のコールのうちの1つは、ModifiedCountを返します。これは、デバッグまたはリリースのビルドを実行したかどうか、デバッガを接続したかどうかによって異なります。

示されたコードは、私が実際に簡単に変更できないものです。もちろん、私はむしろ一連の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上でサーバーを実行すると、だけでなく、他のOS
  • C#モンゴドライバ2.3.0と2.4.4

これはMongoDBのでは問題である、あるいは我々が行っている(またはお尻uming)何か間違っている?

実際の最終目標は、どのようにして挿入物を確実に更新で取り戻すことができますか?

+0

投稿したコードを試しましたが、あなたが見た状況を再現できないようです。どのバージョンのC#ドライバを使用していますか? –

+0

@ケビン良いキャッチ、それはドライバ2.3.0と2.4.4で起こります。私の現在のマシン(MongoDB 3.4.3)上のドライバのエラー率は、2500個のインサートで1です。私は長い間ツールを実行したままエラーを表示する必要はありません。 – CodeCaster

+0

@KevinAdistambha、私はMongo 3.4.4と3.6.6-rc3、ドライバ2.4.4とC#4.6を使って同様に再現できました。1 – Skami

答えて

3

ReplaceOneAsyncは、新しい文書が古い文書と同じ場合(変更されていないため)、0を返します。

あなたのテストがDateTime.Nowへのさまざまな呼び出しが同じ値を返すほど速く実行されるように見えます。そうすれば、正確に同じドキュメントをInsertOneAsyncとReplaceOneAsyncに渡すことができます。

+0

そう、 'ModifiedCount'ではなく' MatchedCount'をチェックする必要があります。ドキュメントを変更していないときにドキュメントを置き換えようとはしないと誓っても構いませんが、そのコードの周りに単体テストを追加するのは理想的です。 – CodeCaster

関連する問題