Condividi tramite


Gestione dei cluster in Orleans

Orleans fornisce la gestione dei cluster tramite un protocollo di appartenenza predefinito, talvolta definito appartenenza al cluster. L'obiettivo di questo protocollo è consentire a tutti i silos (server Orleans) di accettare il set di silos attualmente attivi, rilevare i silos in errore e consentire a nuovi silos di unirsi al cluster.

Il protocollo si basa su un servizio esterno per fornire un'astrazione di IMembershipTable. IMembershipTable è una tabella piatta e durevole usata per due scopi. In primo luogo, funge da punto di incontro affinché i silos possano trovarsi tra loro e i Orleans clienti possano trovare i silos. In secondo luogo, archivia la visualizzazione di appartenenza corrente (elenco di silo attivi) e aiuta a coordinare l'accordo su questa visualizzazione.

Attualmente sono disponibili diverse implementazioni di IMembershipTable: basate su Archiviazione tabelle di Azure, Azure Cosmos DB, ADO.NET (PostgreSQL, MySQL/MariaDB, SQL Server, Oracle), Apache ZooKeeper, Consul IO, AWS DynamoDB, MongoDB, Redis, Apache Cassandra e un'implementazione in memoria per lo sviluppo.

Oltre a IMembershipTable, ogni silo partecipa a un protocollo di membership peer-to-peer completamente distribuito che rileva i silos falliti e raggiunge un accordo sul set di silos attivi. L'implementazione interna del Orleansprotocollo di appartenenza è descritta di seguito.

