1 Punkte von GN⁺ 2024-09-26 | 1 Kommentare | Auf WhatsApp teilen
  • Wafris ersetzt im Middleware-Client v2 für Rails den benutzereigenen Redis-Speicher aus v1 durch einen SQLite-basierten Speicher, um sowohl die Komplexität der Bereitstellung als auch die Latenz bei der Request-Auswertung zu senken
  • Die ursprüngliche Entscheidung für Redis war von der Trägheit des Rails-Ökosystems mit Heroku und Sidekiq geprägt, in der Praxis mussten Nutzer aber zunehmend auch die Rolle des Redis-Administrators übernehmen
  • Der Lesepfad, der alle eingehenden HTTP-Requests mit Regeln vergleicht, ist der zentrale Engpass; Schreibvorgänge fürs Reporting können langsamer, gebündelt oder asynchron verarbeitet werden
  • Bei Tests der schlechtesten IP-Bereichsabfrage mit einem Datensatz von 1,2 Millionen Bereichen auf einem lokalen MacBook Air M2 zeigte SQLite gegenüber lokalem Redis eine etwa 3-fach höhere Performance; Netzwerklatenz war dabei nicht eingerechnet
  • v2 nutzt eine Synchronisationsstruktur, bei der nach einer bestimmten Zeit oder Anzahl von Requests auf neue Regeln geprüft und dann die gesamte neue SQLite-DB heruntergeladen wird; erfolgreiche Installationen stiegen dadurch um etwa das Dreifache

Bereitstellungs-Reibung in Redis-basiertem v1

  • Wafris ist ein Open-Source-Unternehmen für Web Application Firewalls und bietet einen Middleware-Client für Rails-Anwendungen an
  • Der v1-Client benötigte einen benutzereigenen Redis-Speicher, der zusammen mit der Anwendung bereitgestellt wurde
  • Für die anfängliche Wahl von Redis spielten die einfache Anbindung auf Heroku, der Komfort des Remote-Zugriffs und erfolgreiche Beispiele wie Sidekiq eine Rolle
  • Tatsächliche Nutzerumgebungen waren vielfältiger, und viele Nutzer hatten Schwierigkeiten, Probleme bei Redis-Bereitstellung und -Konfiguration zu debuggen
  • Auch auf RailsWorld 2023 war die Stimmung gegenüber der Annahme negativ, dass neben einer Rails-Anwendung selbstverständlich ein Redis-Server nötig sei

Der Kern des Geschwindigkeitsproblems ist die Netzwerklatenz

  • Redis ist im Vergleich zu traditionellen RDBMS schnell, verlangt als separate Datenbank aber trotzdem Verbindungs-, Speicher- und Prozessverwaltung
  • In Cloud-Umgebungen beeinflusst Netzwerklatenz direkt die Performance der Request-Verarbeitung
    • Wafris muss alle eingehenden HTTP-Requests einer Anwendung mit gespeicherten Regeln vergleichen
    • Selbst wenn der v1-Client maximal optimiert war, konnte eine langsame Netzwerkumgebung der Anwendung die gesamte Antwort verlangsamen
  • Rails-Apps wurden auch nicht immer als einheitlicher „majestic monolith“ bereitgestellt
    • Apps, die über mehrere Zonen verteilt bereitgestellt werden
    • Apps, die auf mehrere Server mit überlappenden Zuständigkeiten aufgeteilt sind
    • Apps, die nur teilweise Rails nutzen und zusammen mit anderen Sprachen oder Frameworks bereitgestellt werden
  • In solchen Betriebsumgebungen wurde die Reibung durch Redis noch größer

