Tenemos una situación en la que tengo que lidiar con una afluencia masiva de eventos que llegan a nuestro servidor, a aproximadamente 1000 eventos por segundo, en promedio (el pico podría ser ~ 2000).
El problema
Nuestro sistema está alojado en Heroku y utiliza una base de datos Heroku Postgres DB relativamente cara , que permite un máximo de 500 conexiones de base de datos. Utilizamos la agrupación de conexiones para conectarnos desde el servidor a la base de datos.
Los eventos llegan más rápido de lo que el grupo de conexiones DB puede manejar
El problema que tenemos es que los eventos llegan más rápido de lo que el grupo de conexiones puede manejar. En el momento en que una conexión ha finalizado el viaje de ida y vuelta de la red desde el servidor a la base de datos, para que pueda liberarse de nuevo al grupo, más que n
eventos adicionales entran.
Eventualmente, los eventos se acumulan, esperando ser guardados y debido a que no hay conexiones disponibles en el grupo, se agota el tiempo de espera y todo el sistema se vuelve no operativo.
Hemos resuelto la emergencia emitiendo los eventos ofensivos de alta frecuencia a un ritmo más lento por parte de los clientes, pero aún queremos saber cómo manejar estos escenarios en el caso de que necesitemos manejar esos eventos de alta frecuencia.
Restricciones
Otros clientes pueden querer leer eventos al mismo tiempo
Otros clientes solicitan continuamente leer todos los eventos con una clave particular, incluso si aún no están guardados en la base de datos.
Un cliente puede consultar GET api/v1/events?clientId=1
y obtener todos los eventos enviados por el cliente 1, incluso si esos eventos aún no se han guardado en la base de datos.
¿Hay ejemplos de "aula" sobre cómo lidiar con esto?
Soluciones posibles
Poner en cola los eventos en nuestro servidor
Podríamos poner en cola los eventos en el servidor (con la cola con una concurrencia máxima de 400 para que el grupo de conexiones no se agote).
Esta es una mala idea porque:
- Se comerá la memoria del servidor disponible. Los eventos en cola apilados consumirán grandes cantidades de RAM.
- Nuestros servidores se reinician una vez cada 24 horas . Este es un límite duro impuesto por Heroku. El servidor puede reiniciarse mientras los eventos están en cola, lo que nos hace perder los eventos en cola.
- Introduce el estado en el servidor, lo que perjudica la escalabilidad. Si tenemos una configuración de servidores múltiples y un cliente quiere leer todos los eventos en cola + guardados, no sabremos en qué servidor viven los eventos en cola.
Use una cola de mensajes separada
Supongo que podríamos usar una cola de mensajes (¿como RabbitMQ ?), Donde bombeamos los mensajes y en el otro extremo hay otro servidor que solo se ocupa de guardar los eventos en la base de datos.
No estoy seguro de si las colas de mensajes permiten consultar eventos en cola (que aún no se guardaron), por lo que si otro cliente desea leer los mensajes de otro cliente, puedo obtener los mensajes guardados de la base de datos y los mensajes pendientes de la cola y concatenarlos juntos para que pueda enviarlos de vuelta al cliente de solicitud de lectura.
Use múltiples bases de datos, cada una de las cuales guarda una parte de los mensajes con un servidor central coordinador de DB para administrarlos
Sin embargo, otra solución que tenemos es utilizar múltiples bases de datos, con un "coordinador de DB / equilibrador de carga" central. Al recibir un evento, este coordinador elegiría una de las bases de datos para escribir el mensaje. Esto debería permitirnos usar múltiples bases de datos Heroku, aumentando así el límite de conexión a 500 x número de bases de datos.
Tras una consulta de lectura, este coordinador podría emitir SELECT
consultas a cada base de datos, fusionar todos los resultados y enviarlos de vuelta al cliente que solicitó la lectura.
Esta es una mala idea porque:
- Esta idea suena como ... ejem ... ¿sobre ingeniería? Sería una pesadilla para administrar también (copias de seguridad, etc.). Es complicado de construir y mantener y, a menos que sea absolutamente necesario, suena como una violación de KISS .
- Sacrifica la consistencia . Hacer transacciones a través de múltiples bases de datos es imposible si seguimos con esta idea.
ANALYZE
de las consultas y no son un problema. También construí un prototipo para probar la hipótesis del grupo de conexiones y verifiqué que este es realmente el problema. La base de datos y el servidor en sí viven en diferentes máquinas, de ahí la latencia. Además, no queremos renunciar a Heroku a menos que sea absolutamente necesario, no preocuparnos por las implementaciones es una gran ventaja para nosotros.
select null
en 500 conexiones. Apuesto a que encontrará que el grupo de conexiones no es el problema allí.