Tecnica per sincronizzare medie / grandi quantità di dati

3

Ho implementato la sincronizzazione per la mia app (con un backend REST), funziona bene ma ci sono alcuni problemi:

  1. Blocchi - Poiché non desidero sovrascrivere i possibili input dell'utente durante la sincronizzazione, blocco l'interfaccia utente (overlay di avanzamento) durante la sincronizzazione. Ciò ovviamente influisce negativamente sull'esperienza utente.
  2. Dimensione: se i dati sono grandi, la richiesta potrebbe richiedere molto e potrebbero verificarsi errori di memoria insufficienti nell'elaborazione della risposta.

Quindi sto pensando a come migliorarlo. Per il punto 2, ho pensato di impaginare semplicemente la richiesta, cioè di inviare quante voci desidero e una data di riferimento (ad es. Data di creazione) per semplificare la query del database.

Ma il punto 1 è un problema indipendentemente da ciò che faccio per il punto 2, a condizione che l'utente possa manipolare i dati durante la sincronizzazione, c'è la possibilità che questi dati vengano sovrascritti con il risultato della sincronizzazione. Ad esempio, potrei bloccare il database del client durante la sincronizzazione, in modo tale che solo il processo di sincronizzazione possa scrivere su di esso e accodare le operazioni del client, ma cosa succede quando queste operazioni provano ad es. incrementare alcuni dati che sono stati appena modificati tramite la sincronizzazione e questo porta a un risultato diverso da quello che il client intendeva fare.

La mia app riguarda la gestione personale della spesa, quindi i requisiti non sono rigorosi, se un aggiornamento viene perso o qualcosa non è tragico. Sto cercando una soluzione bilanciata, che sia facile da implementare pur mantenendo un UX piuttosto buono.

    
posta Ixxzz 05.06.2016 - 15:38
fonte

2 risposte

1

Blocchi

Il problema a cui ti riferisci per lo scenario 1 è la coerenza, ovvero avere 2 negozi con le stesse informazioni e che richiedono che siano coerenti.

In un ambiente distribuito come questo, la concorrenza ottimistica può prevalere, il che significa che, invece di bloccare, consentire qualsiasi modifica in qualsiasi momento e creare un meccanismo per tracciare le sovrapposizioni.

Primo passo, ci sono dati che si sono veramente sovrapposti? O il tuo set di dati è veramente solo un insieme di elementi? In quest'ultimo caso, se ti trovi in una situazione in cui gli articoli possono essere aggiunti da qualsiasi cliente e si replicano, è sufficiente che ciascun cliente tenga traccia di quali articoli non sono stati ancora inseriti.

Se possono esserci sovrapposizioni, ad esempio, si gestiscono documenti (sembra improbabile), si ha il blocco o si concedono aggiornamenti simultanei e sovrapposizioni di flag come problema da risolvere da parte dell'utente. Esempi di questo modello:

  1. Sistemi di controllo della versione del software come git, mercurial, subversion, ecc.
  2. Evernote
  3. Google Documenti

Se hai mai lavorato con il controllo della versione del software, riconoscerai l'idea di un conflitto di fusione e / o sovrapposizione. Questo è lo stesso problema che hai. I sistemi di controllo del codice sorgente come Vault evitano questo problema da parte di ciascun client che tratta ogni file come di sola lettura fino a quando un file non viene "estratto", il che crea un blocco reciprocamente esclusivo su tutti i client. Git, d'altra parte, identificherà i conflitti, li unirà se può farlo, e in caso contrario, costringerà l'utente finale (sviluppatore) a unirsi manualmente.

Evernote consente a mia moglie ed io di modificare la nostra lista della spesa. Per minimizzare i conflitti, ci sincronizziamo il più spesso possibile, ma ci sarà sempre la possibilità di avere cambiamenti in conflitto, cosa che è accaduta. Evernote aggiunge semplicemente versioni di documenti in conflitto l'una sopra l'altra con marcatori che indicano che c'era un conflitto.

In un caso in cui i cambiamenti simultanei sono rari, di solito questo tipo di concorrenza ottimistica è l'ideale. In un sistema di transazioni altamente concorrente, il blocco potrebbe essere un approccio migliore.

Dimensioni

Per quanto riguarda la dimensione dei dati, hai due opzioni a cui posso pensare:

  1. Impaginazione
  2. Streaming

