Основы ЖурналаEdGrain
Наследуемые в журнале зерна являются производными от JournaledGrain<TGrainState,TEventBase>следующих параметров типа:
- Представляет
TGrainState
состояние зерна. Он должен быть классом с открытым конструктором по умолчанию. TEventBase
является общим супертипом для всех событий, которые могут быть подняты для этого зерна, и может быть любым классом или интерфейсом.
Все объекты состояния и события должны быть сериализуемыми (так как поставщикам согласованности журналов может потребоваться сохранить их и /или отправить их в сообщения уведомлений).
Для зерен, события которых являются POCOs (обычные старые объекты C#), JournaledGrain<TGrainState> можно использовать в качестве сокращенного списка JournaledGrain<TGrainState,TEventBase>.
Чтение состояния зерна
Чтобы прочитать текущее состояние зерна и определить его номер версии, ЖурналedGrain имеет свойства
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
, события в конечном итоге будут подтверждены - это происходит автоматически в фоновом режиме.
Методы перехода состояния
Среда выполнения автоматически обновляет состояние зерна при возникновении событий. Приложению не нужно явно обновлять состояние после вызова события. Однако приложению по-прежнему нужно предоставить код, указывающий , как обновить состояние в ответ на событие. Это можно сделать двумя способами.
(a) Класс GrainState может реализовать один или несколько Apply
методов в объекте StateType
. Как правило, создается несколько перегрузок, а для типа среды выполнения события выбирается ближайшее совпадение:
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
объект, поэтому разработчики должны убедиться, что он может быть десериализирован правильно при чтении из хранилища.
Создание нескольких событий
Перед вызовом можно выполнить несколько вызовов RaiseEvent
ConfirmEvents
:
RaiseEvent(e1);
RaiseEvent(e2);
await ConfirmEvents();
Однако это, скорее всего, приведет к двум последовательным доступом к хранилищу, и это может привести к риску, что зерно завершается сбоем после записи только первого события. Таким образом, обычно лучше одновременно создавать несколько событий, используя
RaiseEvents(IEnumerable<EventType> events)
Это гарантирует, что заданная последовательность событий записывается в хранилище атомарным образом. Обратите внимание, что так как номер версии всегда соответствует длине последовательности событий, при увеличении числа версий несколько событий увеличивается на несколько раз.
Получение последовательности событий
Следующий метод из базового JournaledGrain
класса позволяет приложению получить указанный сегмент последовательности всех подтвержденных событий:
Task<IReadOnlyList<EventType>> RetrieveConfirmedEvents(
int fromVersion,
int toVersion);
Однако она не поддерживается всеми поставщиками согласованности журналов. Если не поддерживается или если указанный сегмент последовательности больше недоступен, NotSupportedException создается исключение.
Чтобы получить все события до последней подтвержденной версии, один вызовет
await RetrieveConfirmedEvents(0, Version);
Можно получить только подтвержденные события: исключение создается, если toVersion
больше текущего значения свойства Version
.
Так как подтвержденные события никогда не изменяются, нет рас беспокоиться о, даже в присутствии нескольких экземпляров или отложенного подтверждения. Однако в таких ситуациях значение свойства Version
может быть больше времени await
возобновления, чем во время RetrieveConfirmedEvents
вызова, поэтому может потребоваться сохранить его значение в переменной. См. также раздел о гарантиях параллелизма.