Best practice per la sincronizzazione dell'app-cloud della rappresentazione del database

2

Sto pianificando un'app Android simile a un libro di cucina che consenta agli utenti di archiviare / aggiungere / modificare le ricette del succo di frutta, tenere traccia degli ingredienti, ecc. Internamente, memorizzerò i dati in un database sul dispositivo dell'utente (Room, che è un wrapper SQLite) e conserva una copia sul Google Drive dell'utente in modo che possa accedervi da altri dispositivi. Lato Google Drive, il database sarà rappresentato in JSON.

Di cosa sono bloccato, come faccio a rendere la sincronizzazione più efficiente e meno incline al danneggiamento dei dati? Sicuramente non voglio riscrivere l'intero database per ogni cambiamento - sarebbe troppo costoso e molto probabilmente rovinerebbe i dati.

Altri approcci che sto considerando sono:

  1. Mantieni un file JSON separato per ogni tabella (con timestamp per le ultime modifiche) con JSONArray di voci in esso contenute. Sul lato positivo, non avrei dovuto riscrivere tutti i dati semi-statici. Sul lato negativo, tutti i dati più vulnerabili - ricette attuali, ingredienti in magazzino, ecc. - rimarranno altrettanto vulnerabili.

  2. Mantieni una sottocartella per ogni tabella con ogni voce in un file separato con timestamp per le ultime modifiche sia nella sottocartella che nei file / voci interessati. Questo dovrebbe minimizzare la possibilità di corruzione dei dati, ma occuparsi di troppi file non sembra una buona idea - per prima cosa, sarebbe troppo costoso.

Onestamente, come qualcuno che ha avuto a che fare con le app client-server fino ad ora sono un po 'fuori dalla mia profondità, quindi qualsiasi consiglio sarebbe apprezzato.

    
posta Kaworu 04.10.2018 - 10:02
fonte

1 risposta

2

Stai provando a creare un sistema distribuito alla fine coerente. Quelli sono intrinsecamente complicati. È comprensibile che tu abbia difficoltà a trovare una buona soluzione perché non ce n'è una .

Per esempio, si consideri un utente con due dispositivi di “phone” e “tablet”, e la seguente sequenza di eventi:

  • Il telefono e il tablet iniziano con lo stesso database e sono disconnessi da qualsiasi rete.
  • alle 13:00, l'utente cancella la ricetta "Limone" sul proprio tablet.
  • alle 14:00, l'utente modifica la ricetta "Lemon bar" sul proprio telefono.
  • alle 15:00, il telefono si sincronizza con il backup del cloud. Il database di backup conterrà la modifica delle barre di limone.
  • alle 16:00, il tablet si sincronizza con il backup del cloud. Ma ora, il database di backup contiene una modifica di un record che il tablet vuole eliminare. Cosa fare?

In un sistema di controllo della versione, questo sarebbe un "conflitto di unione" che richiederebbe una risoluzione manuale. Tuttavia, in genere non è auspicabile implementare l'interfaccia utente per la risoluzione dei conflitti, almeno nelle app consumer.

Un possibile approccio consiste nel separare i concetti dello stato del database interrogabile e la sequenza di eventi che hanno portato a tale stato ( Evento Sourcing ). Lo stato può essere ricalcolato dalla riproduzione di tutti gli eventi. Qui, la domanda difficile rimane su come fondere più stream di eventi, in particolare su come ordinare gli eventi. In molti casi, è bene ordinare per data e ora dell'evento e si fondono da una strategia “Last One vince” in caso di conflitto. Tuttavia, le esatte regole di fusione dipenderanno dal tuo dominio problematico. Non esiste un approccio universale che funzioni sempre.

In particolare, il risultato è che mentre tutti i dispositivi alla fine mostreranno lo stesso stato (i dispositivi saranno coerenti), ci sono buone probabilità che l'unione abbia portato a perdita di dati o perdita di qualità dei dati. Per le app di consumo questo è probabilmente un po 'accettabile, soprattutto perché i conflitti saranno rari. Ma questo dipende dai tuoi precisi requisiti.

Con un approccio orientato agli eventi, memorizzeresti i registri degli eventi nel backup del cloud e possibilmente le istantanee del database, anche se l'eliminazione di vecchie istantanee senza impedire a un altro dispositivo di sincronizzare correttamente è così difficile che potrebbe essere meglio evitare gli snapshot completamente . Puoi assegnare a ciascun log un nome univoco, ad es. in base al dispositivo + timestamp o basato su un hash dei contenuti. Se i file sono immutabili, questo semplifica la sincronizzazione: carica tutti i file che hai ma il cloud non lo fa e scarica tutti i nuovi registri del cloud (confronta anche l'idea di storage adressable di contenuto ). Mentre la sincronizzazione coinvolgerà lo stato del database locale in fase di ricostruzione dai log degli eventi, questo non dovrebbe rappresentare un problema per database ragionevolmente piccoli.

Tutto ciò diventa molto più semplice se si esegue il proprio server che può fungere da fonte di verità per lo stato dell'applicazione. Normalmente gli eventi verrebbero inviati al server immediatamente e produrranno un nuovo stato lì. Se il dispositivo è disconnesso, potrebbe offrire una modalità offline che memorizza gli eventi in un secondo momento, con la consapevolezza che questi eventi potrebbero essere scartati in caso di conflitto.

Una nota sulla definizione degli eventi: un evento non dovrebbe sostituire un'entità con una nuova versione, ma descrivere una piccola modifica autonoma che si traduce in un modello di dati logicamente coerente. Ciò renderà gli eventi più facili da unire. Ad esempio, un evento che sostituisce una ricetta con una versione modificata è problematico. Potrebbe essere preferibile creare più eventi "contrassegna come preferito", "modifica quantità ingrediente", "inserisci passaggio", "modifica passaggio". Nota che le entità non devono essere referenziate da un ID che dipende dall'ordine degli eventi, ad es. un ID autoincrement. Invece, preferisci gli UUID.

    
risposta data 07.10.2018 - 13:28
fonte