同じドキュメントを連続して複数回更新しようとすると、ConcurrencyException が発生し続けます。PUT attempted on document '<id>' using a non current etag
がメッセージです。
UI から保存するたびに、MassTransit を使用してイベントを発行します。このイベントはサブスクライバー キューに送信されますが、イベント ハンドラーをオフラインにします (オフライン サブスクライバーをテストします)。イベントハンドラがオンラインになると、キューが読み取られ、メッセージが意図したとおりに処理されます。
ただし、同じオブジェクトが複数回キューにあるため、最初の書き込みは成功しますが、次の書き込みは成功せず、この同時実行例外がスローされます。
ファクトリ クラスを使用して、すべてのアプリケーションで一貫した IDocumentStore と IDocumentSession を保持しています。UseOptimisticConcurrency = false
GetSession() メソッドで具体的に設定します。
public static class RavenFactory
{
public static IDocumentStore CreateDocumentStore()
{
var store = new DocumentStore() { ConnectionStringName = "RavenDB" };
// Setting Conventions
store.Conventions.RegisterIdConvention<MyType>((db, cmd, e) => e.MyProperty.ToString());
store.Conventions.RegisterAsyncIdConvention<MyType>((db, cmd, e) => new CompletedTask<string>(e.MyProperty.ToString()));
// Registering Listeners
store
.RegisterListener(new TakeNewestConflictResolutionListener())
.RegisterListener(new DocumentConversionListener())
.RegisterListener(new DocumentStoreListener());
// Initialize and return
store.Initialize();
return store;
}
public static IDocumentSession GetSession(IDocumentStore store)
{
var session = store.OpenSession();
session.Advanced.UseOptimisticConcurrency = false;
return session;
}
}
イベントハンドラはこんな感じ。IDocumentSession は、依存性注入を使用して注入されます。IDocumentSession のインスタンスを取得するロジックは次のとおりです。
private static void InitializeRavenDB(IUnityContainer container)
{
container.RegisterInstance<IDocumentStore>(RavenFactory.CreateDocumentStore(), new ContainerControlledLifetimeManager());
container.RegisterType<IDocumentSession, DocumentSession>(new PerResolveLifetimeManager(), new InjectionFactory(c => RavenFactory.GetSession(c.Resolve<IDocumentStore>())));
}
そして、これが ConcurrencyException を持つ実際の EventHandler です。
public class MyEventHandler:Consumes<MyEvent>.All, IConsumer
{
private readonly IDocumentSession _session;
public MyEventHandler(IDocumentSession session)
{
if (session == null) throw new ArgumentNullException("session");
_session = session;
}
public void Consume(MyEvent message)
{
Console.WriteLine("MyEvent received: Id = '{0}'", message.MyProperty);
try
{
_session.Store(message);
_session.SaveChanges();
}
catch (Exception ex)
{
var exc = ex.ToString();
// Deal with concurrent writes ...
throw;
}
}
}
並行性への対処方法についてビジネスで整理できるようになるまで、現時点では並行性例外を無視したいと思います。
では、ConcurrencyException が発生する理由はありますか? ドキュメントが以前に更新されたかどうかに関係なく、保存が行われるようにします。