Mehr denn je zielen neue Produkte darauf ab, globalen Einfluss zu nehmen, und das Benutzererlebnis (User Experience) wird schnell zum entscheidenden Faktor dafür, ob sie erfolgreich sind oder nicht. Diese Eigenschaften Ihrer Anwendung können das Benutzererlebnis erheblich beeinflussen.
- Performance & geringe Latenz
- Die Anwendung tut, was Sie erwarten
- Sicherheit
- Funktionen und Benutzeroberfläche
Lassen Sie uns unsere Suche nach dem perfekten Benutzererlebnis beginnen!
1) Performance & geringe Latenz
Andere haben es bereits gesagt; Performance ist User Experience (1, 2). Wenn Sie die Aufmerksamkeit potenzieller Besucher gewonnen haben, kann eine geringe Latenzerhöhung dazu führen, dass Sie diese Aufmerksamkeit wieder verlieren.
2) Die Anwendung tut, was Sie erwarten
Was bedeutet „tut, was Sie erwarten“ überhaupt? Es bedeutet, dass, wenn ich meinen Namen in meiner Anwendung in „Robert“ ändere und die Anwendung neu lade, mein Name Robert und nicht Brecht sein wird. Es scheint wichtig zu sein, dass eine Anwendung diese Garantien liefert, oder?
Ob die Anwendung diese Garantien liefern kann, hängt von der Datenbank ab. Wenn wir niedrige Latenz und Performance anstreben, landen wir im Bereich verteilter Datenbanken, bei denen nur wenige der neueren Datenbanken diese Garantien liefern. Im Bereich verteilter Datenbanken kann es Gefahren geben, es sei denn, wir wählen eine stark (vs. letztendlich) konsistente Datenbank. In dieser Reihe werden wir detailliert darauf eingehen, was dies bedeutet, welche Datenbanken diese Funktion der starken Konsistenz bieten und wie sie Ihnen helfen können, fantastisch schnelle Apps mit minimalem Aufwand zu erstellen.
3) Sicherheit
Sicherheit scheint auf den ersten Blick nicht immer einen Einfluss auf das Benutzererlebnis zu haben. Sobald Benutzer jedoch Sicherheitslücken bemerken, können Beziehungen irreparabel beschädigt werden.
4) Funktionen und Benutzeroberfläche
Beeindruckende Funktionen und eine großartige Benutzeroberfläche haben einen großen Einfluss auf das bewusste und unbewusste Denken. Oft wünschen sich Menschen ein bestimmtes Produkt erst, nachdem sie erlebt haben, wie es aussieht und sich anfühlt.
Wenn eine Datenbank Zeit bei der Einrichtung und Konfiguration spart, können wir uns mit unseren restlichen Bemühungen darauf konzentrieren, beeindruckende Funktionen und eine großartige Benutzeroberfläche zu liefern. Es gibt gute Nachrichten für Sie; heutzutage gibt es Datenbanken, die all das bieten, keine Konfiguration oder Serverbereitstellung erfordern und einfach zu bedienende APIs wie GraphQL direkt out-of-the-box bereitstellen.
Was ist so anders an dieser neuen Generation von Datenbanken? Lassen Sie uns einen Schritt zurücktreten und zeigen, wie die ständige Suche nach niedrigerer Latenz und besserem UX, in Kombination mit Fortschritten in der Datenbankforschung, schließlich zu einer neuen Generation von Datenbanken geführt hat, die ideale Bausteine für moderne Anwendungen sind.
Die Suche nach Verteilung
I. Content Delivery Networks (CDNs)
Wie bereits erwähnt, hat die Performance einen erheblichen Einfluss auf die UX. Es gibt mehrere Möglichkeiten, die Latenz zu verbessern, wobei die offensichtlichste die Optimierung Ihres Anwendungscodes ist. Sobald Ihr Anwendungscode ziemlich optimal ist, bleiben die Netzwerklatenz und die Lese-/Schreibleistung der Datenbank oft der Engpass. Um unsere Anforderung an geringe Latenz zu erfüllen, müssen wir sicherstellen, dass unsere Daten so nah wie möglich am Client sind, indem wir die Daten global verteilen. Wir können die zweite Anforderung (Lese-/Schreibleistung) erfüllen, indem wir mehrere Maschinen zusammenarbeiten lassen oder anders ausgedrückt, Daten replizieren.
Verteilung führt zu besserer Performance und folglich zu guter Benutzererfahrung. Wir haben bereits die weit verbreitete Nutzung einer Verteilungslösung gesehen, die die Bereitstellung statischer Daten beschleunigt; sie heißt Content Delivery Network (CDN). CDNs werden von der Jamstack-Community hoch geschätzt, um die Latenz ihrer Anwendungen zu reduzieren. Sie verwenden typischerweise Frameworks und Tools wie Next.js/Now, Gatsby und Netlify, um Frontend-Code von React/Angular/Vue in statische Websites zu kompilieren, damit sie diese von einem CDN ausliefern können.

