Creazione di un formato dati per set di dati ad alto slancio

Qui nel Met Office produciamo molti dati e li produciamo velocemente. Al momento della stesura stiamo creando e archiviando circa 200 TB di dati al giorno. Inoltre, molti di questi dati diventano rapidamente obsoleti. Ad esempio, eseguiamo nuovamente il nostro modello meteorologico britannico ad alta risoluzione ogni ora. Se non hai caricato, analizzato, elaborato e analizzato correttamente i dati entro un’ora, le tue conclusioni saranno obsolete. Questo è il problema dei dati “ad alto slancio”, che sono sia grandi che rapidi.

All’Informatics Lab abbiamo implementato una proposta di modifica delle specifiche in Zarr (un nuovo formato di file ottimizzato per il cloud) che ci consente di anteporre, aggiungere e “rollare” questi set di dati di grandi dimensioni e in rapido movimento in modo efficiente, concorrente e sicuro. Ciò consente di lavorare con set di dati che non sono solo grandi ma anche in rapido movimento.

Cosa c’è che non va in NetCDF?

Attualmente gran parte dell’elaborazione dei dati nel Met Office utilizza i file NetCDF come formato di archiviazione dei dati. Sebbene abbia funzionato bene con il Met Office, non pensiamo che la versione corrente di NetCDF (e gli strumenti associati) siano adatti per l’accesso supportato dall’archivio oggetti altamente concorrenziale, come tipico in un’impostazione di cloud computing. Per un argomento più completo sul perché vedi l’articolo di Matthew Rocklin – HDF in the Cloud ma in breve:

  • L’accesso a dati specifici e persino a metadati richiede molte piccole letture su una rete ad alta latenza che rallenta l’accesso ai dati.
  • Di solito vogliamo lavorare con molti (Net, 1000 o più) file NetCDF contemporaneamente, esacerbando notevolmente il problema sopra.
  • La maggior parte (tutte?) Le librerie ricche di funzionalità per funzionare con HDF utilizzano una libreria C che funziona solo su file system POSIX non con archivi di oggetti (pertanto richiedono soluzioni alternative non ottimali)

Per questi motivi (e non solo) stiamo esaminando altri formati di dati che funzionano meglio per il nostro flusso di lavoro supportato dall’archivio oggetti altamente concorrente. Zarr è quello di cui siamo più entusiasti al momento.

Il problema con Zarr

Per tutti i suoi professionisti, forse la più grande sfida con Zarr per noi è la gestione di set di dati “in movimento”. Per illustrare questo, immagina un set di dati supportato da S3 dei numeri da 1 a 10 memorizzati come un array Zarr 1D con una dimensione di blocco di uno. Ciò creerebbe 11 oggetti in S3; l’oggetto .zarray con attributi del set di dati e i blocchi da 0 a 9 che conterrebbero i dati da 1 a 10. Questo è illustrato di seguito:

Se desideri ampliare questo array, ad esempio aggiungi il numero 11, si tratta di un’operazione relativamente semplice che richiede la modifica di un oggetto ( .zattrs ) e la creazione di un nuovo oggetto ( 10 ). Questo è illustrato di seguito (aggiunte verdi , modifiche blu ):

Tuttavia, se si desidera anteporre all’array originale, aggiungendo il numero 0, è necessario riscrivere ogni singolo oggetto e creare un nuovo oggetto. Sembra così:

In questo banale esempio che non è un grosso problema, ma quando si lavora con grandi set di dati con migliaia o milioni di blocchi di dimensioni significative, diventa impossibile.

Il nostro esempio nel mondo reale

Cominciamo con una versione semplificata del problema che in realtà vogliamo risolvere. Il nostro semplice esempio è un set di dati continuo contenente le date dei prossimi 4 giorni. Se oggi è il 01/01/19, il nostro set di dati è simile al seguente:

Ora fingiamo di aver aspettato 24 ore ed è ora il 02/01/19. Ancora una volta il nostro set di dati dovrebbe rappresentare i prossimi 4 giorni e quindi il nostro set di dati aggiornato ora assomiglia a questo (di nuovo cambia in blu ):

Si noti che abbiamo dovuto modificare tutti gli oggetti (tranne .zattrs ). Tuttavia, in termini informativi, ciò che è accaduto è che abbiamo eliminato il 01/01/19 e creato il 05/01/19 . Le date 03/01/19 , 04/01/19 e 05/01/19 non avrebbero dovuto essere modificate.

