Abbiamo una situazione in cui devo affrontare un massiccio afflusso di eventi in arrivo sul nostro server, a circa 1000 eventi al secondo, in media (il picco potrebbe essere ~ 2000).
Il problema
Il nostro sistema è ospitato su Heroku e utilizza un Heroku Postgres DB , che consente un massimo di 500 connessioni DB. Utilizziamo il pool di connessioni per connettersi dal server al DB.
Gli eventi arrivano più velocemente di quanto il pool di connessioni DB possa gestire
Il problema che abbiamo è che gli eventi arrivano più velocemente di quanto il pool di connessioni possa gestire. Nel momento in cui una connessione ha completato il roundtrip della rete dal server al DB, in modo che possa essere rilasciato nuovamente al pool, più di n
di eventi aggiuntivi entrano.
Alla fine gli eventi si accumulano, in attesa di essere salvati e poiché non ci sono connessioni disponibili nel pool, scadono e l'intero sistema non viene reso operativo.
Abbiamo risolto l'emergenza emettendo gli eventi ad alta frequenza offensivi a un ritmo più lento da parte dei clienti, ma vogliamo ancora sapere come gestire questi scenari nell'evento in cui dobbiamo gestire gli eventi ad alta frequenza.
Vincoli
Altri client potrebbero voler leggere eventi contemporaneamente
Altri client richiedono continuamente di leggere tutti gli eventi con una chiave particolare, anche se non sono ancora stati salvati nel DB.
Un cliente può interrogare GET api/v1/events?clientId=1
e ottenere tutti gli eventi inviati dal client 1, anche se quegli eventi non sono ancora stati salvati nel DB.
Ci sono esempi di "aula" su come affrontarlo?
Possibili soluzioni
Accoda gli eventi sul nostro server
Potremmo accodare gli eventi sul server (con la coda con una concorrenza massima di 400, quindi il pool di connessioni non si esaurisce).
Questa è una pessima idea perché:
- Mangerà la memoria del server disponibile. Gli eventi accodati accatastati consumeranno enormi quantità di RAM.
- I nostri server riavviano una volta ogni 24 ore . Questo è un limite rigido imposto da Heroku. Il server può riavviarsi mentre gli eventi vengono messi in coda causandoci la perdita degli eventi accodati.
- Introduce lo stato sul server, compromettendo così la scalabilità. Se abbiamo una configurazione multi-server e un client vuole leggere tutti gli eventi accodati + salvati, non sapremo su quale server vivono gli eventi collegati.
Utilizza una coda messaggi separata
Suppongo che potremmo usare una coda di messaggi, (come RabbitMQ ?), dove pompiamo i messaggi in essa e dall'altra fine c'è un altro server che si occupa solo di salvare gli eventi sul DB.
Non sono sicuro che le code di messaggi consentano di eseguire query sugli eventi in coda (che non erano ancora stati salvati), quindi se un altro cliente desidera leggere i messaggi di un altro client, posso semplicemente ottenere i messaggi salvati dal DB e dai messaggi in sospeso dalla coda e concatenarli insieme in modo da poterli inviare nuovamente al client di richiesta di lettura.
Utilizza più database, ciascuno dei quali salva una parte dei messaggi con un server DB-coordinator centrale per gestirli
Un'altra soluzione che abbiamo è quella di utilizzare più database, con un "coordinatore di DB / bilanciamento del carico" centrale. Dopo aver ricevuto un evento questo coordinatore sceglierebbe uno dei database in cui scrivere il messaggio. Ciò dovrebbe consentirci di utilizzare più database Heroku, aumentando così il limite di connessione a 500 x numero di database.
Su una query di lettura, questo coordinatore potrebbe rilasciare% query diSELECT
a ciascun database, unire tutti i risultati e inviarli al client che ha richiesto la lettura.
Questa è una pessima idea perché:
- Questa idea suona come ... ahem .. over-engineering? Sarebbe anche un incubo da gestire (backup ecc.). È complicato da costruire e mantenere e, a meno che non sia assolutamente necessario, sembra una KISS violazione.
- Sacrifica Consistenza . Fare transazioni su più DB è un no-go se andiamo con questa idea.