È possibile memorizzare nella cache i dati su un servizio REST che restituisce dati impaginati?

3

Secondo i principi dell'architettura REST, un'applicazione RESTful dovrebbe essere stateless, quindi ogni volta che richiamo un servizio REST di ASP.NET 4 (con GET verb) che estrae decine di migliaia di record, il servizio REST li impagina in blocchi di 10 (con OData v4), che rende l'interfaccia utente leggera perché carica solo 10 record ogni volta, tuttavia ogni volta che l'utente chiama il blocco successivo di 10 record il controller ASP.NET chiama il metodo di lettura sul livello di accesso ai dati (Dapper micro ORM) che a sua volta tira le stesse migliaia di record più e più volte, anche se il controller restituisce solo 10 record ogni volta grazie al motore di impaginazione OData, le query del livello di accesso ai dati (Dapper) per le stesse migliaia di record ogni volta, che è costoso e lento. So che potrei modificare la query utilizzata da Dapper in modo che il filtro di paginazione scenda al livello di query, ma trovo che sia troppo oneroso da fare poiché il filtro che OData invia può essere piuttosto complesso e non ho il lusso di generare un albero semantico per generare filtri sulla clausola WHERE, e inoltre, non è che OData funzioni in primo luogo? Non è possibile semplicemente memorizzare in cache le migliaia di record da qualche parte per evitare di chiamare il database ogni volta se lo stesso filer viene richiesto più e più volte?

Oh sì, e Entity Framework è un assoluto no, Dapper è obbligatorio, invece.

    
posta Rikai no hōhō 21.02.2018 - 00:58
fonte

4 risposte

3

Isn't it possible to simply cache the thousands of records somewhere to avoid calling the database each time

Sì, ma è esattamente l'opposto di essere stateless , che è REST. Stai cercando di andare contro l'ideologia principale dei servizi RESTful.

Può essere fatto? A livello tecnico, se lo vuoi davvero, certo. Ma questo è un caso del problema XY . La soluzione proposta (Y - la memorizzazione nella cache dei dati) è una soluzione alternativa al problema reale (X - impaginando la query per ridurre la quantità di dati restituiti).

La memorizzazione dell'intero set di dati risolve solo la metà del problema. Se l'utente guarda mediamente solo due pagine significa che hai recuperato 20 righe utili e 9980 righe inutili (supponendo 10.000 righe in totale). Ciò significa che 99,8% dei dati recuperati non viene mai utilizzato. Questo è ancora un grande spreco di spazio.

Inoltre, dovresti memorizzare nella cache un set di dati separato per ogni combinazione univoca di filtri. Il che significa che probabilmente avrai una tonnellata di dati duplicati in memoria, perché un determinato record potrebbe apparire in diversi set di dati filtrati in modo diverso.
Supponendo che tu abbia una quantità significativa di utenti concorrenti e memorizzi tutte le loro richieste nella cache, sto iniziando a pensare che sarebbe più efficiente memorizzare semplicemente l'intera tabella in memoria una volta, solo per eliminare i duplicati ( Non sto dicendo che dovresti fare questo , sto solo sottolineando che il caching di tutto causerà più problemi di quanti ne possa risolvere).

Ci sono molte ragioni per cui non dovresti cercare di memorizzare nella cache i dati non impaginati come soluzione per un problema di impaginazione.

I know I could modify the query that Dapper uses so the pagination filter goes down to the query level, but I find that's too much burden to do

Bene, se rifiuti di impaginare il set di dati, ovviamente non puoi aspettarti un set di dati impaginato. Ma poi stai escludendo la soluzione corretta a favore di una soluzione unRESTful più semplice; che probabilmente creerà debito tecnico per il futuro.

Dai un'occhiata a questa impaginazione con l'esempio di Dapper Dovrai cambiare la sottoquery a tua scelta query.

SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY InsertDate) AS RowNum, *
          FROM      Posts
          WHERE     InsertDate >= '1900-01-01'
        ) AS result