Leider sind CDNs nicht für jeden Anwendungsfall ausreichend, da wir nicht für alle Anwendungen auf statisch generierte HTML-Seiten angewiesen sein können. Es gibt viele Arten von hochdynamischen Anwendungen, bei denen man nicht alles statisch generieren kann. Zum Beispiel:
- Anwendungen, die Echtzeit-Updates für sofortige Kommunikation zwischen Benutzern erfordern (z. B. Chat-Anwendungen, kollaboratives Zeichnen oder Schreiben, Spiele).
- Anwendungen, die Daten in vielen verschiedenen Formen darstellen, indem sie Daten filtern, aggregieren, sortieren und anderweitig so stark manipulieren, dass man nicht alles im Voraus generieren kann.
II. Verteilte Datenbanken
Im Allgemeinen benötigt eine hochdynamische Anwendung eine verteilte Datenbank, um die Performance zu verbessern. Genau wie ein CDN strebt eine verteilte Datenbank danach, ein globales Netzwerk anstelle eines einzelnen Knotens zu werden. Im Wesentlichen wollen wir von einem Szenario mit einem einzelnen Datenbankknoten...

...zu einem Szenario übergehen, in dem die Datenbank zu einem Netzwerk wird. Wenn sich ein Benutzer von einem bestimmten Kontinent aus verbindet, wird er automatisch zur nächstgelegenen Datenbank weitergeleitet. Dies führt zu geringeren Latenzen und zufriedeneren Endbenutzern.

Wenn Datenbanken Angestellte wären, die am Telefon warten, würde der Datenbankangestellte Ihnen mitteilen, dass ein Mitarbeiter in der Nähe ist, und den Anruf weiterleiten. Glücklicherweise leiten verteilte Datenbanken uns automatisch zum nächstgelegenen Datenbankmitarbeiter weiter, sodass wir uns nie um den Datenbankmitarbeiter auf dem anderen Kontinent kümmern müssen.

Verteilte Datenbanken sind Multi-Region, und Sie werden immer zum nächstgelegenen Knoten weitergeleitet.
Neben der Latenz bieten verteilte Datenbanken auch einen zweiten und einen dritten Vorteil. Der zweite ist die Redundanz, was bedeutet, dass Ihre Daten nicht verloren gehen, wenn einer der Datenbankstandorte im Netzwerk durch einen Godzilla-Angriff vollständig ausgelöscht würde, da andere Knoten noch Duplikate Ihrer Daten haben.


Und nicht zuletzt ist der dritte Vorteil der Nutzung einer verteilten Datenbank die Skalierung. Eine Datenbank, die auf einem Server läuft, kann schnell zum Engpass Ihrer Anwendung werden. Im Gegensatz dazu replizieren verteilte Datenbanken Daten über mehrere Server und können sich automatisch an die Anforderungen der Anwendungen anpassen. Bei einigen fortschrittlichen verteilten Datenbanken wird dieser Aspekt vollständig für Sie erledigt. Diese Datenbanken werden als „serverlos“ bezeichnet, was bedeutet, dass Sie nicht einmal konfigurieren müssen, wann die Datenbank hoch- und runterskaliert werden soll, und Sie nur für die Nutzung Ihrer Anwendung bezahlen, nichts mehr.
Die Verteilung dynamischer Daten bringt uns in den Bereich der verteilten Datenbanken. Wie bereits erwähnt, kann es Gefahren geben. Im Gegensatz zu CDNs sind die Daten hochdynamisch; die Daten können sich schnell ändern und gefiltert und sortiert werden, was zusätzliche Komplexität mit sich bringt. Die Datenbankwelt untersuchte verschiedene Ansätze, um dies zu erreichen. Frühe Ansätze mussten Kompromisse eingehen, um die gewünschte Performance und Skalierbarkeit zu erzielen. Sehen wir uns an, wie sich die Suche nach Verteilung entwickelt hat.