Trennung von Lesen und Schreiben in der Wafris-Request-Verarbeitung

  • Wafris wird als Rails-Middleware installiert; nach dem Setzen von Regeln wie „IP 1.2.3.4 blockieren“ werden eingehende Requests mit diesen Regeln verglichen
  • Der vereinfachte Verarbeitungsablauf besteht aus zwei Schritten
    1. HTTP-Request mit den Regeln vergleichen und bei Treffer 403, sonst 200 zurückgeben
    2. Das Ergebnis wie Blockieren, Erlauben oder Durchlassen melden
  • Der erste Schritt, das Lesen der Regeln, ist deutlich wichtiger als das Schreiben fürs Reporting
    • Requests müssen sequenziell verarbeitet werden
    • Wenn das Filtering nicht funktioniert, können bösartige Requests durchkommen
    • Die Request-Auswertung beeinflusst die vom Nutzer wahrgenommene Website-Performance
    • Reporting-Schreibvorgänge können langsamer, im Batch oder asynchron erfolgen

Warum SQLite gewählt wurde

  • Der wichtigste Engpass bei Wafris war Netzwerk-I/O, und die Aussage aus der SQLite-Dokumentation „SQLite competes with fopen(), not with client/server databases“ beeinflusste die Entscheidung
  • Unter der Annahme, dass allein das Eliminieren der Netzwerk-Roundtrips schneller sein könnte als die Redis-basierte Struktur, wurden SQLite und Redis benchmarked
  • Als Referenzen dienten

Benchmark-Umfang und absichtliche Einschränkungen

  • Der Benchmark war kein allgemeiner Datenbank-Performancevergleich, sondern ein bewusst verzerrter Test für den Hot Path von Wafris und dessen schlimmste Query
  • Die schlimmste Query war eine Abfrage auf einer „lexical decimal“-Datenstruktur, die IP-Bereiche und Kategorien zuordnet
    • Ein einfaches Beispiel ist ein IP-zu-Land-Mapping, das prüft, ob eine IP-Adresse in einem Bereich zwischen zwei Adressen liegt, und dann das Land zurückgibt
    • Diese Struktur umfasst Millionen von Zeilen, und bei IPv6 ist jeder Eintrag groß
  • Bereichsabfragen wurden vorab berechnet und in beide Speicher geschrieben
  • Im pathologischen Fall muss jeder eingehende HTTP-Request die Request-IP mit folgenden Bereichen vergleichen
    1. benutzerdefinierte Allow-Bereiche
    2. benutzerdefinierte Block-Bereiche
    3. GeoIP-Bereiche
    4. IP-Reputationsbereiche
  • Dieser Query-Typ war wichtig genug, dass keine anderen Queries oder Funktionen portiert wurden und nur dieser eine Typ getestet wurde

Testmethode und Ergebnisse

  • Getestet wurde auf einem lokalen MacBook Air M2 mit per Homebrew installiertem Redis und einer lokalen SQLite-DB
  • Das Protokoll war wie folgt
    1. Verwendung des bestehenden Bereichs-Datensatzes mit 1,2 Millionen Einträgen
    2. Ausführung mehrerer IP-Sets in derselben Reihenfolge auf SQLite und Redis
    3. Pro Multiplikator wurde der Test 5-mal ausgeführt und der Durchschnitt verwendet
  • Für den konkreten Use Case von Wafris zeigte SQLite gegenüber lokalem Redis eine etwa 3-fach höhere Performance
  • Dieses Ergebnis gilt vor Berücksichtigung der Netzwerklatenz
  • Der Test spiegelte absichtlich ein einfaches Setup und reale Nutzungsmängel wider und lässt sich nur schwer zu einem allgemeinen Datenbankvergleich verallgemeinern

