有时,同一粒度的多个实例可能处于活动状态,例如在运行多群集和使用 OneInstancePerClusterAttribute时。
JournaledGrain
旨在支持复制的实例,且摩擦最小。 它依赖于 日志一致性提供程序 来运行必要的协议,以确保所有实例都同意相同的事件序列。 具体而言,它处理以下方面:
一致版本:粒度状态的所有版本(暂定版本除外)都基于相同的全局事件序列。 具体而言,如果两个实例看到相同的版本号,则它们会看到相同的状态。
赛车事件:多个实例可以同时引发事件。 一致性提供程序解决了这种竞争情况,确保所有实例都达成相同的序列。
通知/反应性:在一个粒度实例上引发事件后,一致性提供程序不仅会更新存储,还会通知所有其他粒度实例。
有关一致性模型的一般讨论,请参阅 我们的 TechReport 和 GSP 论文 (全局序列协议)。
条件事件
如果赛车赛事发生冲突,可能会出现问题,例如,由于某种原因,双方不应进行安排。 例如,当从银行账户提取资金时,两个实例可以独立确定存在足够的资金以进行提款,并发出提款事件。 但是,这两个事件的组合可能会导致帐户透支。 为了避免这种情况, JournaledGrain
API 支持 RaiseConditionalEvent 该方法。
bool success = await RaiseConditionalEvent(
new WithdrawalEvent() { /* ... */ });
条件事件仔细检查本地版本是否与存储中的版本匹配。 如果没有,这意味着在此期间事件序列已经增长,这意味着此事件在与另一个事件的竞争中失败了。 在这种情况下,条件事件 不会 追加到日志中,并 RaiseConditionalEvent 返回 false
。
这类似于使用电子标签进行条件性存储更新,并提供简单机制以避免提交冲突的事件。
对同一粒度(例如 DepositEvent
和 WithdrawalEvent
)使用有条件和无条件事件是可能的和明智的。 押金不需要有附加条件:即使DepositEvent
在比赛中失败,也不必取消,仍然可以追加到全局事件序列中。
等待 RaiseConditionalEvent
返回的任务足以确认该事件,也不需要调用 ConfirmEvents
。
显式同步
有时,你可能希望确保粒度完全赶上最新版本。 可以通过调用以下方法来执行:
await RefreshNow();
此调用执行两项操作:
- 确认所有未确认的事件。
- 从存储加载最新版本。