即时确认和延迟确认

在本文中,你将了解即时确认和延迟确认之间的差异。

立即确认

对于许多应用程序,你想要确保立即确认事件。 这可以防止持久化版本滞后于内存中的当前版本,并避免在粒度失败时丢失最新状态的风险。 可以遵循以下规则来保证这一点:

  1. 在粒度方法返回之前,确认使用ConfirmEvents的所有RaiseEvent调用。
  2. 确保在粒度方法返回之前完成 RaiseConditionalEvent 的任务。
  3. 请避免同时使用 ReentrantAttributeAlwaysInterleaveAttribute 属性,因此每次只处理一个 Grain 调用。

如果遵循这些规则,则意味着在引发事件后,在事件写入存储之前,无法执行其他粒度代码。 因此,无法观察内存中版本与存储版本之间的不一致。 虽然这通常是你想要的,但它也有一些潜在的缺点。

潜在缺点

  • 如果与远程群集或存储的连接暂时中断,颗粒将不可用。 实际上,粒度在等待确认事件时无法执行任何代码,这可能需要无限期的时间(日志一致性协议会一直重试,直到存储连接恢复)。

  • 处理单个粒度实例的许多更新时,一次确认一个更新可能会变得非常低效,这可能会导致吞吐量不佳。

延迟确认

为了在上述情况下提高可用性和吞吐量,谷粒可以选择执行以下一项或两项操作:

  • 允许粒度方法引发事件,而无需等待确认。
  • 允许重新进入,因此即使以前的请求因等待确认而卡住,该实体仍能继续处理新的请求。

这意味着,当某些事件仍在确认时,可以执行粒度代码。 API JournaledGrain<TGrainState,TEventBase> 具有特定条款,赋予你精确控制权,以处理当前正在进行的未确认事件。

可以检查以下属性,找出当前未确认的事件:

IEnumerable<EventType> UnconfirmedEvents { get; }

此外,由 JournaledGrain<TGrainState,TEventBase>.State 属性返回的状态不包括未确认事件的效果,因此有一个备用属性:

StateType TentativeState { get; }

此属性返回一个“暂定”状态,该状态是通过在State中应用所有未确认事件后获取的。 暂定状态本质上是对未来状态的“最佳预测”,即在所有未确认事件得到确认后,最有可能成为下一个确认状态。 但是,不能保证它最终将成为被确认的状态。 这是因为粮食可能会歉收,或者事件可能会与其他事件竞争并失败,导致取消(如果有条件)或比预期晚出现在序列中(如果无条件)。

并发保证

请注意,基于轮次的调度(协作并发)的保证始终适用,即使在使用重新进入或延迟确认的情况下也是如此。 这意味着,即使有多个方法正在进行中,但只有一种方法可以主动执行,所有其他方法都停滞在一个 await位置。 因此,并行线程永远不会造成任何真正的竞争条件。

具体而言,请注意:

这些保证假设你的代码保持在任务和异步/await的推荐做法范围内(特别是,不使用线程池任务,或者仅在不涉及Grain功能且正确等待的代码中使用线程池任务)。