Der traditionelle Ansatz verteilter Datenbanken
Eine logische Wahl war es, auf traditionellen Datenbanken (MySQL, PostgreSQL, SQL Server) aufzubauen, da bereits so viel Aufwand in sie gesteckt wurde. Traditionelle Datenbanken wurden jedoch nicht für die Verteilung entwickelt und verfolgten daher einen eher einfachen Ansatz zur Verteilung. Der typische Ansatz zur Skalierung von Lesevorgängen war die Verwendung von Read Replicas. Ein Read Replica ist lediglich eine Kopie Ihrer Daten, von der Sie lesen, aber nicht schreiben können. Eine solche Kopie (oder ein Replikat) entlastet den Knoten, der die Originaldaten enthält, von Abfragen. Dieser Mechanismus ist sehr einfach, da die Daten inkrementell zu den Replikaten kopiert werden, sobald sie eingehen.
Aufgrund dieses relativ einfachen Ansatzes sind die Daten eines Replikats immer älter als die Originaldaten. Wenn Sie die Daten zu einem bestimmten Zeitpunkt von einem Replikatknoten lesen, erhalten Sie möglicherweise einen älteren Wert als wenn Sie vom primären Knoten lesen. Dies wird als „stale read“ bezeichnet. Programmierer, die traditionelle Datenbanken verwenden, müssen sich dieser Möglichkeit bewusst sein und mit dieser Einschränkung im Hinterkopf programmieren. Erinnern Sie sich an das Beispiel vom Anfang, bei dem wir einen Wert schreiben und ihn wieder lesen? Bei traditionellen Datenbankreplikaten können Sie nicht erwarten, das zu lesen, was Sie schreiben.
Sie könnten das Benutzererlebnis leicht verbessern, indem Sie die Ergebnisse von Schreibvorgängen auf dem Frontend optimistisch anwenden, bevor alle Replikate über die Schreibvorgänge informiert sind. Ein Neuladen der Webseite kann die Benutzeroberfläche jedoch in einen früheren Zustand zurückversetzen, wenn die Aktualisierung das Replikat noch nicht erreicht hat. Der Benutzer würde dann denken, dass seine Änderungen nie gespeichert wurden.

Die erste Generation verteilter Datenbanken
Beim Replikationsansatz traditioneller Datenbanken ist der offensichtliche Engpass, dass alle Schreibvorgänge an denselben Knoten gehen. Die Maschine kann hochskaliert werden, stößt aber unweigerlich an eine Grenze. Wenn Ihre App an Popularität gewinnt und die Schreibvorgänge zunehmen, wird die Datenbank nicht mehr schnell genug sein, um neue Daten zu akzeptieren. Um horizontal für Lese- und Schreibvorgänge zu skalieren, wurden verteilte Datenbanken erfunden. Eine verteilte Datenbank hält ebenfalls mehrere Kopien der Daten, aber Sie können auf jede dieser Kopien schreiben. Da Sie Daten über jeden Knoten aktualisieren, müssen alle Knoten miteinander kommunizieren und sich gegenseitig über neue Daten informieren. Mit anderen Worten, es ist keine Einbahnstraße mehr wie im traditionellen System.