WHERE   RowNum >= 1 // *your pagination parameters
    AND RowNum =< 10  //*
ORDER BY RowNum

Tutto ciò che devi fare per implementare questo per te è calcolare i limiti di riga ( 1 e 10 nell'esempio). Questi possono essere trovati usando semplici calcoli.

Nota: presumo che pageNumber sia 1-indexed, perché in genere è come l'interpreta l'interfaccia utente. pageSize è 10 nel tuo caso.

var row_limit_lower = ((pageNumber - 1) * pageSize) + 1;
var row_limit_upper = (pageNumber * pageSize) - 1;

Scopri quale numero di pagina è stato richiesto. Sulla base di questo (e una dimensione della pagina, che deduco è sempre 10 per la tua applicazione), puoi calcolare e implementare l'impaginazione necessaria nella query stessa, impedendo così il recupero di molte righe inutili.

    
risposta data 21.02.2018 - 12:02
fonte
1

Non ho mai lavorato con ASP.Net, ma prima di tutto, perché il livello di accesso ai dati non ha ottenuto solo i record necessari? La maggior parte dei database relazionali ti permette di impaginare con qualcosa come LIMIT e OFFSET, e presumo che Dapper ti dia accesso a queste funzionalità.

Una volta installato, puoi incorporare il numero di pagina nella chiave di cache.

Tuttavia, potrebbe valere la pena di avere un punto di taglio dopo il quale paes non viene memorizzato nella cache, dal momento che non molti utenti possono visitare le pagine successive, ma provare a memorizzare quel contenuto potrebbe sfrattare i dati utilizzati più frequentemente dalla cache.

    
risposta data 21.02.2018 - 02:59
fonte
1

Un'opzione (già menzionata in precedenza) sarebbe quella di introdurre un livello di caching sul tuo sistema come componente aggiuntivo. Quindi è possibile lasciare tutto uguale, tranne - quando è necessario interrogare il database, si passa prima alla cache, se la cache non riesce a soddisfare, quindi la query viene passata al database. La tua chiave di cache sarebbe fondamentalmente i parametri che hai passato alla stored procedure - stringificati.

Questo tipo di memorizzazione nella cache trasparente non rende realmente lo stato del sistema o violare i principi REST. Sei libero di definire che cos'è lo SLA per i tuoi dati e abbinalo alla durata della cache.

Suggerirei di serializzare i tuoi dati in alcuni archivi, BLOB, Redis, File, Mongo (dovrai sperimentare ciò che è meglio per il tuo caso d'uso) e aggiungere un piccolo servizio in cima.

L'impaginazione della query non è sempre l'opzione migliore: cosa succede se la query impiega 30 secondi per essere eseguita? Lo eseguirai su ogni pagina? Anche nel tuo caso hai menzionato che il tuo filtro è complesso e potrebbe essere difficile comunicare con la stored procedure.

Devi decidere il modo migliore per applicare le possibili soluzioni per soddisfare le tue esigenze specifiche, quindi non c'è davvero solo una risposta migliore qui.

Inoltre, ci sono molti modi per risolvere il problema della "cache diventa troppo grande". Di nuovo - dovrai trovare quello che funziona per te. Uno potrebbe essere - solo una voce nella cache per utente.

    
risposta data 21.02.2018 - 17:31
fonte
1

Esiste una soluzione REST pura a questo tipo di problema che potrebbe funzionare per te, se sei disposto a cambiare leggermente l'interfaccia utente. Invece di fare inizialmente un GET all'utente, lo si cambia in un POST. Inizi a eseguire la query e acquisisci i risultati da qualche parte. Mentre è in esecuzione, si restituisce un URI che punta ai risultati.

L'interfaccia utente può quindi iniziare a ritrarre il risultato e impaginare secondo necessità. se lo si desidera, è possibile scadere i risultati a un certo punto e restituire una risposta 410 successiva.

    
risposta data 21.02.2018 - 17:59
fonte

Leggi altre domande sui tag