Ancora una volta, non è un grosso problema in questo banale esempio, ma nel nostro scenario del mondo reale stiamo caricando 7 TB al giorno su AWS Earth e desideriamo esporlo come un set di dati a rotazione di 7 giorni. I modelli meteorologici sottostanti si aggiornano ogni 1 a 6 ore, quindi vorremmo “scorrere” su questa frequenza. Se tutti gli oggetti devono essere riscritti circa ogni 3 ore, stiamo esaminando:

Archivio di 7 giorni * 7 TB al giorno * 24 ore al giorno / 3 ore di frequenza di aggiornamento = ~ 400 TB al giorno .

È molto, soprattutto se si considera che per quanto riguarda il contenuto delle informazioni hai aggiunto solo 7 TB. Anche se (grande se) la maggior parte può essere considerata mossa anziché scrittura, sono ancora milioni di operazioni su oggetti. Con la natura alla fine coerente di S3 è quasi inevitabile che si verifichino errori come collisioni di nomi di oggetti o l’accesso a una combinazione di oggetti pre e post move durante la lettura dei dati.

Questo esempio dimostra a) che questo caso d’uso non è attualmente fattibile eb) questo caso d’uso non è puramente accademico. La buona notizia è che a) è già stata suggerita una soluzione eb) abbiamo implementato una soluzione alternativa che può essere utilizzata nel frattempo.

La soluzione

Una soluzione, come suggerito nel numero 267 di zarr-developers / zarr, consiste nell’aggiungere un ulteriore bit di metadati che definisce l’origine dell’array Zarr. Al momento è inserito nelle specifiche (2.2 al momento della stesura) che l’origine è a 0 lungo ogni dimensione. Questa proposta suggerisce che la posizione dell’origine sia impostata nell’oggetto .zarray . Quindi, per il nostro esempio del set di dati a rotazione di 4 giorni il giorno 2 (02/01/19) il set di dati sarebbe simile al seguente:

Ora invece di “spostare” tutti i dati lungo un oggetto ( 10 , 21 , ecc.) Abbiamo invece creato un nuovo oggetto su 4 e modificato la nostra origin da implicitamente a 0 a esplicitamente a 1. Abbiamo anche eliminato ( rosso ) l’oggetto di cui non abbiamo più bisogno ( 0 ). Questa è una soluzione molto più ordinata al nostro problema di rolling data. Anche accettando indici negativi (come -1) questo approccio consente anche di estendere set di dati anteponendo blocchi.

Se ti sembra una buona idea, ti incoraggio a 👍 la richiesta di funzionalità e forse aggiungere un esempio di come desideri utilizzarla. Poiché questa funzionalità richiede una modifica delle specifiche, è necessario superare una soglia ragionevolmente elevata, quindi ti preghiamo di condividere le tue opinioni sul problema # 267 di zarr-developers / zarr.

Una soluzione alternativa per qui e ora

Invece di fork di Zarr e creare la nostra versione con questa modifica delle specifiche, abbiamo implementato una soluzione alternativa che equivale alla stessa cosa. Per fare ciò abbiamo sfruttato il fatto che Zarr funziona su un negozio di array, che è qualsiasi cosa con un’interfaccia chiave-valore come S3 o un dizionario Python. Creando il nostro negozio di array in grado di comprendere che i blocchi sono indicizzati e possono applicare un offset a tale indice, abbiamo effettivamente implementato l’origine diversa da zero considerata sopra. Abbiamo usato l’elemento .zattrs delle specifiche Zarr per inserire i nostri metadati offset personalizzati. Ecco un’illustrazione del nostro sistema basato sul nostro esempio di 5 giorni di cui sopra:

Responsabilità di scarico per l’eliminazione

Nel nostro scenario AWS Earth reale, vogliamo eliminare alcune centinaia di migliaia di oggetti al giorno. Sebbene non sia un importo insormontabile per monitorare, tracciare, eliminare e amministrare in generale, è un sovraccarico che preferiremmo non avere.

