Compartir a través de


Confirmaciones inmediata y retrasada

En este artículo va a aprender las diferencias entre confirmaciones inmediata y diferida.

Confirmación inmediata

En muchas aplicaciones se quiere garantizar que los eventos se confirman inmediatamente, de modo que la versión persistente no se retrase con respecto a la versión actual en memoria y que no se corra el riesgo de perder el estado más reciente si se produce un error en el grano. Esto se puede garantizar con estas reglas:

  1. Confirme todas las llamadas RaiseEvent que usan ConfirmEvents antes de que se devuelva el método de grano.
  2. Asegúrese de que las tareas devueltas por RaiseConditionalEvent finalizan antes de que se devuelva el método de grano.
  3. Evite los atributos ReentrantAttribute o AlwaysInterleaveAttribute, de modo que solo se pueda procesar una llamada a grano a la vez.

Si se siguen estas reglas, después de que se produzca un evento, no se puede ejecutar ningún otro código de grano hasta que se haya escrito el evento en el almacenamiento. Por lo tanto, es imposible observar incoherencias entre la versión en memoria y la versión en el almacenamiento. Aunque esto suele ser exactamente lo que se quiere, también tiene algunas posibles desventajas.

Posibles desventajas

  • Si la conexión a un clúster o almacenamiento remoto se interrumpe temporalmente, el grano deja de estar disponible: de hecho, el grano no puede ejecutar ningún código mientras está bloqueado esperando confirmar los eventos, lo que puede tardar un período de tiempo indefinido (el protocolo de coherencia del registro sigue reintentando hasta que se restaura la conectividad del almacenamiento).

  • Al controlar una gran cantidad de actualizaciones en una sola instancia de grano, su confirmación de una en una puede resultar muy ineficaz, por ejemplo, lo que da lugar a un rendimiento deficiente.

Confirmación retrasada

Para mejorar la disponibilidad y el rendimiento en las situaciones mencionadas arriba, los granos pueden optar por hacer una o ambas de las siguientes:

  • Permitir que los métodos de grano generen eventos sin esperar confirmación.
  • Permitir la reentrada, de modo que el grano pueda seguir procesando nuevas llamadas aunque las anteriores estén bloqueadas esperando confirmación.

Esto significa que el código de grano se puede ejecutar mientras algunos eventos siguen en proceso de confirmación. La API JournaledGrain<TGrainState,TEventBase> tiene algunas disposiciones específicas para proporcionar a los desarrolladores un control preciso sobre cómo tratar los eventos no confirmados que están actualmente en curso.

Se puede examinar la siguiente propiedad para averiguar qué eventos están sin confirmar:

IEnumerable<EventType> UnconfirmedEvents { get; }

Además, dado que el estado devuelto por la propiedad JournaledGrain<TGrainState,TEventBase>.State no incluye el efecto de los eventos sin confirmar, hay una propiedad alternativa

StateType TentativeState { get; }

que devuelve un estado "provisional", obtenido de "State", al aplicar todos los eventos no confirmados. El estado provisional es básicamente una "mejor estimación" de lo que probablemente sea el siguiente estado confirmado después de que se confirmen todos los eventos no confirmados. Pero no hay ninguna garantía de que realmente lo sea, ya que se puede producir un error en el grano, o los eventos pueden correr contra otros eventos y perder, lo que hace que se cancelen (si son condicionales) o que aparezcan en una posición posterior en la secuencia a lo previsto (si son incondicionales).

Garantías de simultaneidad

Tenga en cuenta que las garantías de programación basada en turnos de Orleans (simultaneidad cooperativa) siempre se aplican, incluso cuando se usa reentrada o confirmación retrasada. Esto significa que aunque puede haber varios métodos en curso, solo uno se puede estar ejecutando activamente; todos los demás están bloqueados en espera, por lo que nunca hay verdaderas carreras causadas por subprocesos paralelos.

Sobre todo tenga en cuenta que:

Estas garantías dan por hecho que el código de usuario se ajusta al procedimiento recomendado en lo que respecta a las tareas y a async/await (en particular, no usa tareas de grupo de subprocesos, o solo las usa para el código que no llama a la funcionalidad de grano, y se esperan correctamente).