Betriebsunterschiede, die im Diagramm nicht sichtbar sind

  • Selbst wenn SQLite im Benchmark deutlich schlechter als Redis abgeschnitten hätte, ging Wafris davon aus, dass es in realen Umgebungen wegen der Netzwerklatenz zu Redis im selben Datacenter oder derselben Region trotzdem schneller sein könnte
  • Auch wenn ein Redis-Server robust mit Clustering oder Sharding aufgebaut ist, bleiben Einschränkungen wie Netzwerkbandbreite, Verbindungszahl und Latenz zwischen Regionen bestehen
  • Da SQLite lokal auf jeder Compute-Instanz vorhanden ist, verschwinden für diesen Wafris-Anwendungsfall die Kosten horizontaler Skalierung nahezu
  • Auch das Onboarding ist mit SQLite einfacher
    • Nutzer können ein gem zur Web-App hinzufügen und es ausführen, ohne überhaupt zu wissen, dass SQLite verwendet wird
  • Für Redis gäbe es ebenfalls viel Spielraum für zusätzliche Optimierung, aber Wafris konnte Nutzer kaum konsistent davon überzeugen, selbst grundlegende Einstellungen wie Cache-Eviction-Policies zu ändern

Erforderliche Strukturänderungen nach dem Wechsel zu SQLite

  • Der Update-Ablauf von Redis-basiertem v1 war einfach
    1. Der Nutzer aktualisiert Regeln im Wafris Hub
    2. Der Wafris Hub aktualisiert die Regeln im Redis-Speicher des Nutzers
  • Mit SQLite konnte der Wafris Hub die SQLite-Datenbank nicht direkt auf den Webserver „pushen“
  • Einige Anbieter von SQLite as a service ermöglichen etwas Ähnliches, passten für Wafris aber wegen Kosten-, Performance- und Sicherheitsaspekten nicht
    • Einzelne Nutzer müssten dies selbst bereitstellen
    • Ports müssten geöffnet werden
    • Eingehende Verbindungen müssten erlaubt sein
  • Der Update-Ablauf des SQLite-basierten v2 sieht so aus
    1. Der Nutzer aktualisiert Regeln im Wafris Hub
    2. Der Client prüft in zeitlichen Abständen oder nach einer bestimmten Anzahl von Requests auf aktualisierte Regeln
    3. Wenn sich die Regeln geändert haben, lädt der Client eine komplett neue SQLite-Datenbank herunter
  • Diese Struktur reduzierte die Verantwortung der Nutzer für Installation und Konfiguration deutlich, und erfolgreiche Installationen des v2-Clients stiegen um etwa das Dreifache

SQLite-Struktur in verteilten Deployments

  • Wenn Rails-Apps in Cloud-Umgebungen wie AWS, Heroku oder Fly mit Auto-Scaling betrieben werden, steigt oft die Zahl der Compute-Instanzen, nicht aber die der Datenbanken
  • Wenn Requests von 100 req/s auf 10.000 req/s steigen, fahren Dynos, Machines oder EC2-Instanzen hoch, aber der Datenbank-Engpass kann bestehen bleiben
  • Nach Beobachtung von Wafris sind Hauptursachen für Ausfälle von Rails-Apps oft nicht echte DDoS-Angriffe, sondern Credential Stuffing oder schlechter Bot-Traffic
    • Solcher Traffic löst Auto-Scaling aus
    • Danach können Datenbankverbindungen erschöpft werden und die App zusammenbrechen
  • Wenn die SQLite-DB auf jede Compute-Instanz synchronisiert wird, bleiben alle Aufrufe auf neuen Instanzen lokal

Der Schreibpfad wird aus dem Client entfernt

  • Die vorherigen Tests berücksichtigten keine Schreiboperationen, und Wafris geht auch nicht davon aus, dass SQLite für alle Rollen geeignet ist
  • Wafris setzt SQLite gezielt für eine leseorientierte Rolle ein und entwirft den Schreibpfad separat neu
  • Der Reporting-Pfad in v2 wurde wie folgt geändert
    • Reporting über eine asynchrone Verbindung zum Wafris Hub
    • Batch-Übertragung von Reporting-Daten
    • vollständige Entfernung von Datenbank-Schreibvorgängen aus dem Client
  • Diese Struktur passt möglicherweise nicht zu den meisten anderen Services, entspricht aber dem Ziel von Wafris nach einfacher Bereitstellung und einem schnellen Client
  • Die SQLite-basierte v2-Architektur hat bereits mehreren Websites geholfen, Angriffe zu überstehen und online zu bleiben, und zugleich den Support-Aufwand und die Reibung für Nutzer reduziert

