.Net 드라이버를 사용하여 비동기 업데이트 또는 MongoDB 문서 삽입

미스터 호박

이런 문서가 있습니다

public class SomeDocument
{
    public Guid Id { get; set; }
    public string PropertyA { get; set; }
    public string PropertyB { get; set; }
}

이제 PropertyA와 PropertyB를 적절하게 업데이트하고 비동기 방식으로 작동하는 두 가지 서비스 (A와 B)가 있습니다. 즉, 어떤 서비스가 먼저 완료되고 문서를 작성해야하며 누가 업데이트해야하는지 알 수 없습니다.

따라서 문서를 업데이트 (또는 생성)하기 위해 현재 서비스 A에서 이와 같은 코드를 사용합니다.

var filter = new FilterDefinitionBuilder<SomeDocument>().Where(r => r.Id == id);
var options = new FindOneAndUpdateOptions<SomeDocument, SomeDocument>() { IsUpsert = true };
var update = Builders<SomeDocument>.Update.Set(r => r.PropertyA, "Property A value");

await Database.GetCollection<SomeDocument>("someDocuments").FindOneAndUpdateAsync(filter, update, options);

서비스 B의 다음 코드

var filter = new FilterDefinitionBuilder<SomeDocument>().Where(r => r.Id == id);
var options = new FindOneAndUpdateOptions<SomeDocument, SomeDocument>() { IsUpsert = true };
var update = Builders<SomeDocument>.Update.Set(r => r.PropertyB, "Property B value");

await Database.GetCollection<SomeDocument>("someDocuments").FindOneAndUpdateAsync(filter, update, options);

모든 것이 괜찮아 보이지만 때로는 두 서비스가 동시에 작동 할 때 다음 오류가 발생합니다.

Unhandled Exception: MongoDB.Driver.MongoCommandException: Command findAndModify failed: E11000 duplicate key error collection: someDocuments index: _id_ dup key: { : BinData(3, B85ED193195A274DA94BC86B655B4509) }.
   at MongoDB.Driver.Core.WireProtocol.CommandWireProtocol`1.ProcessReply(ConnectionId connectionId, ReplyMessage`1 reply)
   at MongoDB.Driver.Core.WireProtocol.CommandWireProtocol`1.<ExecuteAsync>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MongoDB.Driver.Core.Servers.Server.ServerChannel.<ExecuteProtocolAsync>d__26`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MongoDB.Driver.Core.Operations.CommandOperationBase`1.<ExecuteProtocolAsync>d__29.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MongoDB.Driver.Core.Operations.WriteCommandOperation`1.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MongoDB.Driver.Core.Operations.FindAndModifyOperationBase`1.<ExecuteAsync>d__19.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MongoDB.Driver.OperationExecutor.<ExecuteWriteOperationAsync>d__3`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MongoDB.Driver.MongoCollectionImpl`1.<ExecuteWriteOperationAsync>d__62`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at CVSP.MongoDbStore.MongoDbWriteModelFacade.<AddRecordField>d__6.MoveNext() in D:\Projects\Test\Source\MongoDbStore\WriteModel\MongoDbWriteModelFacade.cs:line 58
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__6_1(Object state)
   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

이 경우 문서를 어떻게 삽입 / 업데이트해야합니까?

최신 정보

확장이 트릭을 수행했습니다.

    public static async Task<TProjection> FindOneAndUpdateWithConcurrencyAsync<TDocument, TProjection>(this IMongoCollection<TDocument> collection, FilterDefinition<TDocument> filter, UpdateDefinition<TDocument> update, FindOneAndUpdateOptions<TDocument, TProjection> options = null, CancellationToken cancellationToken = default(CancellationToken))
    {
        try
        {
            return await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken);
        }
        catch (MongoException ex)
        {
            Thread.Sleep(10);

            return await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken);
        }
    }

처음 귀두에서 try / catch를 사용하는 것은 이상해 보이며 처음부터 마음에 들지 않았지만 https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/#upsert-and-unique - 모든 의심이 사라졌습니다.

살렘

글쎄, 이것은 동기화 문제이며 불행히도 간단한 해결책이 없습니다. 해킹을 찾기 위해 백엔드에서 일어날 수있는 일을 분석해 보겠습니다.

문서를 upsert하려는 두 개의 스레드 (서비스)가 있다고 가정 해 보겠습니다.

t1: 00:00:00.250 -> find document with Id (1)
t2: 00:00:00.255 -> find document with id (1)

t1: 00:00:00.260 -> No document found
t2: 00:00:00.262 -> No document found

t1: 00:00:00.300 -> Insert a document with Id(1)
t2: 00:00:00.300 -> Insert a document with Id(1)

빙고 ... 예외가 있습니다. 두 스레드가 동일한 ID를 가진 문서를 삽입하려고합니다.

우리가 여기서 무엇을 할 수 있습니까?