L'impaginazione è probabilmente il modo in cui andrei perché è più semplice codificare, di solito, e più facilmente tracciare i tuoi progressi, e in un certo senso, ti dà più controllo. Con i miei consigli per la gestione della coerenza sopra, si spera che l'impaginazione diventi un non-problema.

Lo streaming potrebbe essere un'alternativa anche per te e ridurrebbe il rischio in termini di coerenza dato che avresti un singolo round trip. Lo streaming fondamentalmente significa cambiare questo flusso di dati:

results = getResultsFromServer();
for (result in results) {
  doSomething(result)
}

a:

getResultsFromServer((result) => {
  doSomething(result)
})

Nel primo caso, l'intero set di risultati deve essere memorizzato nel buffer in modo da poterlo scorrere sopra, anche se si consumano solo i dati uno alla volta in modo forward-only. In quest'ultimo, hai solo bisogno di un articolo alla volta. Ho implementato questo approccio una sola volta per un ampio report che avrebbe sommerso i nostri server di produzione quando un cliente lo eseguiva per un intervallo di date molto ampio.

    
risposta data 08.06.2016 - 03:24
fonte
0

Penso di capire che la tua preoccupazione principale è che l'input sia direttamente associato al database locale e che la sincronizzazione scriva anche nel database locale. Quindi, se l'utente salva qualche valore durante la sincronizzazione, non è definito per quali informazioni verranno mostrate. (L'ultimo vince.)

Una soluzione che utilizzo spesso per l'interfaccia utente è l'utente che modifica una copia dei dati e li salva solo nel database dopo aver scelto di salvarli (anche se è una scelta implicita come fare clic sull'input, applicabile a cose come le liste To Do). Ciò facilita anche l'annullamento di un'operazione: elimina la copia modificata.

Fare qualcosa del genere ti aiuterà nella tua storia di sincronizzazione, perché puoi mantenere le modifiche dalla sincronizzazione al database locale senza disturbare l'input dell'utente. E se "salvano" durante una sincronizzazione, puoi aspettare di salvare fino al termine della sincronizzazione. Non avresti bisogno di bloccare. Se è necessario che i dati "in corso" siano persistenti (ad esempio, in caso di arresto anomalo del dispositivo), è comunque possibile salvarlo ... solo in una posizione diversa nel database.

Per quanto riguarda l'incremento di un certo valore e il raggiungimento di un risultato diverso, questo è un caso classico per l'utilizzo delle operazioni idempotente . Un semplice esempio per illustrare:

AddQuantity(1); // result depends on previous quantity
SetQuantity(13); // result does not depend on previous quantity
                 // can be applied numerous times w/o changing result
                 // idempotent operation

L'uso di numeri letterali è ovviamente forzato, ma 13 potrebbe essere pre-calcolato e trasportato in coda, invece di calcolarlo in seguito tramite il metodo AddQuantity.

Quando si ottengono più utenti che sincronizzano gli stessi elementi, si verificheranno conflitti di concorrenza. Un utente aggiorna e sincronizza, mentre un altro si aggiorna da una vecchia versione e quando il secondo utente va alla sincronizzazione, sovrascrive le modifiche del primo utente.

A quel punto, in genere la risposta è una sorta di controllo della concorrenza come Concorrenza ottimistica con numeri di versione (o ETags) . Il server genererà quindi un conflitto se si tenta di aggiornare una versione precedente dell'articolo. Spetterà a te su come gestire quel conflitto. È possibile visualizzare un messaggio all'utente e chiedergli cosa fare. (Suggerimento: probabilmente sceglieranno sempre di sovrascrivere la versione del server con la loro versione.) Oppure potresti tranquillamente prendere una delle versioni come autorevole. Oppure potresti provare ad applicare un algoritmo per unire le differenze (se applicabile alla tua situazione). Fondamentalmente, fai ciò che fornisce il massimo valore per la tua app, con i compromessi che puoi vivere con.

Si noti che se si esegue la sincronizzazione mentre l'utente sta modificando qualcosa, si ha la possibilità di rilevare gli aggiornamenti all'elemento che stanno cambiando e di visualizzare un banner "Questo elemento è stato aggiornato da un altro utente."

    
risposta data 08.06.2016 - 00:38
fonte

Leggi altre domande sui tag