JournaledGrain の基本
ジャーナル処理されたグレインは、次の型パラメーターを使用して JournaledGrain<TGrainState,TEventBase> から派生します。
TGrainState
はグレインの状態を表します。 これは、パブリックの既定のコンストラクターを持つクラスである必要があります。TEventBase
は、このグレインに対して発生させることのできるすべてのイベントに共通のスーパータイプであり、任意のクラスまたはインターフェイスにすることができます。
すべての状態およびイベント オブジェクトはシリアル化可能である必要があります (ログ整合性プロバイダーがそれらを保持したり、通知メッセージで送信したりする必要がある場合があるため)。
イベントが POCO (プレーンな古い C# オブジェクト) であるグレインの場合は、JournaledGrain<TGrainState> は JournaledGrain<TGrainState,TEventBase> の短縮形として使用できます。
グレイン状態の読み取り
現在のグレイン状態を読み取り、そのバージョン番号を確認するには、JournaledGrain にプロパティがあります。
GrainState State { get; }
int Version { get; }
バージョン番号は常に確認済みイベントの合計数と等しく、状態は確認されたすべてのイベントを初期状態に適用した結果です。 バージョン 0 を持つ初期状態 (イベントが適用されていないため) は、GrainState クラスの既定のコンストラクターによって決定されます。
重要: アプリケーションで、State
によって返されるオブジェクトを直接変更しないでください。 読み取り専用を意図しています。 むしろ、アプリケーションで状態を変更する場合は、イベントを発生させることによって間接的に行う必要があります。
イベントを発生させる
イベントの発生は、RaiseEvent 関数を呼び出すことによって実行されます。 たとえば、チャットを表すグレインは、ユーザーが投稿を送信したことを示すために PostedEvent
を発生させることができます。
RaiseEvent(new PostedEvent()
{
Guid = guid,
User = user,
Text = text,
Timestamp = DateTime.UtcNow
});
RaiseEvent
はストレージ アクセスへの書き込みを開始しますが、書き込みが完了するまで待機しないことに注意してください。 多くのアプリケーションでは、イベントが永続化されたことが確認されるまで待つことが重要です。 その場合、常に ConfirmEvents を待ってフォローアップします。
RaiseEvent(new DepositTransaction()
{
DepositAmount = amount,
Description = description
});
await ConfirmEvents();
ConfirmEvents
を明示的に呼び出さない場合でも、イベントは最終的に確認されます。バックグラウンドで自動的に行われます。
状態遷移メソッド
ランタイムは、イベントが発生するたびにグレイン状態を "自動的に" 更新します。 イベントを発生させた後、アプリケーションで状態を明示的に更新する必要はありません。 ただし、アプリケーションでは、イベントに応答して状態を更新する "方法" を指定するコードを提供する必要があります。 これは、次の 2 つの方法で行うことができます。
(a)GrainState クラスは、StateType
に 1 つ以上の Apply
を実装できます。 通常、複数のオーバーロードが作成され、イベントのランタイム型に最も近い一致が選択されます。
class GrainState
{
Apply(E1 @event)
{
// code that updates the state
}
Apply(E2 @event)
{
// code that updates the state
}
}
(b) グレインは TransitionState
関数をオーバーライドできます。
protected override void TransitionState(
State state, EventType @event)
{
// code that updates the state
}
遷移メソッドは、状態オブジェクトを変更する以外に副作用がないと見なされ、決定論的である必要があります (そうでないと、効果は予測できません)。 遷移コードが例外をスローした場合、その例外はキャッチされ、ログ整合性プロバイダーによって発行された Orleans ログの警告に含まれます。
ランタイムが遷移メソッドを正確に呼び出すタイミングは、選択したログ整合性プロバイダーとその構成に依存します。 アプリケーションは、ログ整合性プロバイダーによって特に保証されている場合を除き、特定のタイミングに依存しないでください。
Orleans.EventSourcing.LogStorage ログ整合性プロバイダーなど、一部のプロバイダーでは、グレインが読み込まれるたびにイベント シーケンスが再生されます。 そのため、イベント オブジェクトをストレージから適切に逆シリアル化できる限り、GrainState
クラスと遷移メソッドを根本的に変更できます。 ただし、Orleans.EventSourcing.StateStorage ログ整合性プロバイダーなどの他のプロバイダーでは、GrainState
オブジェクトのみが永続化されるため、開発者はストレージから読み取るときに正しく逆シリアル化できることを確認する必要があります。
複数のイベントを発生させる
ConfirmEvents
を呼び出す前に RaiseEvent
に対して複数の呼び出しを行うことができます。
RaiseEvent(e1);
RaiseEvent(e2);
await ConfirmEvents();
ただし、これにより 2 つの連続するストレージ アクセスが発生する可能性があり、最初のイベントのみを書き込んだ後にグレインが失敗するリスクが発生します。 したがって、通常は、次を使用して複数のイベントを一度に発生させる方が適切です。
RaiseEvents(IEnumerable<EventType> events)
これにより、特定の一連のイベントがストレージにアトミックに書き込まれることが保証されます。 バージョン番号は常にイベント シーケンスの長さと一致するため、複数のイベントを発生させると、バージョン番号が一度に 1 つ以上増えます。
イベント シーケンスを取得する
基本 JournaledGrain
クラスから次のメソッドを使用すると、アプリケーションは、確認されたすべてのイベントのシーケンスの指定されたセグメントを取得できます。
Task<IReadOnlyList<EventType>> RetrieveConfirmedEvents(
int fromVersion,
int toVersion);
ただし、すべてのログ整合性プロバイダーでサポートされているわけではありません。 サポートされていない場合、またはシーケンスの指定されたセグメントが使用できなくなった場合は、NotSupportedException がスローされます。
最新の確認済みバージョンまでのすべてのイベントを取得するには、次のように呼び出します。
await RetrieveConfirmedEvents(0, Version);
確認されたイベントのみを取得できます。toVersion
が Version
プロパティの現在の値より大きい場合、例外がスローされます。
確認されたイベントは変わらないため、複数のインスタンスが存在する場合や、確認が遅れた場合でも、心配する競合はありません。 ただし、このような状況では、await
が再開されるまで、RetrieveConfirmedEvents
の呼び出し時より Version
プロパティの値が大きくなる可能性があるため、その値を変数に保存することをお勧めします。 コンカレンシーの保証に関するセクションも参照してください。
.NET