Diese Art von Datenbanken kann jedoch immer noch unter den oben genannten „stale reads“ leiden und viele andere potenzielle Probleme im Zusammenhang mit Schreibvorgängen einführen. Ob sie unter diesen Problemen leiden, hängt von ihrer Entscheidung in Bezug auf Verfügbarkeit und Konsistenz ab.
Diese erste Generation verteilter Datenbanken wurde oft als „NoSQL-Bewegung“ bezeichnet, ein Name, der von Datenbanken wie MongoDB und Neo4j beeinflusst wurde, die ebenfalls alternative Sprachen zu SQL und unterschiedliche Modellierungsstrategien (Dokumente oder Graphen anstelle von Tabellen) boten. NoSQL-Datenbanken verfügten oft nicht über typische traditionelle Datenbankfunktionen wie Constraints und Joins. Im Laufe der Zeit stellte sich heraus, dass dieser Name schrecklich war, da viele Datenbanken, die als NoSQL galten, eine Form von SQL anboten. Es entstanden mehrere Interpretationen, die behaupteten, dass NoSQL-Datenbanken
- keine SQL als Abfragesprache anbieten.
- nicht nur SQL anbieten (NoSQL = Not Only SQL)
- keine typischen traditionellen Funktionen wie Joins, Constraints, ACID-Garantien anbieten.
- ihre Daten anders modellieren (Graph, Dokument oder Temporal-Modell).
Einige der neueren Datenbanken, die nicht-relational waren, aber SQL anboten, wurden dann als „NewSQL“ bezeichnet, um Verwechslungen zu vermeiden.
Falsche Interpretationen des CAP-Theorems
Die erste Generation von Datenbanken war stark vom CAP-Theorem inspiriert, das besagt, dass man während einer Netzwerkpartition weder Konsistenz noch Verfügbarkeit gleichzeitig haben kann. Eine Netzwerkpartition tritt im Wesentlichen auf, wenn etwas passiert, sodass zwei Knoten nicht mehr über neue Daten sprechen können, und kann aus vielen Gründen auftreten (z. B. manchmal knabbern Haie an Googles Kabeln). Konsistenz bedeutet, dass die Daten in Ihrer Datenbank immer korrekt sind, aber nicht unbedingt für Ihre Anwendung verfügbar sind. Verfügbarkeit bedeutet, dass Ihre Datenbank immer online ist und Ihre Anwendung immer auf diese Daten zugreifen kann, garantiert aber nicht, dass die Daten korrekt oder auf mehreren Knoten gleich sind. Wir sprechen im Allgemeinen von hoher Verfügbarkeit, da es keine 100%ige Verfügbarkeit gibt. Availability wird in Ziffern von 9 (z. B. 99,9999 % Verfügbarkeit) angegeben, da immer die Möglichkeit besteht, dass eine Reihe von Ereignissen zu Ausfallzeiten führt.

Aber was passiert, wenn es keine Netzwerkpartition gibt? Datenbankanbieter haben das CAP-Theorem etwas zu allgemein genommen und entweder potenzielle Datenverluste in Kauf genommen oder waren verfügbar, unabhängig davon, ob eine Netzwerkpartition vorliegt oder nicht. Während das CAP-Theorem ein guter Anfang war, betonte es nicht, dass es möglich ist, hoch verfügbar und konsistent zu sein, wenn keine Netzwerkpartition vorliegt. Meistens gibt es keine Netzwerkpartitionen, daher war es sinnvoll, diesen Fall zu beschreiben, indem das CAP-Theorem zum PACELC-Theorem erweitert wurde. Der Hauptunterschied sind die drei letzten Buchstaben (ELC), die für Else Latency Consistency stehen. Dieses Theorem besagt, dass die Datenbank im Falle einer Netzwerkpartition die Latenz und die Konsistenz ausbalancieren muss.

Einfach ausgedrückt: Wenn keine Netzwerkpartition vorliegt, steigt die Latenz mit zunehmenden Konsistenzgarantien. Wir werden jedoch sehen, dass die Realität noch subtiler ist.
Wie hängt das mit dem Benutzererlebnis zusammen?
Betrachten wir ein Beispiel dafür, wie der Verzicht auf Konsistenz das Benutzererlebnis beeinträchtigen kann. Stellen Sie sich eine Anwendung vor, die Ihnen eine benutzerfreundliche Oberfläche zum Zusammenstellen von Teams von Personen bietet; Sie ziehen Personen per Drag & Drop in verschiedene Teams.

