在本文中,你将了解即时确认和延迟确认之间的差异。
立即确认
对于许多应用程序,你想要确保立即确认事件。 这可以防止持久化版本滞后于内存中的当前版本,并避免在粒度失败时丢失最新状态的风险。 可以遵循以下规则来保证这一点:
- 在粒度方法返回之前,确认使用ConfirmEvents的所有RaiseEvent调用。
- 确保在粒度方法返回之前完成 RaiseConditionalEvent 的任务。
- 请避免同时使用 ReentrantAttribute 或 AlwaysInterleaveAttribute 属性,因此每次只处理一个 Grain 调用。
如果遵循这些规则,则意味着在引发事件后,在事件写入存储之前,无法执行其他粒度代码。 因此,无法观察内存中版本与存储版本之间的不一致。 虽然这通常是你想要的,但它也有一些潜在的缺点。
潜在缺点
如果与远程群集或存储的连接暂时中断,颗粒将不可用。 实际上,粒度在等待确认事件时无法执行任何代码,这可能需要无限期的时间(日志一致性协议会一直重试,直到存储连接恢复)。
处理单个粒度实例的许多更新时,一次确认一个更新可能会变得非常低效,这可能会导致吞吐量不佳。
延迟确认
为了在上述情况下提高可用性和吞吐量,谷粒可以选择执行以下一项或两项操作:
- 允许粒度方法引发事件,而无需等待确认。
- 允许重新进入,因此即使以前的请求因等待确认而卡住,该实体仍能继续处理新的请求。
这意味着,当某些事件仍在确认时,可以执行粒度代码。 API JournaledGrain<TGrainState,TEventBase> 具有特定条款,赋予你精确控制权,以处理当前正在进行的未确认事件。
可以检查以下属性,找出当前未确认的事件:
IEnumerable<EventType> UnconfirmedEvents { get; }
此外,由 JournaledGrain<TGrainState,TEventBase>.State 属性返回的状态不包括未确认事件的效果,因此有一个备用属性:
StateType TentativeState { get; }
此属性返回一个“暂定”状态,该状态是通过在State
中应用所有未确认事件后获取的。 暂定状态本质上是对未来状态的“最佳预测”,即在所有未确认事件得到确认后,最有可能成为下一个确认状态。 但是,不能保证它最终将成为被确认的状态。 这是因为粮食可能会歉收,或者事件可能会与其他事件竞争并失败,导致取消(如果有条件)或比预期晚出现在序列中(如果无条件)。
并发保证
请注意,基于轮次的调度(协作并发)的保证始终适用,即使在使用重新进入或延迟确认的情况下也是如此。 这意味着,即使有多个方法正在进行中,但只有一种方法可以主动执行,所有其他方法都停滞在一个 await
位置。 因此,并行线程永远不会造成任何真正的竞争条件。
具体而言,请注意:
- 在方法执行过程中,属性State和TentativeStateVersionUnconfirmedEvents属性可以更改。
- 但是,此类更改只能在卡在一个
await
位置时发生。
这些保证假设你的代码保持在任务和异步/await的推荐做法范围内(特别是,不使用线程池任务,或者仅在不涉及Grain功能且正确等待的代码中使用线程池任务)。