Hallo zusammen,
wir haben den folgenden Use Case zu entwickeln:
Eine Webanwendung, die eine Datentabelle mit bis zu 100.000 Zeilen und 30-50 Spalten anzeigt. Jede Spalte ist filterbar und sortierbar. Die Webanwendung basiert auf Angular und PrimeNG (mit PrimeNG Table) und das Backend basiert auf ASP.Net Core WebApi, aber ich denke, das spielt keine Rolle.
Die Benutzer sind sehr pingelig, daher ist die Performance eine Top-Priorität, und die Anwendung sollte skalierbar sein. Das Filtern einer Tabelle mit 100.000 Zeilen sollte idealerweise weniger als 5 Sekunden dauern.
Es gibt zwei Besonderheiten in unserer Umgebung:
- Der Datensatz für die Webanwendung wird in einer SQL-Stored procedure generiert. Die komplexe Logik zur Aufbereitung der Daten befindet sich also nicht in der API selbst.
- Die Webanwendung wird Echtzeitkommunikation über SignalR implementieren. Wenn also ein Benutzer Daten ändert, erhalten alle anderen Benutzer diese Änderungen sofort.
Was wir vorläufig ausgeschlossen haben..
Im Moment denke ich, dass es keine gute Idee ist, den gesamten Datensatz von der API herunterzuladen und den Client das ganze Filtern/Sortieren übernehmen zu lassen.
Alles würde im Javascript - Code passieren. Und ich kann mir nicht vorstellen das dies bei großen Datenmengen noch schnell ist. Zudem hängt es auch vom PC des Endnutzers ab.
Deshalb dachten wir an das gute alte Paging.
Unsere Idee
Ich habe diesen Prozess vor Augen:
Web Client sendet Anfrage an API -> API ruft SQL-Stored Procedure auf und speichert das Ergebnis in einem Cache Store wie Redis oder ähnlichem und gibt dann die erste Page (die ersten 100 Zeilen) an den Client zurück.
Jetzt, wenn der Benutzer die Daten filtert / sortiert oder zu einer anderen Page wechselt:
Web Client sendet Anfrage an API mit der "CacheId" (oder etwas, um den Datensatz im Cache zu identifizieren) -> Wenn die API eine CacheId erhält, versucht sie zuerst, die Daten aus dem Cache zu laden, anstatt die SQL Stored Procedure erneut aufzurufen. -> Die API wendet Filter, Sortierung, Paging usw. an und gibt dann das Ergebnis an den Client zurück.
Auf diese Weise wird die SQL-Stored Procedure (der teure Part) nicht jedes Mal aufgerufen und auch nur ein kleiner Teil der Daten wird an den Client übertragen.
Natürlich entstehen bei dieser serverseitigen Lösung aber auch mehr Anfragen an die API, welche natürlich auch einen gewissen Zeit-Overhead besitzen.
Unser Hauptproblem / Sorgen
Wie soll die Echtzeit-Kommunikation mit dieser Architektur gehandhabt werden? Wenn jemand etwas an den Daten ändert, müssen alle zwischengespeicherten Datensätze aktualisiert werden. Es ist keine Option, die Datensätze im Cache ungültig zu machen und von Grund auf neu zu generieren, wenn jemand irgendwas ändert. Da Datenänderungen sehr häufig (jede Minute) auftreten wäre es sehr rechenintensiv ständig den kompletten Cache neu zu generieren.
Wir brauchen also so etwas wie das hier:
Benutzer ändert Daten und sendet "DataChanged-Event" -> Datensätze werden aus dem Cache gelesen -> Datensätze werden aktualisiert und dann in den Cache zurückgeschrieben
Unsere Fragen
- Sind wir mit unserer Lösung auf dem richtigen Weg, oder machen wir etwas völlig falsch? Sollten wir doch alles Client-seitig machen? Client-seitig hätte natürlich den Vorteil von generell weniger API-Anfragen und weniger Netzwerklatenz, weil nur einmal ein Zugriff erfolgt um die Daten zu laden. Andererseits hat man dann wieder die Client-seitigen Performance Limitierungen.
- Wo sollen wir den Cache-Update-Prozess ansiedeln? Vielleicht eine Azure-Function / Konsolenanwendung oder ähnliches, das alle "DataChanges Events" empfängt und dann den Cache aktualisiert?
Wir würden uns über ein paar Anregungen freuen :)
|
|