Excelente pregunta Matt (+1), y veo que el propio Sr. Oliver respondió como la respuesta (+1)!
Quería dar un enfoque ligeramente diferente con el que estoy jugando para ayudar con el cuello de botella de 3.000 compromisos por segundo que está viendo.
El patrón CQRS, que la mayoría de las personas que usan EventStore de JOliver parecen estar intentando seguir, permite una serie de subpatrones de "escalamiento horizontal". La primera persona que suele hacer cola es cuando el evento se compromete, lo que está viendo es un cuello de botella. "Queue off" significa descargado de las confirmaciones reales e insertándolas en algún proceso de E/S no bloqueante optimizado para escritura o " cola".
Mi interpretación libre es: Retransmisión
Comando -> controladores de comandos -> evento de difusión -> controladores de eventos -> Evento tienda
En realidad, hay dos puntos de la escala de salida aquí en estos patrones: la Controladores de comandos y Controladores de eventos. Como se señaló anteriormente, la mayoría comienza con escalar las porciones del Manejador de eventos o los Compromisos en su caso a la biblioteca EventStore, porque este suele ser el mayor cuello de botella debido a la necesidad de persistir en algún lugar (por ejemplo, la base de datos Microsoft SQL Server).
Yo mismo estoy usando algunos proveedores diferentes para probar el mejor rendimiento para "poner en cola" estas confirmaciones. CouchDB y .NET AppFabric Cache (que tiene una gran función GetAndLock()). [OT] Realmente me gustan las funciones de caché duradera de AppFabric que le permiten crear servidores de caché redundantes que hacen copias de seguridad de sus regiones en varias máquinas; por lo tanto, su caché permanece activa mientras haya al menos 1 servidor funcionando. [/ OT]
Por lo tanto, imagine que sus controladores de eventos no escriben las confirmaciones en EventStore directamente. En su lugar, tiene un controlador que los inserta en un sistema de "cola", como Windows Azure Queue, CouchDB, Memcache, AppFabric Cache, etc. El punto es elegir un sistema con poco o ningún bloque para poner en cola los eventos, pero algo eso es durable con redundancia incorporada (Memcache es mi menos favorito para las opciones de redundancia). Debe tener esa redundancia, en caso de que un servidor caiga, todavía tiene el evento en cola.
Para finalmente comprometerse con este "evento en cola", hay varias opciones. Me gusta el patrón Queue de Windows Azure para esto, debido a los muchos "trabajadores" que puede tener constantemente buscando trabajo en la cola. Pero no tiene que ser Windows Azure: he imitado el patrón Queue de Azure en código local utilizando una "Cola" y "Roles de trabajo" ejecutándose en subprocesos de fondo. Se escala muy bien.
Supongamos que hay 10 trabajadores buscando constantemente esta "cola" para cualquier evento actualizado por el usuario (generalmente escribo un rol de trabajador único por tipo de evento, facilita el escalado a medida que supervisa las estadísticas de cada tipo). Dos eventos se insertan en la cola, los dos primeros trabajadores recogen un mensaje al instante e insertan (Commit them) directamente en su EventStore al mismo tiempo - multihilo, como mencionó Jonathan en su respuesta. Su cuello de botella con ese patrón sería cualquier respaldo de base de datos/tienda de eventos que seleccione. Digamos que su EventStore está usando MSSQL y el cuello de botella sigue siendo 3,000 RPS. Eso está bien, porque el sistema está diseñado para 'ponerse al día' cuando esos RPS se reducen a, digamos 50 RPS después de una ráfaga de 20,000. Este es el patrón natural que permite CQRS: "Consistencia eventual".
Dije que había otros patrones de escalabilidad nativos de los patrones CQRS. Otro, como mencioné anteriormente, son los controladores de comandos (o eventos de comando). Esto también lo he hecho, especialmente si tiene un dominio de dominio muy rico como lo hace uno de mis clientes (docenas de comprobaciones de validación intensivas en el procesador en cada comando). En ese caso, en realidad pondré en cola los Comandos, para ser procesados en segundo plano por algunos roles de trabajador.Esto también le da un buen patrón de escalabilidad, porque ahora todo su backend, incluidas las confirmaciones EvetnStore de los eventos, puede enhebrarse.
Obviamente, la desventaja de eso es que pierdes algunas verificaciones de validación en tiempo real. Lo resuelvo generalmente segmentando la validación en dos categorías al estructurar mi dominio. Uno es Ajax o validaciones "livianas" en tiempo real en el dominio (algo así como una verificación previa al comando). Y los otros son comprobaciones de validación de falla física, que solo se realizan en el dominio pero no están disponibles para la verificación en tiempo real. Entonces necesitaría codificar por error en el modelo de Dominio. Es decir, siempre codifique una salida si algo falla, por lo general en la forma de un correo electrónico de notificación que le devuelve al usuario que algo salió mal. Como el usuario ya no está bloqueado por este comando en cola, se le debe notificar si el comando falla.
Y sus comprobaciones de validación que deben ir al 'backend' van a su base de datos Query o de "solo lectura", riiiight? No vaya a EventStore para verificar, por ejemplo, una dirección de correo electrónico única. Haría su validación contra su almacén de datos de solo lectura de alta disponibilidad para las consultas de su interfaz. Diablos, haga que un solo documento de CouchDB se dedique solo a una lista de todas las direcciones de correo electrónico del sistema como su porción de Consulta de CQRS.
CQRS es solo sugerencias ... Si realmente necesita verificar en tiempo real un método de validación pesado, entonces puede construir un almacén de consultas (de solo lectura) y acelerar la validación, en la etapa Precomando, antes se inserta en la cola. Mucha flexibilidad. Y hasta diría que validar cosas como los nombres de usuario vacíos y los correos electrónicos vacíos no es ni siquiera una cuestión de dominio, sino una responsabilidad de la interfaz de usuario (descargando la necesidad de realizar la validación en tiempo real en el dominio). Diseñé algunos proyectos en los que obtuve validación de UI muy completa en MVC/MVVM ViewModels. Por supuesto, mi dominio tiene una validación muy estricta, para garantizar que sea válido antes del procesamiento. Pero mover las mediocres comprobaciones de validación de entrada, o lo que yo llamo validación "ligera", hacia arriba en las capas de ViewModel brinda esa retroalimentación casi instantánea al usuario final, sin llegar a mi dominio. (También hay trucos para mantenerlo sincronizado con su dominio).
Por lo tanto, en resumen, posiblemente analice los eventos antes de comprometerlos. Esto encaja muy bien con las características de subprocesamiento múltiple de EventStore como Jonathan menciona en su respuesta.
Gracias por la respuesta Jonathan. Para aclarar;) Cada Commit es un nuevo EventSource, por lo que estoy comprometiendo 3K distintos EventSources por segundo. Ommitir el salto de red no mejoró las cosas, pero es un punto válido. En lo que respecta a las transacciones, no me estoy alistando explícitamente en una transacción, pero eso puede no ser lo mismo que no usar Transacciones. Estoy usando JSON para la serialización, aunque como no estamos atados a la CPU, no creo que eso nos esté limitando todavía. He publicado el arnés de prueba en GitHub (https://github.com/MattCollinge/EventStore-Performance-Tests.git). – MattC