Protocollo di appartenenza

  1. All'avvio, ogni silo aggiunge una voce per se stessa in una tabella condivisa nota usando un'implementazione di IMembershipTable. Orleans usa una combinazione di identità del silo (ip:port:epoch) e ID di distribuzione del servizio (ID cluster) come chiavi univoche nella tabella. Epoch è semplicemente il tempo in tick in cui è stato avviato questo silo, garantendo che ip:port:epoch sia univoco all'interno di una determinata distribuzione Orleans.

  2. I silo monitorano l'uno l'altro direttamente tramite sonde applicative ("sei vivo"). heartbeats Le sonde vengono inviate come messaggi diretti dal silo al silo sugli stessi socket TCP usati per le normali comunicazioni. In questo modo, le sonde sono correlati completamente ai problemi effettivi di rete e allo stato del server. Ogni silo controlla un insieme configurabile di altri silo. Un silo sceglie chi problemare calcolando hash coerenti sulle identità di altri silo, formando un anello virtuale di tutte le identità e selezionando i successivi X silos sull'anello. Si tratta di una tecnica distribuita nota denominata hashing coerente ed è ampiamente usata in molte tabelle hash distribuite, ad esempio Chord DHT.

  3. Se un silo S non riceve risposte di sondaggio Y dal server monitorato P, sospetta P aggiungendo il sospetto con timestamp nella riga di P in IMembershipTable.

  4. Se P ha più di Z sospetti entro K secondi, S scrive che P è deceduto nella riga di P e trasmette uno snapshot della tabella di appartenenza corrente a tutti gli altri silos. I silos aggiornano periodicamente la tabella, quindi lo snapshot è un'ottimizzazione per ridurre il tempo necessario affinché tutti i silos apprendano la nuova visualizzazione di appartenenza.

  5. In dettaglio:

    1. Il sospetto viene scritto in IMembershipTable, in una colonna speciale nella riga corrispondente a P. Quando S sospetta P, scrive: "al momento TTT S ha sospettato P".

    2. Un sospetto non è sufficiente per dichiarare p morto. Sono necessari sospetti Z da diversi silo all'interno di un intervallo di tempo configurabile T (in genere 3 minuti) per dichiarare P morto. Il sospetto viene scritto usando il controllo della concorrenza ottimistica fornito da IMembershipTable.

    3. Il silo sospetto S legge la riga di P.

    4. Se S è l'ultimo sospettato (ci sono già stati sospetti Z-1 entro il periodo T, come registrato nella colonna sospetta), S decide di dichiarare P morto. In questo caso, S si aggiunge all'elenco di sospetti e scrive anche nella colonna Stato P che P è morto.

    5. In caso contrario, se S non è l'ultimo sospetto, S si aggiunge solo alla colonna del sospetto.

    6. In entrambi i casi, il writeback usa il numero di versione o ETag letto in precedenza, serializzando gli aggiornamenti a questa riga. Se la scrittura ha esito negativo a causa di una mancata corrispondenza della versione/ETag, S ritenta (legge nuovamente e tenta di scrivere, a meno che P non sia già stato contrassegnato come morto).

    7. A livello generale, questa sequenza di "lettura, modifica locale, writeback" è una transazione. Tuttavia, le transazioni di archiviazione non vengono necessariamente usate. Il codice "transaction" viene eseguito localmente in un server e la concorrenza ottimistica fornita da IMembershipTable garantisce isolamento e atomicità.

  6. Ogni silo legge periodicamente l'intera tabella di appartenenza per il suo impiego. In questo modo, i silos imparano a conoscere i nuovi silos che si uniscono e gli altri silos dichiarati morti.

  7. Trasmissione snapshot: per ridurre la frequenza delle letture periodiche delle tabelle, ogni volta che un silo scrive nella tabella (sospetto, nuovo join e così via), invia uno snapshot dello stato della tabella corrente a tutti gli altri silo. Poiché la tabella di appartenenza è coerente e a versionamento monotono, ogni aggiornamento genera un'istantanea con versione univoca che può essere condivisa in modo sicuro. Ciò consente la propagazione immediata delle modifiche di appartenenza senza attendere il ciclo di lettura periodico. La lettura periodica viene comunque mantenuta come meccanismo di fallback nel caso in cui la distribuzione degli snapshot non riesca.

  8. Visualizzazioni di appartenenza ordinate: il protocollo di appartenenza garantisce che tutte le configurazioni di appartenenza siano completamente ordinate a livello globale. Questo ordinamento offre due vantaggi principali:

    1. connettività garantita: quando un nuovo silo viene aggiunto al cluster, deve convalidare la connettività bidirezionale a ogni altro silo attivo. Se un silo esistente non risponde (potenzialmente indica un problema di connettività di rete), il nuovo silo non può essere aggiunto. In questo modo si garantisce la connettività completa tra tutti i silo nel cluster in fase di avvio. Si veda la nota IAmAlive riportata di seguito per un'eccezione negli scenari di ripristino di emergenza.

    2. Aggiornamenti coerenti delle directory: protocolli di livello superiore, ad esempio la directory granulare distribuita, si basano su tutti i silo con una visualizzazione coerente e monotonica dell'appartenenza. Ciò consente una risoluzione più intelligente delle attivazioni duplicate di grano. Per maggiori dettagli, consultare la documentazione della directory Grain.

    Dettagli dell'implementazione:

    1. Il IMembershipTable richiede aggiornamenti atomici per garantire un ordine totale globale di modifiche:

      • Le implementazioni devono aggiornare sia le voci della tabella (elenco di silo) che il numero di versione in modo atomico.
      • A tale scopo, utilizzare transazioni di database (come in SQL Server) o operazioni atomiche di confronto e scambio usando ETags (come in Azure Table Storage).
      • Il meccanismo specifico dipende dalle funzionalità del sistema di archiviazione sottostante.
    2. Una riga speciale della versione di appartenenza nella tabella tiene traccia delle modifiche:

      • Ogni scrittura nella tabella (sospetti, dichiarazioni di morte, collegamenti) incrementa questo numero di versione.
      • Tutte le scritture vengono serializzate attraverso questa riga mediante aggiornamenti atomici.
      • La versione a aumento monotonico garantisce un ordinamento totale di tutte le modifiche di appartenenza.
    3. Quando silo S aggiorna lo stato del silo P:

      • Prima, S legge lo stato più recente della tabella.
      • In una sola operazione atomica, aggiorna contemporaneamente la riga di P ed incrementa il numero di versione.
      • Se l'aggiornamento atomico ha esito negativo (ad esempio, a causa di modifiche simultanee), l'operazione riprova con ritardo esponenziale.

    considerazioni sulla scalabilità:

    La serializzazione di tutte le scritture tramite la riga di versione può influire sulla scalabilità a causa di un aumento della contesa. Il protocollo si è dimostrato efficace nell'ambiente di produzione con un massimo di 200 silo, ma potrebbe affrontare sfide oltre un migliaio di silo. Per distribuzioni molto grandi, altre parti di Orleans (messaggistica, directory granulare, hosting) rimangono scalabili anche se gli aggiornamenti dell'appartenenza diventano un collo di bottiglia.

  9. configurazione predefinita: La configurazione predefinita è stata ottimizzata durante l'uso in produzione in Azure. Per impostazione predefinita: ogni silo viene monitorato da tre altri silo, due sospetti sono sufficienti per dichiarare un silo morto e i sospetti vengono considerati solo dagli ultimi tre minuti (altrimenti sono obsoleti). Le sonde vengono inviate ogni dieci secondi e occorre perdere tre sonde per sospettare un silo.

  10. monitoraggio automatico: il rilevatore di errori incorpora idee della ricerca Lifeguard di Hashicorp (paper, talk, blog) per migliorare la stabilità del cluster durante eventi irreversibili in cui una grande parte del cluster riscontra un errore parziale. Il componente LocalSiloHealthMonitor assegna punteggi all'integrità di ogni silo utilizzando diverse euristiche.

    • Stato attivo nella tabella di appartenenza
    • Nessun sospetto da altri silo
    • Risposte recenti riuscite alla sonda
    • Richieste di probe recenti ricevute
    • Velocità di risposta del pool di thread (elementi di lavoro eseguiti entro 1 secondo)
    • Accuratezza del timer (attivazione entro 3 secondi dall'orario programmato)

    Il punteggio di salute di un silo influisce sui timeout della sonda: i silos malsani (punteggio 1-8) hanno timeout aumentati rispetto ai silos sani (punteggio 0). Ciò offre due vantaggi:

    • Offre più tempo alle sonde per avere successo quando la rete o il sistema è sotto stress.
    • Rende più probabile che i silos nocivi vengano eliminati prima di poter votare erroneamente i silos sani.

    Ciò è particolarmente utile durante scenari come la saturazione del pool di thread, in cui i nodi lenti potrebbero sospettare erroneamente nodi integri solo perché non possono elaborare le risposte abbastanza rapidamente.

  11. Probe indiretto: un'altra funzionalità ispirata a Lifeguard migliora l'accuratezza del rilevamento degli errori riducendo la probabilità che un silo non integro o partizionato dichiari erroneamente un silo integro morto. Quando un silo di monitoraggio ha due tentativi di sondaggio rimanenti per un silo di destinazione prima di effettuare una votazione per dichiararlo fuori servizio, utilizza il sondaggio indiretto.

    • Il silo di monitoraggio seleziona in modo casuale un altro silo come intermediario e gli chiede di sondare la destinazione.
    • L'intermediario tenta di contattare il silo di destinazione.
    • Se la destinazione non risponde entro il periodo di timeout, l'intermediario invia una notifica negativa.
    • Se il silo di monitoraggio riceve un riconoscimento negativo dall'intermediario e l'intermediario si dichiara integro (tramite il monitoraggio automatico, descritto in precedenza), il silo di monitoraggio esprime un voto per dichiarare il bersaglio morto.
    • Con la configurazione predefinita di due voti necessari, un'accettazione negativa da parte di una sonda indiretta viene conteggiata come entrambi i voti, consentendo una dichiarazione più rapida di silos inattivi quando più prospettive confermano l'errore.
  12. Applicazione del rilevamento perfetto degli errori: una volta che un silo viene dichiarato morto nella tabella, tutti lo considerano tale, anche se non è realmente morto (ad esempio, semplicemente partizionato temporaneamente o i messaggi di heartbeat sono stati persi). Tutti smettano di comunicare con esso. Una volta che il silo apprende che è morto (leggendo il nuovo stato dalla tabella), termina il processo. Di conseguenza, deve essere presente un'infrastruttura per riavviare il silo come un nuovo processo (all'avvio viene generato un nuovo numero di epoca). Se ospitato in Azure, questo avviene automaticamente. In caso contrario, è necessaria un'altra infrastruttura, ad esempio un servizio Windows configurato per il riavvio automatico in caso di errore o una distribuzione kubernetes.

  13. Cosa accade se la tabella è inaccessibile per un certo periodo di tempo:

    Quando il servizio di archiviazione è inattivo, non è disponibile o si verificano problemi di comunicazione, il Orleans protocollo non dichiara erroneamente silos inattivo. I silo operativi continuano a funzionare senza problemi. Tuttavia, Orleans non sarà in grado di dichiarare un silo come morto (se rileva un silo morto tramite sonde non riuscite, non può registrare questo fatto nella tabella) e non potrà consentire l'ingresso di nuovi silo. Quindi, la completezza ne risente, ma l'accuratezza no: il partizionamento dalla tabella non causa mai Orleans di dichiarare erroneamente un silo morto. Inoltre, in una partizione di rete parziale (in cui alcuni silo possono accedere alla tabella e altri non possono), Orleans potrebbero dichiarare un silo inattivo, ma ci vuole tempo per tutti gli altri silo per conoscerlo. Il rilevamento potrebbe essere ritardato, ma Orleans non disattiva mai erroneamente un silo a causa dell'indisponibilità delle tabelle.

  14. IAmAlive funzioni per la diagnostica e il recupero da disastri:

    Oltre ai segnali di vita inviati tra silo, ogni silo aggiorna periodicamente un timestamp "I Am Alive" nella riga della tabella. Questo serve due scopi:

    1. Diagnostica: fornisce agli amministratori di sistema un modo semplice per controllare lo stato di attività del cluster e determinare quando un silo è stato attivo per l'ultima volta. Il timestamp viene aggiornato per impostazione predefinita ogni 30 secondi.

    2. Ripristino di emergenza: se un silo non ha aggiornato il timestamp per diversi periodi (configurato tramite NumMissedTableIAmAliveLimit, impostazione predefinita: 3), i nuovi silo lo ignorano durante i controlli della connettività di avvio. In questo modo il cluster può eseguire il ripristino da scenari in cui i silos si arrestano in modo anomalo senza una pulizia adeguata.