Sobald Sie eine Person in ein Team ziehen, wird eine Aktualisierung ausgelöst, um dieses Team zu aktualisieren. Wenn die Datenbank nicht garantiert, dass Ihre Anwendung das Ergebnis dieser Aktualisierung sofort lesen kann, muss die Benutzeroberfläche diese Änderungen optimistisch anwenden. In diesem Fall können schlimme Dinge passieren:
- Der Benutzer aktualisiert die Seite und sieht seine Aktualisierung nicht mehr und denkt, dass seine Aktualisierung verloren gegangen ist. Wenn er erneut aktualisiert, ist sie plötzlich wieder da.
- Die Datenbank hat die Aktualisierung aufgrund eines Konflikts mit einer anderen Aktualisierung nicht erfolgreich gespeichert. In diesem Fall kann die Aktualisierung abgebrochen werden, und der Benutzer erfährt es nie. Er bemerkt möglicherweise nur, dass seine Änderungen verschwunden sind, wenn er das nächste Mal neu lädt.
Dieser Kompromiss zwischen Konsistenz und Latenz hat viele heftige Diskussionen zwischen Front-End- und Back-End-Entwicklern ausgelöst. Die erste Gruppe wollte ein großartiges UX, bei dem Benutzer Feedback erhalten, wenn sie Aktionen ausführen, und zu 100 % sicher sein können, dass die Ergebnisse ihrer Aktionen konsistent gespeichert werden, sobald sie dieses Feedback erhalten und darauf reagieren. Die zweite Gruppe wollte ein skalierbares und performantes Backend aufbauen und sah keinen anderen Weg, als die oben genannten UX-Anforderungen zu opfern, um dies zu liefern.
Beide Gruppen hatten berechtigte Argumente, aber es gab keine Wunderwaffe, um beide zufrieden zu stellen. Als die Transaktionen zunahmen und die Datenbank zum Engpass wurde, blieb ihnen nichts anderes übrig, als entweder traditionelle Datenbankreplikation oder eine verteilte Datenbank zu wählen, die die starke Konsistenz zugunsten von etwas namens „eventual consistency“ opferte. Bei „eventual consistency“ wird eine Aktualisierung der Datenbank irgendwann auf allen Maschinen angewendet, aber es gibt keine Garantie, dass die nächste Transaktion den aktualisierten Wert lesen kann. Mit anderen Worten, wenn ich meinen Namen in „Robert“ ändere, gibt es keine Garantie, dass ich tatsächlich „Robert“ erhalte, wenn ich meinen Namen unmittelbar nach der Aktualisierung abfrage.
Consistency Tax
Um mit „eventual consistency“ umzugehen, müssen Entwickler sich der Grenzen einer solchen Datenbank bewusst sein und viel zusätzliche Arbeit leisten. Programmierer greifen oft zu Hacks bei der Benutzererfahrung, um die Datenbankbeschränkungen zu verbergen, und Backends müssen viele zusätzliche Code-Ebenen schreiben, um verschiedene Fehlerszenarien zu bewältigen. Das Finden und Entwickeln kreativer Lösungen für diese Einschränkungen hat die Art und Weise, wie Front- und Back-End-Entwickler ihre Arbeit verrichtet haben, tiefgreifend beeinflusst, die technische Komplexität erheblich erhöht, ohne dennoch ein ideales Benutzererlebnis zu bieten.
Wir können diese zusätzliche Arbeit, die erforderlich ist, um die Datenkorrektheit zu gewährleisten, als eine „Steuer“ betrachten, die ein Anwendungsentwickler zahlen muss, um gute Benutzererlebnisse zu liefern. Das ist die Steuer für die Verwendung eines Softwaresystems, das keine Konsistenzgarantien bietet, die in heutigen Web-Scale-Umgebungen mit hoher Nebenläufigkeit Bestand haben. Wir nennen das die Konsistenzsteuer (Consistency Tax).
Glücklicherweise hat sich eine neue Generation von Datenbanken entwickelt, die keine Konsistenzsteuer erfordert und ohne Einbußen bei der Konsistenz skalieren kann!
Die zweite Generation verteilter Datenbanken
Eine zweite Generation verteilter Datenbanken ist entstanden, um starke (anstelle von „eventual“) Konsistenz zu bieten. Diese Datenbanken skalieren gut, verlieren keine Daten und geben keine veralteten Daten zurück. Mit anderen Worten, sie tun, was Sie erwarten, und es ist nicht mehr erforderlich, sich über Einschränkungen zu informieren oder die Konsistenzsteuer zu zahlen. Wenn Sie einen Wert aktualisieren, spiegelt der nächste Lesevorgang immer den aktualisierten Wert wider, und verschiedene Aktualisierungen werden in derselben zeitlichen Reihenfolge angewendet, wie sie geschrieben wurden. FaunaDB, Spanner und FoundationDB sind zum Zeitpunkt der Erstellung die einzigen Datenbanken, die starke Konsistenz ohne Einschränkungen (auch als Strict serializability bezeichnet) bieten.
Das PACELC-Theorem überarbeitet