Possiamo evitare questo onere scaricandolo su S3. Per raggiungere questo obiettivo, stiamo utilizzando gli strumenti del ciclo di vita S3 per eliminare automaticamente gli oggetti più vecchi di sette giorni. Tuttavia, la complicazione in questo è che mentre desideriamo che i blocchi di Zarr (in cui sono archiviati i dati effettivi) scadano dopo sette giorni, non vogliamo che .zarray altri oggetti come .zarray oggetti .zarray in quanto ciò romperebbe le specifiche Zarr, risultando in dati illeggibili. Per ovviare a questo, usiamo i tag S3 per contrassegnare oggetti chunk (e solo oggetti chunk) con type=temp_chunk . Possiamo quindi impostare una regola sul nostro bucket per rimuovere questi oggetti dopo sette giorni.

Demo e fonte

Se vuoi provarlo, tra cui semplici demo di anteprime e rolling come discusso sopra, dai un’occhiata alla demo del binder interattivo. Se sei interessato al codice, dai un’occhiata alla fonte su GitHub.

Concorrenza in scrittura

Gli esempi sopra parlano principalmente della lettura dei dati, ma per raggiungere il nostro obiettivo dobbiamo anche scrivere quei dati. Dato il numero di oggetti provenienti da AWS Earth (~ 100.000 al giorno) e il numero di blocchi che vogliamo produrre (1-10 volte il numero), queste operazioni di scrittura dovranno avvenire contemporaneamente. La bellezza del sistema di cui sopra è che finché prendiamo decisioni sensate sul chunking, allora la scrittura simultanea sicura viene gratis.

Le considerazioni sul chunking sono abbastanza semplici. Dobbiamo garantire che qualsiasi blocco sia associato a un solo input. In altre parole: l’elaborazione simultanea di qualsiasi combinazione di input non comporterà mai la scrittura su alcun blocco più di una volta. Ciò significa che i blocchi possono essere più piccoli dei file di input (un input → molti blocchi) ma mai più grandi (molti input → un blocco). A seconda del flusso di input questo potrebbe essere un problema, ma è una concessione che siamo disposti a fare nel nostro caso.

Con l’avvertenza di cui sopra, puoi effettuare liberamente le modifiche ai dati in qualsiasi ordine e avere sempre un set di dati valido. L’ordinamento ideale è:

  1. Scrivi eventuali nuovi pezzi in arrivo
  2. Aggiorna i .zattrs con un nuovo offset
  3. Elimina tutti i vecchi blocchi

In questo scenario, si ottiene uno Zarr “rolled” che non ha dati mancanti. Questo è il caso anche tra uno qualsiasi dei passaggi, ad esempio prima del passaggio 2 ma dopo il passaggio 1.

A causa della natura alla fine coerente di S3 potresti non essere in grado di controllare l’ordine di questi eventi, ma questo è ok. Se i passaggi si verificano fuori sequenza, si ottiene uno Zarr valido con dati nulli / mancanti (probabilmente solo temporalmente). Ad esempio, se il passaggio 3 si verifica per primo, si hanno dati mancanti all’inizio del set di dati e se il passaggio 2 si verifica prima di 1, si hanno dati mancanti alla fine. Queste sono situazioni perfettamente legittime.

limitazioni

La prima limitazione è che questo sistema funziona solo per aggiungere, rimuovere o spostare interi blocchi alla volta. Nel nostro esempio di set di dati a rotazione di 4 giorni, se avessimo una dimensione di blocco di due, potremmo “rotolare” solo a giorni alterni. C’è un buon suggerimento di @alimanfoo su come risolverlo con un’altra aggiunta complementare alla specifica.

La limitazione più significativa per noi e quella su cui avremo più blog in futuro è la gestione dei metadati. I nostri datasest sono accompagnati da risme di metadati come la griglia di coordinate su cui si trovano i dati, l’intervallo di altezza dei dati e così via. La soluzione sopra scarta molte di queste informazioni per fornire un set di dati di scrittura parallela efficiente, scalabile. Maggiori informazioni su come affrontarlo in futuro.

Guarda questo spazio…

Siamo entusiasti di questa piccola aggiunta alle specifiche Zarr e alla soluzione alternativa e crediamo che renda i set di dati Zarr “live” una realtà pratica. Detto questo, questo lavoro è solo l’inizio, quindi guarda questo spazio …

Aggiornare

Un post di follow-up “Come (e non gestire) i metadati in set di dati ad alto momento” è stato ora pubblicato discutendo alcune delle sfide dei metadati citate in precedenza.