Tabella di appartenenza

Come accennato, IMembershipTable funge da punto di incontro per i silo per trovarsi tra loro e per Orleans ai clienti di trovare i silo. Consente inoltre di coordinare l'accordo sulla visione dell'appartenenza. Il repository principale Orleans contiene implementazioni per molti sistemi, tra cui Archiviazione tabelle di Azure, Azure Cosmos DB, PostgreSQL, MySQL/MariaDB, SQL Server, Apache ZooKeeper, ISUL IO, Apache Cassandra, MongoDB, Redis, AWS DynamoDB e un'implementazione in memoria per lo sviluppo.

  1. Archiviazione tabelle di Azure: in questa implementazione, l'ID distribuzione di Azure funge da chiave di partizione e l'identità silo (ip:port:epoch) funge da chiave di riga. Insieme garantiscono una chiave univoca per ogni silo. Per il controllo della concorrenza, viene usato il controllo della concorrenza ottimistica basato su ETag delle tabelle di Azure. Ogni volta che i dati vengono letti dalla tabella, l'ETag per ogni riga di lettura viene archiviata e usata quando si tenta di eseguire il writeback. Il servizio tabelle di Azure assegna e controlla automaticamente gli ETag in ogni scrittura. Per le transazioni a più righe, viene usato il supporto per le transazioni batch fornite da Tabella di Azure , garantendo transazioni serializzabili su righe con la stessa chiave di partizione.

  2. SQL Server: in questa implementazione, l'ID di distribuzione configurato distingue tra le distribuzioni e quali silo appartengono alle distribuzioni. L'identità silo è definita come una combinazione di deploymentID, ip, port, epoch nelle tabelle e nelle colonne appropriate. Il back-end relazionale usa il controllo della concorrenza ottimistica e le transazioni, analogamente all'uso di ETag nell'implementazione della tabella di Azure. L'implementazione relazionale prevede che il motore di database generi l'ETag. Per SQL Server 2000, l'ETag generato viene acquisito da una chiamata a NEWID(). In SQL Server 2005 e versioni successive viene usato ROWVERSION . Orleans legge e scrive ETag relazionali come tag opachi VARBINARY(16) e li archivia in memoria come stringhe con codifica Base64 . Orleans supporta inserimenti su più righe tramite UNION ALL (per Oracle, incluso DUAL), che viene attualmente usato per inserire i dati delle statistiche. L'implementazione esatta e la logica per SQL Server sono disponibili in CreateOrleansTables_SqlServer.sql.

  3. Apache ZooKeeper: in questa implementazione, l'ID di distribuzione configurato viene usato come nodo radice e l'identità silo (ip:port@epoch) come nodo figlio. Insieme garantiscono un percorso unico per ogni silo. Per il controllo della concorrenza, viene usato il controllo della concorrenza ottimistica basato sulla versione del nodo . Ogni volta che i dati vengono letti dal nodo radice della distribuzione, la versione per ogni nodo silo figlio di lettura viene archiviata e usata quando si tenta di eseguire il writeback. Ogni volta che i dati di un nodo cambiano, il servizio ZooKeeper aumenta in modo atomico il numero di versione. Per le transazioni a più righe, viene utilizzato il metodo multi, garantendo transazioni serializzabili sui nodi del silo con lo stesso ID di distribuzione del nodo padre.

  4. Consul IO: l'archivio Key/Value di Consul è stato usato per implementare la tabella di appartenenza. Per ulteriori dettagli, consultare Consul Deployment.

  5. AWS DynamoDB: in questa implementazione, l'ID distribuzione del cluster viene usato come chiave di partizione e identità silo (ip-port-generation) come RangeKey, rendendo univoco il record. La concorrenza ottimistica viene ottenuta usando l'attributo ETag effettuando scritture condizionali in DynamoDB. La logica di implementazione è molto simile a Archiviazione tabelle di Azure.

  6. Apache Cassandra: in questa implementazione, il composto di ID servizio e ID cluster funge da chiave di partizione e l'identità silo (ip:port:epoch) come chiave di riga. Insieme garantiscono una riga unica per silo. Per il controllo della concorrenza, viene usato il controllo della concorrenza ottimistica basato su una versione di colonna statica che usa una transazione leggera. Questa colonna di versione viene condivisa per tutte le righe della partizione/cluster, fornendo un numero di versione con incremento coerente per la tabella di appartenenza di ogni cluster. In questa implementazione non sono presenti transazioni a più righe.

  7. Emulazione in memoria per la configurazione dello sviluppo: per questa implementazione viene usata una specifica granularità di sistema. Questo grano risiede in un silo primario designato, che viene usato solo per una configurazione di sviluppo. In qualsiasi utilizzo di produzione reale, non è necessario un silo primario.