Die zweite Generation verteilter Datenbanken hat etwas erreicht, das zuvor als unmöglich galt; sie bevorzugen Konsistenz und liefern dennoch geringe Latenzen. Dies wurde durch intelligente Synchronisationsmechanismen wie Calvin, Spanner und Percolator möglich, die wir in Artikel 4 dieser Serie im Detail besprechen werden. Während ältere Datenbanken immer noch Schwierigkeiten haben, hohe Konsistenzgarantien bei geringeren Latenzen zu liefern, leiden Datenbanken, die auf diesen neuen intelligenten Algorithmen basieren, nicht unter solchen Einschränkungen.
Datenbankdesigns beeinflussen die erreichbare Latenz bei hoher Konsistenz stark.
Da diese neuen Algorithmen es Datenbanken ermöglichen, sowohl starke Konsistenz als auch geringe Latenzen zu bieten, gibt es normalerweise keinen guten Grund, auf Konsistenz zu verzichten (zumindest in Abwesenheit einer Netzwerkpartition). Der einzige Fall, in dem Sie dies tun würden, ist, wenn extrem geringe Schreiblatenz das Einzige ist, was wirklich zählt, und Sie bereit sind, Daten zu verlieren, um dies zu erreichen.

Sind diese Datenbanken immer noch NoSQL?
Es ist nicht mehr einfach, diese neue Generation verteilter Datenbanken zu kategorisieren. Es werden immer noch viele Anstrengungen unternommen (1, 2), um zu erklären, was NoSQL bedeutet, aber keine davon ist mehr wirklich sinnvoll, da sich NoSQL- und SQL-Datenbanken aufeinander zubewegen. Neue verteilte Datenbanken leihen sich von verschiedenen Datenmodellen (Dokument, Graph, Relational, Temporal), und einige von ihnen bieten ACID-Garantien oder unterstützen sogar SQL. Sie haben immer noch eines mit NoSQL gemeinsam: Sie wurden entwickelt, um die Einschränkungen traditioneller Datenbanken zu lösen. Ein Wort wird niemals ausreichen, um zu beschreiben, wie eine Datenbank funktioniert. In Zukunft wäre es sinnvoller, verteilte Datenbanken zu beschreiben, indem diese Fragen beantwortet werden:
- Ist sie stark konsistent?
- Basiert die Verteilung auf Read-Replicas oder ist sie wirklich verteilt?
- Von welchen Datenmodellen leiht sie sich?
- Wie ausdrucksstark ist die Abfragesprache und was sind ihre Einschränkungen?
Fazit
Wir haben erklärt, wie Anwendungen nun von einer neuen Generation global verteilter Datenbanken profitieren können, die dynamische Daten vom nächstgelegenen Standort im CDN-ähnlichen Stil bereitstellen können. Wir haben kurz die Geschichte verteilter Datenbanken durchlaufen und gesehen, dass es kein reibungsloser Weg war. Viele Datenbanken der ersten Generation wurden entwickelt, und ihre Konsistenzentscheidungen – die hauptsächlich vom CAP-Theorem angetrieben wurden – erforderten, dass wir mehr Code schrieben, während die Benutzererfahrung weiterhin beeinträchtigt wurde. Erst kürzlich hat die Datenbank-Community Algorithmen entwickelt, die es verteilten Datenbanken ermöglichen, geringe Latenz mit starker Konsistenz zu kombinieren. Eine neue Ära bricht an, eine Zeit, in der wir keine Kompromisse mehr zwischen Datenzugriff und Konsistenz eingehen müssen!
An dieser Stelle möchten Sie wahrscheinlich konkrete Beispiele für die potenziellen Fallstricke von Datenbanken mit „eventual consistency“ sehen. Im nächsten Artikel dieser Serie werden wir genau das behandeln. Bleiben Sie dran für diese kommenden Artikel.
Sehr aufschlussreich! Ich kann es kaum erwarten, den Rest der Artikel zu lesen!
Hallo Gust, danke für das Feedback! Wir freuen uns, dass es Ihnen gefällt.
Die Beiträge werden wöchentlich veröffentlicht, sodass der nächste am 12. März eintreffen sollte.
Wenn Sie Feedback oder Gedanken haben, lassen Sie es uns auf Twitter wissen (tag @fauna) / Slack (http://community.fauna.com/).