La scorsa settimana al CHAR(10) conferenza abbiamo tenuto un workshop su "Cloud Databases". In parole povere:cosa fare quando i requisiti del caso d'uso superano le risorse disponibili nel server del database.
Questo è stato un argomento principale dell'intera conferenza e durante la giornata sono state illustrate diverse soluzioni. Un tema comune è stato che nessuna soluzione si adatta a tutti i casi d'uso e che ogni soluzione ha il suo costo; quindi devi scegliere la soluzione che il tuo caso d'uso può permettersi.
Un altro punto comune (sebbene implicito) è stato il focus su soluzioni "di alto livello", ovvero:collegare più server di database a un livello superiore per emulare un unico server con risorse maggiori.
Un ovvio vantaggio è che non è necessario modificare il codice PostgreSQL ben analizzato; uno svantaggio è che utilizzando più server di database con le loro linee temporali indipendenti si perdono alcune proprietà utili. Due esempi:la perdita parziale della semantica transazionale genera conflitti; l'analisi preliminare di ogni query al di fuori del database introduce limitazioni alle query accettate.
La discussione è stata piuttosto interessante e quando Dimitri Fontaine ha menzionato i tablespace remoti ho iniziato a interrogarmi su un'idea correlata ma distinta, vale a dire:se un approccio di livello inferiore il problema della messa in comune delle risorse sarebbe davvero impraticabile. Prima che potessi approfondire i dettagli, il workshop si è concluso e ho potuto solo abbozzare l'idea ad alcune delle persone che erano attorno alla lavagna (tra cui Gabriele Bartolini, Nic Ferrier, Marko Kreen, Hannu Krosing, Greg Smith) insieme alle basi domande "sembra fattibile?" e "assomiglia a qualcosa che già conosci?".
Un breve schizzo:uno stack di applicazioni può essere rappresentato in questo modo
(application) --> (connection) --> (db server) --> (resources)
dove le risorse utilizzate dal database includono storage, RAM e CPU. Lo scopo è consentire all'applicazione di comandare più risorse per aumentare la capacità e la velocità. Le applicazioni "intelligenti" che gestiscono più database possono essere rappresentate come
(application) --> (connection) --> (db server) --> (resources) | +---------> (connection) --> (db server) --> (resources)
mentre le soluzioni di “connection pooling” possono essere rappresentate come
(application) --> (connection) --> (db server) --> (resources) | +---------> (db server) --> (resources)
per soluzioni di "livello inferiore" intendo qualcosa come
(application) --> (connection) --> (db server) --> (resources) | +---------> (resources)
che potrebbe assomigliare a qualcosa di familiare, ma non è ciò che sto proponendo qui. Per spiegare la differenza posso aumentare il dettaglio e scrivere
(resources) = (virtual resources) --> (physical resources)
per rappresentare il fatto che al livello più basso si può avere una mappatura non banale tra oggetti fisici e virtuali. Ad esempio, lo storage SAN o lo striping RAID possono fornire dischi virtuali più grandi unendo insieme dischi fisici più piccoli. Tali casi potrebbero essere raffigurati come
(application) --> (connection) --> (db server) --> (virt.res.) --> (ph.res.) | +--------> (ph.res.)
La mia proposta è di mettere in comune le risorse nel server di database livello, in modo da poter avere una "virtualizzazione" più efficiente utilizzando la conoscenza dei casi d'uso specifici per ciascuna risorsa (CPU, RAM, disco), e allo stesso tempo possiamo evitare molte delle difficoltà del paradigma transazionale. L'immagine sarebbe:
(application) --> (connection) --> (db server) --> (virt.res.) --> (ph.res.) | +--------> (virt.res.) --> (ph.res.)
Il vantaggio è che non abbiamo bisogno di gestire tutti i possibili casi d'uso per ogni risorsa virtuale; dobbiamo solo gestire (e ottimizzare) i casi d'uso effettivamente necessari a PostgreSQL. Ad esempio:WAL dovrebbe essere ancora scritto in un archivio locale “non virtualizzato”, bgwriter accederà a risorse locali e remote (RAM e disco), ecc.
Alcune parole finali sull'affidabilità. Per funzionare correttamente l'intero sistema necessita di ogni sottosistema; gli errori parziali non vengono gestiti, perché questa architettura non è ridondante. È un sistema distribuito, ma non condiviso. Se questa architettura potesse fornire una scalabilità semplice ed economica tramite un server di database virtuale che è funzionalmente equivalente a un server fisico con risorse maggiori, è possibile ottenere un'elevata disponibilità in modo standard impostando due server virtuali identici in una configurazione Hot Standby.
La qualità della rete ha un grande impatto sulle prestazioni complessive; questo progetto potrebbe essere utile solo se si dispone di un array di macchine nella stessa LAN, non solo per motivi di velocità, ma anche perché un errore di rete sarebbe effettivamente un errore di sistema. Anche con queste restrizioni, la mia opinione è che avere questa opzione sarebbe abbastanza utile.
Questo è ancora uno schizzo, da usare come riferimento per ulteriori discussioni. Prossimi passi possibili:
- per fare un elenco dettagliato dei casi d'uso delle risorse
- per decidere quali tecnologie possono essere d'aiuto in ogni caso d'uso
- per stimare i costi effettivi di performance/sviluppo