이 단점을 우리의 이점으로 바꾸자. 이 예외를 캡처하고 upsert를 다시 호출하십시오. 이번에는 성공적으로 문서를 찾고 업데이트합니다.

다음과 같이 ServiceAnd에 대한 코드를 수정 ServiceB하고 10000 개의 문서를 타이트 루프에 삽입하려고했습니다.

public async Task ServiceA(Guid id)
{
    var filter = new FilterDefinitionBuilder<SomeDocument>().Where(r => r.Id == id);
    var update = Builders<SomeDocument>.Update.Set(r => r.PropertyA, "Property A value");

    var options = new UpdateOptions() { IsUpsert = true };
    var database = _client.GetDatabase("stackoverflow");
    var collection = database.GetCollection<SomeDocument>(CollectionName,
        new MongoCollectionSettings
        {
            WriteConcern = WriteConcern.W1
        });

    await collection.UpdateOneAsync(filter, update, options);
}

public async Task ServiceB(Guid id)
{
    var filter = new FilterDefinitionBuilder<SomeDocument>().Where(r => r.Id == id);
    var update = Builders<SomeDocument>.Update.Set(r => r.PropertyB, "Property B value");

    var options = new UpdateOptions() { IsUpsert = true };
    var database = _client.GetDatabase("stackoverflow");
    var collection = database.GetCollection<SomeDocument>(CollectionName,
        new MongoCollectionSettings
        {
            WriteConcern = WriteConcern.W1
        });

    await collection.UpdateOneAsync(filter, update, options);
}

다음은 내 lanuching 코드입니다. 완벽하지는 않지만 목적에 부합합니다.

for (var i = 0; i < 10000; i++)
{
    var _guid = Guid.NewGuid();
    var _tasks = new[]
    {
      new Task(async (x) =>
      {
          var p = new Program();
          try
          {
              await p.ServiceA(Guid.Parse(x.ToString()));
          }
          catch (MongoWriteException me)
          {
              await Task.Delay(5);
              await p.ServiceA(Guid.Parse(x.ToString()));
          }
      }, _guid),
      new Task(async (x) =>
      {
          var p = new Program();
          try
          {
              await p.ServiceB(Guid.Parse(x.ToString()));
          }
          catch (MongoWriteException me)
          {
              await Task.Delay(5);
              await p.ServiceB(Guid.Parse(x.ToString()));
          }
      }, _guid)
    };

    _tasks[0].Start();
    _tasks[1].Start();
    Task.WaitAll(_tasks);
}

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

MongoDB에서 여러 문서 삽입 또는 업데이트

분류에서Dev

MongoDB가 존재하는 경우 문서 찾기-다른 업데이트-삽입

분류에서Dev

.NET 용 MongoDB 드라이버의 updateOne ()을 사용하여 문서를 업데이트하지만 교체하지 않는 방법

분류에서Dev

MongoDB Java 드라이버에서 문서에 동적 필드를 삽입하는 방법

분류에서Dev

Java mongo db 드라이버를 사용하여 MongoDB 삽입을 사용하는 동안 중복 키 오류

분류에서Dev

C # 드라이버를 사용하여 MongoDB의 다른 데이터베이스로 문서를 이동하는 방법은 무엇입니까?

분류에서Dev

MongoDB-여러 문서 솔루션 업데이트-문서 카운터 업데이트 또는 존재하지 않는 경우 삽입

분류에서Dev

SQL Server에서 DataTable을 사용하여 삽입 또는 업데이트

분류에서Dev

MongoDb가 분할 된 환경에서 Java 드라이버를 사용하여 문서를 삽입 할 수 없음

분류에서Dev

MongoDb가 분할 된 환경에서 Java 드라이버를 사용하여 문서를 삽입 할 수 없음

분류에서Dev

배치 문서가있는 경우 업데이트하고 MongoDB에 삽입

분류에서Dev

csharp mongodb 드라이버를 사용하여 목록에서 항목을 업데이트하는 방법은 무엇입니까?

분류에서Dev

Python 클라이언트를 사용하여 Aerospike에서 비동기 삽입을 수행하는 방법

분류에서Dev

Play Framework 2.x 작업에서 MongoDB 비동기 Java 드라이버를 사용하는 방법은 무엇입니까?

분류에서Dev

nodejs에서 작업자 스레드를 사용하여 mongoDB에 새 데이터를 삽입하는 방법

분류에서Dev

값을 비교하여 SQL 삽입 또는 업데이트

분류에서Dev

MongoJS를 사용하여 MongoDB에 문서를 삽입하는 동안 불일치

분류에서Dev

MongoDB는 또는 쿼리를 사용하여 여러 하위 문서를 업데이트합니다.

분류에서Dev

C # 드라이버를 사용하여 MongoDB의 배열 하위 문서에 포함 된 배열 하위 문서의 필드를 업데이트하는 방법은 무엇입니까?

분류에서Dev