1 Kommentare

 
GN⁺ 2024-09-26
Kommentare auf Hacker News
  • Das Modell, bei dem jeder Anwendungsserver eine Kopie der SQLite-Datenbankdatei hat und diese in regelmäßigen Abständen komplett austauscht, ist wirklich interessant.
    Hier wird es für Regeln einer Web Application Firewall verwendet, aber es dürfte auch gut zu Feature-Flag-Konfigurationen passen. Feature Flags können pro Request dutzendfach geprüft werden, und oft braucht man Abfragen wie „der Nutzer gehört zu Gruppe A und die IP liegt in Land B“, was lokales SQLite gut bewältigen kann. Bei Feature Flags ist es in der Regel akzeptabel, wenn Updates erst einige Sekunden oder sogar später propagiert werden.

    • Dieses Verfahren ist auch ein Modell, das bei CDNs verwendet wird. Ein globales Konfigurationsfile mit Zertifikaten aller Kunden, HTTP-Routing-Regeln usw. wird als einzelne B-Tree-Datei aktualisiert, und dieses „Bundle“ wird häufig an alle Edge-Standorte verteilt.
      Allerdings habe ich für diesen Zweck noch keine Verwendung von SQLite gesehen; üblicherweise nutzt man DBM-artige Datenbanken wie LMDB oder Kyoto Cabinet.
    • Auch bei Airbnb wurden mit diesem Modell Übersetzungen, Feature Flags, Konfigurationen, Suchindizes usw. verteilt. Statt SQLite wurde Sparkey verwendet, ein von Spotify entwickeltes Key-Value-Dateiformat.
      Anfangs holte ein Cron-Job auf jedem Server die Daten des jeweiligen Dienstes ab; nach dem Umzug zu Kubernetes ließ man über ein DaemonSet und etwas Ähnliches wie Host-Tagging/Taints verschiedene Daten auf die jeweiligen Hosts herunterladen und stellte anschließend sicher, dass Dienste, die diese Daten nutzen, nur auf den entsprechenden Hosts liefen. In Ruby hieß das „hammerspace“: https://github.com/airbnb/hammerspace
    • Das Problem, Feature-Flag-Werte pro Request mehrfach abzufragen, löst man, indem man den Flag-Wert nur einmal holt und im Request-Objekt speichert.
      So zahlt man innerhalb eines Requests die teuren Abfragekosten nicht mehr als einmal, und es besteht auch nicht das Risiko, dass ein Flag während der Request-Verarbeitung aktualisiert wird und die Werte dadurch inkonsistent werden.
    • Nachdem ich während langer Migrationen viel mit Feature Flags gearbeitet habe, bin ich überzeugt, dass der wirklich nützliche Grundbaustein „SQLite, aber mit einem globalen Versionszähler, der bei jeder Änderung erhöht wird, und der Möglichkeit, nur die Änderungen zwischen Versionsnummern abzurufen oder anzuwenden“ wäre.
      Darauf lassen sich verschiedenste Verteilmechanismen setzen, etwa ein Gossip-Netzwerk oder ein explizites baumförmiges Cache-/Distributionsnetz. Jeder mit einer neueren Version kann Updates liefern.
      Die aktuelle DB-Version kann in das Anwendungsartefakt eingebettet werden, nach dem Start der App aber effizient aktualisiert werden. Bei Feature Flags oder Live-Konfigurationen kann man statt auf Code-Level-Defaults auf einen aktuellen Zustand zurückfallen.
      Jeder Client kann den globalen Zähler wie ein ETAG mitsenden und nur die Änderungen bekommen; auch eine erneute Verbindung nach einem kurzen Netzwerkausfall wird dadurch einfach und günstig.
      Wenn man einige Minuten Historie vorhalten kann, lässt sich der Zähler nach Art des W3C-Baggage-Headers an Microservice-Aufrufe anhängen, sodass mehrere Dienste Flags/Konfiguration/Daten auf Basis derselben Versionsnummer auswerten können. Selbst ohne zeitbasierte Auswertungsfunktion ist es für nachträgliches Debugging enorm hilfreich, die Generationsnummer in Logs zu erfassen.
    • SQLite für die Verteilung ist großartig. Die Idee scheint bis zu einem gewissen Grad vom Datasette-Projekt inspiriert zu sein, und später könnte man damit im Bereich Reporting und Datenerkundung immer noch etwas Interessantes machen.
  • Neben der Netzwerklatenz habe ich bei Redis beobachtet, dass die Latenz für Reads/Writes tendenziell nahezu linear mit der Anzahl der abgefragten Keys steigt; die Diagramme im Artikel sehen ähnlich aus.
    Als wir in einer monolithischen App Postgres und Redis je nach Zweck zusammen nutzten, funktionierte das ziemlich gut, aber es war viel zu leicht, neue Features in den gemeinsam genutzten Redis-Cluster zu schieben. Redis ist Single-Threaded, daher kann ein rücksichtsloses Feature, das mehr als 100.000 Keys massenhaft abfragt, auch andere Features ausbremsen.
    Deshalb schlug ich als Leitlinie vor, Redis dann zu verwenden, wenn man jeweils einen einzelnen Key oder eine kleine, feste Anzahl von Keys liest und schreibt, etwa bei Locks oder Rate Limiting für populäre Endpoints, wenn viele zufällige Features Redis nutzen.
    In diesem Fall würde Redis bei einfachen Single-Key-Abfragen (IP-Adresse) glänzen, scheint aber für komplexere Reads wie die Darstellung von Range Queries nicht gut zu passen. Ich verstehe nicht im Detail, warum SQLite im Vergleich zu lokalem Redis so gut abschneidet, daher fand ich das unerwartet und interessant.

    • Redis wurde oft zur Falle, weil Leute seine Stärken und Schwächen leicht missverstehen.
      Redis betrachtet man am besten als Cache mit reichhaltigeren Basisoperationen. Richtig eingesetzt ist es schnell und zuverlässig.
      Wenn man jedoch anfängt, Dinge in Redis zu legen, die nicht gut in das primäre RDBMS passen, entstehen bald Job-Queues, verschiedene Arten von Locks usw.; dann ist es letztlich nur eine Frage der Zeit, bis die Performance über eine Klippe fällt oder es aus anderen Gründen zusammenbricht. Die Wiederherstellung wird normalerweise unsauber und endet leicht damit, dass man einen gewissen Datenverlust akzeptieren muss.
      Diese Entwicklung passiert leicht schrittweise, daher braucht es Disziplin, um sie zu vermeiden. Die Performance von SQLite liegt nicht nur daran, dass Netzwerk-Overhead vermieden wird, sondern auch daran, dass viele die Kosten für Serialisierung/Deserialisierung unterschätzen. Selbst wenn das Redis-Protokoll ziemlich minimal ist, summieren sich die Kosten; bei SQLite läuft vieles letztlich auf Speicherkopien innerhalb des Prozesses hinaus.
  • Vor ein paar Wochen habe ich bei einem internen Hackathon bei Neon einen kleinen Node.js-Server gebaut, der Redis’ Wire Protocol RESP in Postgres-Abfragen übersetzt.
    Das war ein sehr spaßiges Hack-Projekt: https://github.com/btholt/redis-to-postgres

  • Das klingt nach einem speziellen Use Case, für den serverseitiges SQLite ziemlich gut passt, weil die Datenbank read-only ist und keine Replikation braucht.
    Eine andere Alternative wäre, statische Dateien in den Speicher zu laden, aber in diesem Fall scheint die Datenmenge zu groß zu sein, um sie dauerhaft im Speicher zu halten, sodass SQLite eine gute Alternative ist.

    • Ich hoffe, das wurde im Artikel deutlich: Das ist eine hervorragende Lösung für einen bestimmten Use Case, aber kein 1:1-Ersatz für Redis oder Postgres.
    • Wenn man mit statischen Dateien im Speicher beginnt und gerade genug Funktionen hinzufügt, damit es für allgemeine Use Cases funktioniert, landet man am Ende bei einer relationalen Datenbank wie SQLite.
      Der Unterschied ist, dass SQLite sehr robust, performant und außergewöhnlich gut getestet ist.
  • Mich macht die Stelle neugierig, dass es auf der RailsWorld 2023 gegenüber Redis eine Stimmung gegeben habe, als hätte man „Blut gerochen“
    In meiner Karriere habe ich nur eine produktive Rails-App gebaut, und diese App nutzte Redis, daher kenne ich die aktuellen Strömungen nicht gut. Ich frage mich, ob sich das Ökosystem aus geschäftlicher Sicht gerade von Redis abwendet, ob es an der Lizenzänderung liegt oder ob es eher eine Situation nahe an YAGNI ist
    Damals haben wir es zusammen mit Rescue vor allem zum Planen asynchroner Jobs wie Indexierung und Transcoding genutzt, und zu der Zeit wirkte es wie ein ziemlich gutes Tool

    • Es ist ein Stück weit eher YAGNI. Der wichtigste Grund, warum Redis in der Rails-Community so stark genutzt wurde, waren asynchrone Jobs, und das Tool, zu dem die meisten griffen, war Sidekiq
      Auch in der Rails-Community-Umfrage 2024 taucht Redis weiterhin als der in Apps am häufigsten genutzte Datenspeicher auf
      Allerdings nutzen viele Apps Redis zwar, tatsächlich aber nur für Dinge wie Sidekiq, und nicht für Echtzeit-Leaderboards oder Vector-DB-Funktionen. Dadurch ist das tatsächliche Nutzungsmuster etwas unscharf
    • Ich sehe das rein als Frage der Einfachheit
      Heute hat ein Rails-Setup mit einem gewissen Traffic oft Frontend-Server, eine SQL-DB, einen Key-Value-Store (Redis oder Memcached) und dazu einen Cache-Store, der auf diesen Key-Value-Store zeigt. Allerdings unterscheiden sich typische Key-Value-Nutzungen wie das Vorhalten von API-Quoten oder Rate Limits ziemlich stark von Cache-Nutzungsmustern, was lästig ist
      Da die Disk-Performance inzwischen schnell genug ist und auch SQL schneller geworden ist, gibt es eine Bewegung dahin, den Key-Value-Store zu entfernen und traditionelle Key-Value-Zwecke in die SQL-DB zu verlagern, während das Cache-Backend auf Disk liegt. Neue NVMe-Disks sind fast so schnell wie RAM, aber deutlich günstiger, sodass man mehr cachen kann
    • Redis war früher nützlich wie ein sehr cleverer Shared Memory mit eingebauten Funktionen wie Expiration. Mit Clustering konnte man ihn auch über mehrere Maschinen hinweg teilen
      Aber im Zeitalter von Kubernetes und Redis-as-a-service liegt dieser „Shared Memory“ auf einer anderen VM in einem anderen Rack. An dem Punkt hat man Ressourceneffizienz ohnehin schon aufgegeben, also ist es meiner Ansicht nach besser, einfach Dateien aus S3 zu lesen und dorthin zu schreiben
    • Da SSDs heutzutage schnell sind, sollten mehr Leute RocksDB verwenden. Damit kann man einen deutlich größeren Key-Value-Store auf SSDs betreiben
  • Zu der Stelle „Redis ist im Vergleich zu klassischen RDBMS schnell, aber es ist immer noch eine Datenbank, bei der man Verbindungen, Speicher, Prozesse usw. verwalten muss, wodurch der Stack fragiler wird“: Sobald man anfängt, Transaktionen jenseits von Spielzeugniveau zu behandeln, brauchen meiner Ansicht nach alle Datenbanken, ob relational oder nicht relational, ungefähr ein ähnliches Maß an Verwaltung und Wartung
    Auch der Teil mit „schnell“ ist etwas interessant. Wenn man sich nicht um Joins kümmern muss, sind auch Zeileneinfügungen und Abfragen ziemlich schnell

    • Der Grund, warum SQLite in Serverumgebungen außergewöhnlich schnell ist, liegt darin, dass beim Abfragen oder Abrufen von Daten kein Netzwerkaufruf nötig ist
      Wenn man versteht, dass direkt von der lokalen Disk gelesen wird, verlieren Diskussionen über Joins oder Transaktionen stark an Bedeutung. Das ist für sich genommen bereits ein um mehrere Größenordnungen schnellerer Pfad
    • In SQLite gibt es eine VACUUM-Operation, die wie eine Art Garbage Collection wirkt
      Jedes Mal, wenn ich in der Dokumentation lese, wann man VACUUM ausführen sollte, bin ich verwirrt. Als ich zuletzt eine SQLite-basierte Anwendung deployt habe, habe ich es einfach mit einem Counter gelöst und nach vielen Schreibvorgängen VACUUM ausgeführt
    • Der Aussage „alle Datenbanken erfordern ungefähr dasselbe Maß an Verwaltung und Wartung“ stimme ich nicht zu
      Wenn eine Datenbank als eigenständiger Serverprozess betrieben werden muss, wird es zwangsläufig Unterschiede beim Verwaltungs- und Wartungsaufwand geben. Im Extremfall fällt es schwer zu glauben, dass es wirklich keinen Unterschied macht, SQLite oder Oracle zu verwenden
  • Vielleicht ist Redka interessant, eine Neuimplementierung von Redis mit SQLite in Go: https://github.com/nalgeon/redka

    • Es war interessant genug, um einen Umstieg in Betracht zu ziehen, aber die Stelle „Benchmarks zufolge ist Redka um ein Vielfaches langsamer als Redis“ hat mich zögern lassen
      Es ist definitiv ein tolles Projekt, aber damit gibt es kaum einen Anreiz zum Wechseln
  • Ich möchte prüfen, ob ich die Redis-Nutzung richtig verstanden habe
    In v1 lagen WAF und Redis auf demselben Server; wenn ein Client im Admin-Panel eine neue Regel festlegte, wurde diese Regel in das Redis auf demselben Server wie das Admin-Panel geschrieben, und dank des internen Synchronisierungsmechanismus von Redis wurden die Regeln auf die lokalen Redis-Instanzen neben den WAFs weltweit aktualisiert. Ich frage mich, ob danach bei einer neuen Anfrage an irgendeinen WAF der WAF die Anfrage/IP anhand der aktualisierten Redis-Regeln prüfte
    In v2 wurde der Redis-Cluster entfernt, alle WAF-Server haben eine SQLite-DB, und es wurde ein eigener Synchronisierungsmechanismus gebaut, der neue Regeln vom Admin-Panel an jeden WAF+SQLite-Server verteilt; bei einer neuen Anfrage prüft der WAF dann schnell anhand der aktualisierten SQLite-Regeln. Ich frage mich, ob diese Struktur gemeint ist

  • Der beste Satz ist: „SQLite konkurriert nicht mit Client/Server-Datenbanken. SQLite konkurriert mit fopen()