Motivazioni del progetto

Una domanda naturale potrebbe essere il motivo per cui non basarsi completamente su Apache ZooKeeper o etcd per l'implementazione dell'appartenenza al cluster, potenzialmente usando il supporto predefinito di ZooKeeper per l'appartenenza a gruppi con nodi temporanei? Perché implementare il protocollo di appartenenza? Ci sono stati principalmente tre motivi:

  1. Hosting/distribuzione nel cloud:

    Zookeeper non è un servizio ospitato. Ciò significa che in un ambiente cloud i Orleans clienti dovranno distribuire, eseguire e gestire l'istanza di un cluster ZK. È un peso superfluo che non è stato imposto ai clienti. Usando Tabella di Azure, Orleans si basa su un servizio gestito ospitato, rendendo la vita dei clienti molto più semplice. In pratica, nel cloud usare Cloud come piattaforma, non infrastruttura. D'altra parte, quando si eseguono in locale e si gestiscono i server, affidarsi a ZK come implementazione di IMembershipTable è un'opzione praticabile.

  2. Rilevamento diretto degli errori:

    Quando si utilizza l'appartenenza a gruppi di ZK con nodi temporanei, il rilevamento degli errori avviene tra i server Orleans (client ZK) e i server ZK. Ciò potrebbe non essere necessariamente correlato ai problemi di rete effettivi tra Orleans i server. Il desiderio era che il rilevamento degli errori riflette accuratamente lo stato di comunicazione all'interno del cluster. In particolare, in questa progettazione, se un Orleans silo non riesce a comunicare con IMembershipTable, non è considerato morto e può continuare a funzionare. Al contrario, se è stata usata l'appartenenza al gruppo ZK con nodi temporanei, una disconnessione da un server ZK potrebbe causare Orleans la dichiarazione di un silo (client ZK) inattivo, mentre potrebbe essere attivo e completamente funzionante.

  3. Portabilità e flessibilità:

    Come parte della Orleansfilosofia di , Orleans non forza una forte dipendenza da qualsiasi particolare tecnologia, ma piuttosto fornisce un design flessibile in cui i diversi componenti possono essere facilmente trasferiti con implementazioni diverse. Questo è esattamente lo scopo che serve l'astrazione IMembershipTable .