동적 데이터 유형 필드에 C #을 사용하여 MongoDB에서 문서 삽입 및 검색 문제

분류에서Dev

기존 docusign 봉투에서 문서를 삽입 / 업데이트하는 방법

분류에서Dev

$ setOnInsert를 사용하여 문서를 업데이트하는 동안 오류 발생-mongoDB

분류에서Dev

PostgreSQL에서 업데이트 또는 삽입하는 방법

분류에서Dev

mongoDB-커서는 node.js 드라이버를 사용하여 1 개의 문서 만 업데이트합니다.

분류에서Dev

.NET MongoDb 드라이버에서 동적 유형의 ObjectId를 구문 분석하는 방법

분류에서Dev

Mongo가 Vertx에서 삽입 또는 업데이트하기 전에 문서의 유효성을 검사하도록 만드는 방법은 무엇입니까?

분류에서Dev

기존 주 요소에 따라 하위 요소 삽입 / 업데이트 또는 컬렉션에 전체 문서 삽입

분류에서Dev

MongoDB는 JavaScript를 사용하여 모든 문서를 업데이트합니다.

분류에서Dev

MongoDb .net 드라이버를 사용하여 컬렉션의 모든 문서에 대해 몇 가지 특정 필드를 얻는 방법

Related 관련 기사

  1. 1

    MongoDB에서 여러 문서 삽입 또는 업데이트

  2. 2

    MongoDB가 존재하는 경우 문서 찾기-다른 업데이트-삽입

  3. 3

    .NET 용 MongoDB 드라이버의 updateOne ()을 사용하여 문서를 업데이트하지만 교체하지 않는 방법

  4. 4

    MongoDB Java 드라이버에서 문서에 동적 필드를 삽입하는 방법

  5. 5

    Java mongo db 드라이버를 사용하여 MongoDB 삽입을 사용하는 동안 중복 키 오류

  6. 6

    C # 드라이버를 사용하여 MongoDB의 다른 데이터베이스로 문서를 이동하는 방법은 무엇입니까?

  7. 7

    MongoDB-여러 문서 솔루션 업데이트-문서 카운터 업데이트 또는 존재하지 않는 경우 삽입

  8. 8

    SQL Server에서 DataTable을 사용하여 삽입 또는 업데이트

  9. 9

    MongoDb가 분할 된 환경에서 Java 드라이버를 사용하여 문서를 삽입 할 수 없음

  10. 10

    MongoDb가 분할 된 환경에서 Java 드라이버를 사용하여 문서를 삽입 할 수 없음

  11. 11

    배치 문서가있는 경우 업데이트하고 MongoDB에 삽입

  12. 12

    csharp mongodb 드라이버를 사용하여 목록에서 항목을 업데이트하는 방법은 무엇입니까?

  13. 13

    Python 클라이언트를 사용하여 Aerospike에서 비동기 삽입을 수행하는 방법

  14. 14

    Play Framework 2.x 작업에서 MongoDB 비동기 Java 드라이버를 사용하는 방법은 무엇입니까?

  15. 15

    nodejs에서 작업자 스레드를 사용하여 mongoDB에 새 데이터를 삽입하는 방법

  16. 16

    값을 비교하여 SQL 삽입 또는 업데이트

  17. 17

    MongoJS를 사용하여 MongoDB에 문서를 삽입하는 동안 불일치

  18. 18

    MongoDB는 또는 쿼리를 사용하여 여러 하위 문서를 업데이트합니다.

  19. 19

    C # 드라이버를 사용하여 MongoDB의 배열 하위 문서에 포함 된 배열 하위 문서의 필드를 업데이트하는 방법은 무엇입니까?

  20. 20

    동적 데이터 유형 필드에 C #을 사용하여 MongoDB에서 문서 삽입 및 검색 문제

  21. 21

    기존 docusign 봉투에서 문서를 삽입 / 업데이트하는 방법

  22. 22

    $ setOnInsert를 사용하여 문서를 업데이트하는 동안 오류 발생-mongoDB

  23. 23

    PostgreSQL에서 업데이트 또는 삽입하는 방법

  24. 24

    mongoDB-커서는 node.js 드라이버를 사용하여 1 개의 문서 만 업데이트합니다.

  25. 25

    .NET MongoDb 드라이버에서 동적 유형의 ObjectId를 구문 분석하는 방법

  26. 26

    Mongo가 Vertx에서 삽입 또는 업데이트하기 전에 문서의 유효성을 검사하도록 만드는 방법은 무엇입니까?

  27. 27

    기존 주 요소에 따라 하위 요소 삽입 / 업데이트 또는 컬렉션에 전체 문서 삽입

  28. 28

    MongoDB는 JavaScript를 사용하여 모든 문서를 업데이트합니다.

  29. 29

    MongoDb .net 드라이버를 사용하여 컬렉션의 모든 문서에 대해 몇 가지 특정 필드를 얻는 방법

뜨겁다태그

보관