Strategia di memorizzazione nella cache dell'API REST per la raccolta di record

5

Sto progettando un'API REST per i miei client mobili per interagire con il nostro server delle app (costruito con Django / django-rest-framework se fa alcuna differenza).

Ci sono diversi oggetti accessibili tramite l'API, alcuni cambiano frequentemente (diciamo ogni giorno), alcuni quasi mai cambiano (in media meno di una volta al mese), e alcuni per i quali solo alcuni record annidati cambieranno (pensa un post sul blog per il quale aggiungiamo nuovi commenti un paio di volte al giorno).

Poiché i clienti sono sensibili al volume di trasferimento dei dati (per motivi di costo, dati mobili nel paese in via di sviluppo), voglio limitarlo, soprattutto quando scaricano un elenco di oggetti (ad esempio: l'elenco degli oggetti dei post di blog menzionati in precedenza) . Il trasferimento dei dati è di gran lunga la mia più grande preoccupazione qui, molto prima del caricamento lato server.

Ho pensato di utilizzare qualcosa di simile all'header If-Modified-Since HTTP ( link sezione 14.25), che potrebbe funzionare su singole richieste di oggetti, come GET /api/blogposts/<id>/ . Ma con un'elevata latenza di rete (i tempi di ping di oltre 500 ms sono comuni), eseguire dozzine o centinaia di richieste sembra una cattiva idea.

Per ottenere una raccolta di record, mi aspetto che il seguente comportamento aiuti di più nel mio caso (le richieste di cui sto parlando sono simili a quelle descritte in questa risposta : raccolte personalizzate per utente)

GET /api/myblogposts/ inizialmente restituirebbe un elenco JSON di oggetti, non solo gli ID:

[
'post1': {...},
'post2': {...},
...
'postN': {...}
]

Quindi un successivo GET sullo stesso url con l'intestazione appropriata If-Modified-Since: Sat, 29 Oct 2016 19:43:31 GMT filtra l'elenco per restituire solo i record modificati da allora. Il client può quindi unire le modifiche nel suo archivio dati locale.

Questa strategia sembra avere un senso? Esiste uno standard esistente per client e server per negoziare quale sottoinsieme di record trasferire?

    
posta Laurent S 17.02.2017 - 12:58
fonte

1 risposta

3

Se ci pensi veramente, se un record non è cambiato, è già in un certo senso "memorizzato nella cache", perché quel updated_at timestamp non è cambiato; quindi, la tua intuizione circa il recupero dei record che sono stati modificati è il modo migliore per andare su questo "caching". Tuttavia, in realtà non lo chiamerei "cache", ma piuttosto "recupero selettivo".

Tuttavia, come @Joeri Sebrechts ha menzionato nel suo commento, l'uso di testate HTTP in modalità non standard è un ottimo modo per infastidire i manutentori del tuo codice mentre lottano per capire perché tu sia " re usando If-Modified-Since come un parametro di query per filtrare i record. In realtà, è proprio per questo che ha suggerito di usare un parametro di query - sono usati esattamente per questo scopo - e sono pienamente d'accordo.

Quindi la soluzione qui è:

  1. Inizialmente, recupera tutti i record tutti di cui hai bisogno (ad es. all'avvio)
    • memorizza questo valore sul client come timestamp - changed-after (o come vuoi chiamarlo - quando esegui GET
    • assicurati che il record id sia incluso in modo da poter fare un po 'di fusione con i record esistenti più tardi
  2. Quando il cliente deve recuperare nuovi record o aggiornare l'elenco, è sufficiente inviare un altro GET ad es. %codice%
    • la tua API recupererà solo i record con /records?changed-after=THE_STORED_TIMESTAMP
    • invia tali record al client
  3. Sul client, esegui un'operazione di unione sul tuo elenco esistente di record
    • non elimina record dall'elenco
    • prendi semplicemente il set di nuovi record, li trovi nella vecchia lista e li sostituisci
    • lascia il resto della lista non modificato

Alcune altre applicazioni utilizzano websockets per comunicare modifiche ai client; per esempio. il server rileva una modifica in un record e ping a tutti i client che un aggiornamento è disponibile per il recupero. Questo sarebbe, secondo me, il modo più "efficiente" di fare le cose nel caso in cui tu abbia milioni e milioni di record che potrebbero impiegare molto tempo per interrogare, e hai la larghezza di banda disponibile per i websocket. Invece di fare in modo che i client richiedano costantemente gli aggiornamenti che potrebbero essere o meno disponibili (e la possibilità che tali query siano costose), è sufficiente che il server dica ai client quando devono aggiornarsi.

Tuttavia, non sappiamo nulla sulla quantità e la complessità dei dati, ma il semplice fatto che si ha una situazione di latenza elevata e bassa soglia elimina la possibilità di utilizzare websocket, quindi il parametro di query% il filtroupdated_at > changed-after sembra essere l'approccio più appropriato.

PS - Se sei davvero davvero stretto sui dati, potresti persino implementare un log delle modifiche per sapere quali campi sono stati modificati , che consente di inviare selettivamente solo i campi effettivamente aggiornati, anziché l'intero record. Alcuni framework / lingue hanno librerie che fanno ciò, ad es. Paper Trail di Rails . Se pensate che la necessità di un utilizzo della larghezza di banda molto basso valga la pena di aggiungere una tale dipendenza, lo consiglio vivamente. A volte queste librerie rendono il tutto ridicolmente facile, come i metodi alle versioni diff di Paper Trails che ti forniscono solo i dati è stato cambiato. Quindi puoi inviare solo quel dato, insieme al update_at del record, e fondere selettivamente sul client su un campo invece di un intero record base. Neat!

    
risposta data 19.02.2017 - 17:04
fonte

Leggi altre domande sui tag