Proprietà del protocollo di appartenenza

  1. Può gestire un numero qualsiasi di errori:

    Questo algoritmo può gestire qualsiasi numero di errori (f<=n), incluso il riavvio completo del cluster. Questo contrasto con le soluzioni "tradizionali" basate su Paxos, che richiedono un quorum (in genere una maggioranza). Le situazioni di produzione hanno mostrato scenari in cui più della metà dei silos erano inattivi. Questo sistema è rimasto funzionale, mentre l'appartenenza basata su Paxos non sarebbe in grado di fare progressi.

  2. Il traffico verso la tabella è molto leggero

    Le sonde effettive passano direttamente tra i server, non alla tabella. Il routing delle sonde attraverso la tabella genererebbe traffico significativo e sarebbe meno accurato dal punto di vista del rilevamento delle anomalie: se un silo non può raggiungere la tabella, perderebbe il suo battito "I am alive" e altri lo dichiarerebbero morto.

  3. Precisione ottimizzabile rispetto alla completezza:

    Anche se non è possibile ottenere sia un rilevamento perfetto che accurato degli errori, in genere si desidera la possibilità di bilanciare l'accuratezza (non voler dichiarare morto un silo attivo) con la completezza (volendo dichiarare morto un silo morto il prima possibile). Il numero configurabile di voti per dichiarare sonde morte e mancanti consente di bilanciare questi due aspetti. Per ulteriori informazioni, vedere Yale University: Computer Science Failure Detectors.

  4. Scala:

    Il protocollo può gestire migliaia, probabilmente anche decine di migliaia, di server. Ciò contrasta con le tradizionali soluzioni basate su Paxos, come i protocolli di comunicazione di gruppo, che sono noti per non scalare oltre le decine di nodi.

  5. Diagnostica:

    La tabella è anche molto utile per la diagnostica e la risoluzione dei problemi. Gli amministratori di sistema possono trovare istantaneamente l'elenco corrente dei silo attivi nella tabella, così come vedere la storia di tutti i silo eliminati e sospetti. Ciò è particolarmente utile durante la diagnosi dei problemi.

  6. Perché l'archiviazione permanente affidabile necessaria per l'implementazione di IMembershipTable:

    L'archiviazione persistente viene utilizzata per IMembershipTable a due scopi. In primo luogo, funge da punto di incontro affinché i silos possano trovarsi tra loro e i Orleans clienti possano trovare i silos. In secondo luogo, l'archiviazione affidabile consente di coordinare l'accordo sulla visualizzazione di appartenenza. Mentre il rilevamento dei guasti si verifica direttamente tra i silos peer-to-peer, la vista dell'appartenenza viene archiviata in un archivio affidabile e il meccanismo di controllo della concorrenza fornito da questa archiviazione viene usato per raggiungere un accordo su chi è attivo e chi è inattivo. In un certo senso, questo protocollo esternalizza il problema difficile del consenso distribuito al cloud. In questo modo, viene usata la potenza completa della piattaforma cloud sottostante, usandola come piattaforma distribuita come servizio (PaaS).

  7. Scritture dirette IAmAlive nella tabella esclusivamente per la diagnostica:

    Oltre ai battiti cardiaci inviati tra i silos, ogni silo aggiorna periodicamente una colonna "I Am Alive" nella sua riga della tabella. Questa colonna "I Am Alive" viene usata solo per la risoluzione dei problemi manuali e la diagnostica e non viene usata dal protocollo di appartenenza stesso. In genere viene scritto a una frequenza molto più bassa (una volta ogni 5 minuti) e funge da strumento molto utile per gli amministratori di sistema per controllare l'attività del cluster o scoprire facilmente quando il silo è rimasto attivo.

Ringraziamenti

Riconoscimenti per il contributo di Alex Kogan alla progettazione e all'implementazione della prima versione di questo protocollo. Questo lavoro è stato svolto come parte di uno stage estivo in Microsoft Research nell'estate del 2011. L'implementazione di ZooKeeper basata su IMembershipTableShay Hazor, l'implementazione di SQL IMembershipTable è stata eseguita da Veikko Eeva, l'implementazione di AWS DynamoDB IMembershipTable è stata eseguita da Guteenberg Ribeiro, l'implementazione di Consul è IMembershipTable stata eseguita da Paul North, e infine l'implementazione di Apache Cassandra IMembershipTable è stata adattata da OrleansCassandraUtilsArshia001.