Abbiamo una situazione in cui devo affrontare un massiccio afflusso di eventi in arrivo sul nostro server, in media a circa 1000 eventi al secondo (il picco potrebbe essere ~ 2000).
Il problema
Il nostro sistema è ospitato su Heroku e utilizza un DB Heroku Postgres relativamente costoso , che consente un massimo di 500 connessioni DB. Usiamo il pool di connessioni per connetterci dal server al DB.
Gli eventi arrivano più velocemente di quanto il pool di connessioni DB sia in grado di gestire
Il problema che abbiamo è che gli eventi arrivano più velocemente di quanto il pool di connessioni possa gestire. Quando una connessione ha terminato il round trip di rete dal server al DB, in modo che possa essere rilasciata nuovamente nel pool, più di n
altri eventi 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 viene reso non operativo.
Abbiamo risolto l'emergenza emettendo gli eventi offensivi ad alta frequenza a un ritmo più lento dai clienti, ma vogliamo ancora sapere come gestire questi scenari nel caso in cui dobbiamo gestire quegli 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 client 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 "classe" 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 cattiva idea perché:
- Consumerà la memoria disponibile del server. Gli eventi accatastati accumulati consumeranno enormi quantità di RAM.
- I nostri server si riavviano ogni 24 ore . Questo è un limite rigido imposto da Heroku. Il server può riavviarsi mentre gli eventi vengono accodati causandoci la perdita degli eventi accodati.
- Introduce lo stato sul server, danneggiando così la scalabilità. Se disponiamo di un'impostazione multi-server e un client desidera leggere tutti gli eventi accodati + salvati, non sapremo su quale server vivono gli eventi accodati.
Utilizzare una coda messaggi separata
Presumo che potremmo usare una coda di messaggi, (come RabbitMQ ?), Dove pompiamo i messaggi in esso e dall'altra parte c'è un altro server che si occupa solo di salvare gli eventi sul DB.
Non sono sicuro che le code dei messaggi consentano di eseguire query sugli eventi accodati (che non sono stati ancora salvati), quindi se un altro client desidera leggere i messaggi di un altro client, posso semplicemente ottenere i messaggi salvati dal DB e i messaggi in sospeso dalla coda e concatenarli insieme in modo da poterli rispedire al client di richiesta di lettura.
Utilizzare più database, ognuno dei quali salva una parte dei messaggi con un server coordinatore DB centrale per gestirli
Un'altra soluzione che abbiamo pensato è usare più database, con un "coordinatore / bilanciamento del carico" centrale. Al ricevimento di 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 può inviare SELECT
query a ciascun database, unire tutti i risultati e inviarli al client che ha richiesto la lettura.
Questa è una cattiva idea perché:
- Questa idea sembra ... ehm ... troppo ingegneristica? Sarebbe un incubo anche da gestire (backup ecc.). È complicato da costruire e mantenere e, a meno che non sia assolutamente necessario, suona come una violazione dei KISS .
- Sacrifica la coerenza . Effettuare transazioni su più DB non è necessario se seguiamo questa idea.
ANALYZE
le query stesse e non sono un problema. Ho anche creato un prototipo per testare l'ipotesi del pool di connessioni e verificato che questo è davvero il problema. Il database e il server stesso vivono su macchine diverse, quindi la latenza. Inoltre, non vogliamo rinunciare a Heroku a meno che non sia assolutamente necessario, non essere preoccupati per le distribuzioni è un vantaggio enorme per noi.
select null
su 500 connessioni. Scommetto che scoprirai che il pool di connessioni non è il problema.