
von St. Pete
Hallo zusammen! Ich bin Mons Anderson, Plattformarchitekt Ich erzähle Ihnen, wie wir unseren S3-Speicher aufgebaut haben, wie er funktioniert, welche Lösungen sich als erfolgreich erwiesen haben und welche sich gelohnt hätten, zu ändern, wenn wir das gleiche Projekt jetzt von vorne beginnen würden.
Der Artikel wurde auf der Grundlage eines Berichts unter erstellt von Mail.ru Cloud Solutions & Tarantool. In dem Artikel werden wir sprechen:
- wie der Mail.ru-Speicher entworfen wurde, worauf wir den S3-Speicher bauten;
- was wir hinzugefügt haben, um Mail.ru Cloud Storage zu machen;
- wie das Objektspeichermodell funktioniert und welche Schritte unternommen wurden, um in die Produktion zu gelangen;
- über Verbesserungen am Kampfsystem: Failover und Skalierung;
- wie wir Sharding und Resharding implementiert haben;
- und auch über die Arbeit mit SSL-Zertifikaten.
Wenn Sie nicht lesen möchten, können Sie dies tun .
Wie der Mail.ru-Speicher entworfen wurde, auf dem wir den S3-Speicher aufgebaut haben
Die Entwicklung unseres S3 begann auf dem Cloud-Speicher von Mail.ru, daher lohnt es sich zunächst zu erklären, wie es funktioniert und was es kann.
Der Cloud-Speicher von Mail.ru besteht aus Servern mit Festplatten. Im Durchschnitt verfügt ein moderner Speicherserver über 36 Festplatten mit jeweils 12–14 Terabyte. Früher waren Festplatten kleiner, aber in drei Jahren ist das Festplattenvolumen gewachsen und beträgt heute fast ein halbes Petabyte an Rohdaten.
Festplatten verschiedener Speicherserver werden zu sogenannten „Paaren“ zusammengefasst. Ein Paar ist eine einzelne Dateispeichereinheit. Im Wesentlichen handelt es sich dabei um eine Festplatte, die auf einer bestimmten Partition unter einem bestimmten Pfad bereitgestellt wird und auf der sich durch Hashes identifizierte Dateien befinden können.
Das Paar ist ein historischer Name, der bis heute überlebt hat, obwohl ein Paar heutzutage nicht unbedingt nur zwei Scheiben hat. Es können drei Festplatten vorhanden sein, es können aber auch verschiedene Hybridspeicher vorhanden sein, zum Beispiel 3/2.

Paare sind Einheiten der Objektspeicherung
Alle Paare werden in PairDB gespeichert – einer Tarantool-basierten Anwendung. Alle Datenbanken in unserem Repository, beginnend mit den allerersten, sind Tarantool; wir verwenden keine anderen Datenbanken.
PairDB speichert alle Paare, ihre Zustände, freien Speicherplatz, Fehlermöglichkeiten und letzte Fehler. Sie kann auch selbst zu Kursen gehen, deren Status aktualisieren und prüfen, ob sie funktionieren oder nicht. Das heißt, PairDB ist eine allgemeine Momentaufnahme des Zustands aller Festplatten in unserem System.

Pair DB: Paarzustandsdatenbank
Dateien werden paarweise gespeichert und um zu wissen, welches Paar welche Datei enthält, benötigen Sie eine weitere Datenbank – FileDB. Es speichert Mapping, Matching-Definition: Datei so und so wird auf Paar so und so gespeichert, sowie eine kleine Anzahl notwendiger Attribute.
Datei-DB: der Speicherort der Datei
Ein weiterer wichtiger Link ist der Nylon-Dienst, ein Router für die Arbeit mit Datenbanken. Es handelt sich um einen einzigen Einstiegspunkt und ermöglicht Ihnen die Arbeit über eine einzige Schnittstelle sowohl mit PairDB als auch mit FileDB. Dies ist ein zustandsloser Dienst, der Anfragen ausgleicht, versteht, zu welchem FileDB-Shard man gehen soll, und weiß, welche Paare aktiv sind und welche nicht.

Nylon: Router für die Arbeit mit Datenbanken
Sie müssen den Inhalt auch irgendwie im Speicher speichern. Dafür gibt es einen Dienst – Streamer. Es bietet zwei HTTP-Methoden: die PUT-Methode, um Inhalte in den Speicher zu verschieben, und die GET-Methode, um sie von dort abzurufen. HTTP ist ein recht beliebtes und praktisches Protokoll für die Datenübertragung.
Wenn wir Streamer kontaktieren, greift dieser über Nylon auf PairDB zu, findet heraus, auf welches Paar eine Datei hochgeladen werden kann, und überträgt die Daten dann per WebDAV an dieses Paar.
Im Wesentlichen besteht jeder Speicherserver aus Nginx und Festplatten, die entlang bestimmter Pfade bereitgestellt werden. Wir können eine Datei vom Streamer in den Speicher hochladen, löschen, umbenennen oder auf Integrität prüfen. Das heißt, es handelt sich um eine praktische Schnittstelle für die Interaktion mit dem Speicher auf niedriger Ebene.

Streamer: Speichereinstiegspunkt
Was wir hinzugefügt haben, um S3-Speicher zu erstellen
Deshalb haben wir uns das allgemeine grundlegende Speicherdesign angesehen, als wir kurz vor der Einführung des S3-Speichers standen. Mit der PUT-Methode könnten wir dort beliebige Inhalte platzieren und einen Hash als Kennung für diese Daten erhalten. Mit dieser ID könnten Sie später vorbeikommen und die Originaldatei abholen. Dies reicht jedoch nicht aus, um S3 zu implementieren. Das S3-Protokoll verfügt zusätzlich zum eigentlichen Speichern von Objekten über Folgendes:
- Speicherung von Metadaten – zusätzliche Eigenschaften von Objekten;
- Organisation des Zugriffs auf Objekte über HTTP;
- Gruppieren von Objekten in Sammlungen – Buckets;
- HTTP-S3-Endpunkt. S3 organisiert Daten in spezifischen Strukturen, sogenannten Buckets, die jeweils einen Einstiegspunkt zum Speichern von Dateien bieten.
Um diese Logik zu implementieren, war ein separater Dienst erforderlich. Außerdem wollte ich sofort eine Architektur für das weitere Wachstum des Dienstes mit linearer Skalierbarkeit bereitstellen.
Erste Komponenten
Ein Daemon, der die S3-API implementiert. Dabei handelt es sich um die Standard-S3-API von Amazon, die XML für Metadaten unterstützt und das direkte Streamen von Inhalten ermöglicht. Wir mussten nichts erfinden, alles wurde beschrieben und dokumentiert.
Wir haben auch Nginx vor dem Dienst installiert. Wir haben es für die SSL-Terminierung, den Lastausgleich und auch für einige Logik in Lua (Metriken, Protokollierung und Ablaufverfolgung) verwendet.
Wir haben Tarantool auch zum Speichern von S3-Metadaten ausgewählt. In der ersten Version suchte der S3-Daemon nach Metadaten in dieser Datenbank; der Inhalt selbst wurde über Streamer in einem großen Speicher gespeichert.

Nginx + S3 API + Metadaten
Objektspeichermodell
Mal sehen, wie S3 funktioniert. Der Benutzer kann einen Bucket erstellen – eine Sammlung von Objekten. Der Bucket wird über den Hostnamen angesprochen und ist eine Subdomäne des Dienstes. Innerhalb eines Buckets kann der Benutzer Objekte erstellen. Der Objektbezeichner ist die URL. Der Inhalt des Objekts ist ein Blob, ein Array binärer Daten, die wir im Speicher speichern. Das Objekt verfügt außerdem über Attribute: Name – dieselbe URL, ACL (Zugriffskontrollliste), andere zusätzliche oder beliebige Attribute – all dies wird in Metadaten gespeichert.
Ein normalisiertes Diagramm dieser Daten könnte wie folgt aussehen: Es gibt Projekte, die Buckets besitzen, die Objekte besitzen, und Objekte können zusammengesetzt sein. Da ein Objekt unter anderem in Teilen geladen werden kann, gibt es zwei Hilfstabellen zum Laden: Uploads und Chunks. Projekte verfügen außerdem über Zugangsdaten und Abrechnung.

Datenschema
Da wir einen B2B-Dienst mit kostenpflichtigem Zugang anboten, war für dieses Schema eine Abrechnung erforderlich.
Wir haben auch den Abrechnungsservice auf Tarantool implementiert.

Verbesserungen am S3-Speicher: Schritte in Richtung Produktion
Wir haben bereits ein funktionierendes Modell erstellt, das verwendet werden kann: Objekte und Metadaten wurden gespeichert, aber es fehlten einige Punkte, um in die Produktion zu gehen.
Erstens das System der Tarifbegrenzungen. Wenn wir den Dienst ohne ihn starten würden, könnten wir während der Spitzenlast unvorhersehbar jeden Teil des Systems überlasten. Die Ratenbegrenzung sollte folgendermaßen funktionieren: Jede S3-Anfrage kommt an einen bestimmten Host, dieser Host ist die Bucket-ID und der Bucket gehört zu einem Client. Wir müssen eine Funktion aus dem Bucket definieren, die es uns ermöglicht, das Ratenlimit zu berechnen.
Darüber hinaus muss das Ratenbegrenzungssystem leistungsstark genug sein, um die Belastung von S3 zu bewältigen.
Hier haben wir erneut Tarantool verwendet. Rate Limits ist ein Cluster aus 21 Instanzen. Die Instanzen sind in Gruppen unterteilt, in drei physische Knoten unterteilt und zu einem großen topologischen Cluster zusammengefasst. Konfigurationsänderungen werden dadurch automatisch weitergegeben: Ratenlimits, Standardeinstellungen und Konfiguration werden festgelegt. Jeder Bucket wird von genau einer Instanz bedient. Wenn eine Anfrage für einen bestimmten Bucket eintrifft, wird die für diesen Bucket verantwortliche Instanz berechnet. Innerhalb dieses Knotens wird die aktuelle Anforderungsrate mithilfe eines Algorithmus ähnlich dem Token Bucket berechnet. Als nächstes sagt das Ratenbegrenzungssystem basierend auf den aktuellen Lastindikatoren und Eigenschaften, die für einen bestimmten Bucket festgelegt sind, ob die Anfrage erfüllt werden kann oder nicht. Die Grenzwertprüfung wird in der frühesten Phase einer S3-Anfrage durchgeführt und schützt so alle anderen Elemente des Systems vor übermäßiger Belastung.

Außerdem ist es unter Last recht schwierig, auf den Cache zu verzichten. S3 impliziert den wiederholten Zugriff auf dieselben Objekte, es handelt sich also um einen Hot Storage. Im Normalfall wird der Zugriff auf eine einzelne Datei über eine vollständige Kette bereitgestellt: Streamer, FileDB, PairDB, Storage. Bei mehrmaligem Zugriff auf eine Datei optimieren wir den Zugriff auf diesen Inhalt jedoch mithilfe eines lokalen Caches.
Der Cache ist mehrschichtig und wird mithilfe von Nginx-, lokalen, SSD- und RAM-Festplatten implementiert. Hier haben wir Tarantool nicht verwendet, da es bequemer ist, Objekte aus dem Dateisystem bereitzustellen, sodass wir Cache-Tiering durchführen können. Darüber hinaus haben wir große Objekte mit einer maximalen Größe von 32 Gigabyte und Tarantool kann nur kleine Objekte zwischenspeichern.

Dieses erste System, mit dem wir auf den Markt kamen, verfügte über eine gewisse berechnete Kapazität, die für die Recherche und das Verständnis des Produkts ausreichte, damit es funktionieren würde.
Verbesserungen am Kampfsystem: Failover und Skalierung
Das System war bereits in Betrieb, aber am Anfang haben wir etwas übersehen – wir mussten ein Failover und eine Skalierung hinzufügen.
Unser S3-Daemon hat Metadaten mithilfe des Tarantool-Protokolls abgerufen. Anstelle der ursprünglichen Datenbank haben wir Tarantool installiert, das als Proxy-Router für Metadatenanfragen fungierte. Aus Sicht der Anwendung, die die API implementiert, hat sich nichts geändert – sie greift weiterhin über das Tarantool-Protokoll auf die Datenbank zu, der Router konnte jedoch ein aktives Failover bereitstellen. Das heißt, wir konnten die Verfügbarkeit eines Knotens überprüfen, bei Umschaltungen und Ausfällen pausieren usw. Die Anwendung selbst haben wir jedoch nicht verändert.

Erfahren Sie mehr darüber, wie wir Sharding implementiert haben
Das nächste Problem, über das wir uns Sorgen machen mussten, war das Sharding. Das System wuchs, die Anzahl der Objekte wuchs und es galt, Möglichkeiten für weiteres Wachstum bereitzustellen.
Kehren wir zum Datenschema zurück: Es gibt Projekte, es gibt Buckets, Kredite und Abrechnungen. Hierbei handelt es sich um Objekte, die mit hoher Wahrscheinlichkeit in absehbarer Zeit weder im Umfang noch in Bezug auf Anfragen über die Grenzen einer Instanz hinauswachsen werden. Das bedeutet, dass es keinen Sinn macht, sie zu fragmentieren, und wir haben sie in eine separate Instanz verschoben, die nicht fragmentiert bleibt. Dies ermöglicht eine konsistentere Verwaltung von Projekten und Buckets, da es einen einzigen, nicht gesicherten Punkt gibt.

Auch im Schema gibt es Objekte, die linear wachsen – anfangs waren es Hunderttausende davon, heute wird ihre Zahl in mehreren Milliarden gemessen. Solche Objekte mussten zusammen mit ihren Teilen in einen Scherbenhaufen gebracht werden.

Wir haben das Schema aufgeteilt, aber Objekte müssen mit Buckets funktionieren: Ein Objekt gehört immer zu einem bestimmten Bucket, und eine ACL funktioniert für den Bucket. Daher behalten wir für jeden Objekt-Shard eine Schattenkopie jedes Buckets. Darüber hinaus müssen Sie beim Ändern von Objekten und beim Ausführen von Abfragen das Volumen zählen, um die Abrechnung durchzuführen, sodass jeder Shard über Abrechnungszähler verfügt.
Wir haben außerdem mehrere weitere Tabellen und Komponenten hinzugefügt:
- Papierkorb zum Zerstören alter Projekte, die gelöscht oder eingefroren wurden;
- eine Warteschlange für Hintergrundaufgaben, d. h. der Hauptspeicher kann Hintergrundaufgaben ausführen, die im Cluster ausgeführt werden müssen;
- Lebenszyklusunterstützung – ein Mechanismus, der es Ihnen ermöglicht, mit Objekten zu arbeiten und deren Lebenszyklus zu verwalten.

Da wir einen Teil der Daten auf Shards übertragen haben, benötigten wir einen Sharding-Proxy. Es wäre möglich, den Router für diese Rolle wiederzuverwenden, aber ein separater Sharding-Proxy, der nur für das Daten-Sharding zuständig ist, ermöglicht es Ihnen, die gesamten Daten vom Router abzurufen, ohne über Sharding nachdenken zu müssen.

Warum wir keine fertige Lösung gewählt haben, sondern eine benutzerdefinierte Sharding-Funktion erstellen wollten, erzähle ich Ihnen gesondert.
Mal sehen, wie es funktioniert. Wir haben 256 Shards verfügbar. Für jeden Bucket wählen wir mithilfe einer bestimmten Konsistenzfunktion einen Bereich aus. Es ist ganz einfach: So wie Sie die Konsistenzfunktion verwenden, um zu bestimmen, ob Sie zu einem Shard gehören, bestimmen Sie auch den Start-Shard und wählen einen Bereich aus:
f(bucket, shards) = subset
Das heißt, wenn man einen Bucket nimmt, kann man sagen, dass dieser und seine Daten immer auf einer bestimmten Teilmenge aller Shards liegen. Dadurch können Sie den Einfluss einiger Buckets auf andere reduzieren und die Arbeit von Map-Reduction-Abfragen vereinfachen, wenn Sie beispielsweise Bucket-Objekte auflisten müssen. Dazu müssen Sie alle Shards abfragen, auf denen diese Objekte gespeichert sind. Würden sich Objekte auf allen Shards befinden, würde sich jede Auflistung auf das gesamte System auswirken, hier betrifft sie jedoch nur eine bestimmte Teilmenge.
Als nächstes gehört jedes Objekt zu einem bestimmten Bucket. Wenn wir also auf ein Objekt zugreifen, greifen wir über den Namen des Objekts in einem bestimmten Bucket zu. Das heißt, wir können eine Funktion für ein Objekt nicht aus dem gesamten verfügbaren Shard-Bereich definieren, sondern nur aus einer Teilmenge seines Buckets:
f(object, subset) = shard
Wir nehmen ein bestimmtes Objekt, übergeben dort nicht alle Shards als Funktionsargumente, sondern eine Teilmenge seines Buckets – und wir erhalten einen bestimmten Shard.

Sharding ist also implementiert, es gibt einen Sharding-Proxy. Dann bleibt nur noch der Weg vom Router und der Datenbank mit Metadaten zum Sharding-Proxy. Um beispielsweise Schattenkopieobjekte zu erstellen – wenn wir einen Bucket erstellen, muss der Hauptspeicher einen Vertreter dieses Buckets auf allen Shards erstellen, auf denen er vorhanden sein soll.

Wie wir Resharding implementiert haben
Das größte Problem beim Sharding ist das Resharding. Für uns war es wichtig, dies ohne Ausfallzeiten zu erreichen, da das System bereits in Produktion war. Wie wir das Problem gelöst haben, zeige ich Ihnen am Beispiel eines ähnlichen Problems bei der Live-Datenmigration von einem Projekt in ein anderes.
Unten sehen Sie ein Diagramm unseres Clusters, das nach der Einführung von Sharding entstanden ist. Wir haben Nginx, S3 API, einen Router, eine Primärdatenbank mit Projekten, einen Sharding-Proxy und die Shards selbst

Oben habe ich den Punkt übersehen, dass es in einer bestimmten Phase des Projekts eine Produktaufgabe gab: „Starten Sie eine weitere Speichereinrichtung, Icebox, wie Hotbox, nur für kalte Daten.“ Im Wesentlichen der gleiche Speicher, aber unter unterschiedlichen URLs und ohne Caches.

Icebox wurde weniger genutzt als Hotbox, daher lief es ziemlich lange ohne Sharding. Am Ende haben wir uns entschieden, darauf zu verzichten und Hotbox und Icebox in einem Dienst zu kombinieren, indem wir einfach die Speicherklassen getrennt haben.
Die Eimer in den Lagern überlappten sich nicht, sie konnten leicht zusammengeführt und verschoben werden, aber die Kunden nutzten beide Lagereinrichtungen, was bedeutete, dass das Problem der fehlenden Ausfallzeiten gelöst werden musste. Man konnte es nicht einfach ausschalten und kopieren. Wir haben die Migration in mehreren Schritten durchgeführt.
Zunächst haben wir die Primärspeicher synchronisiert. Wir hatten Tarantool und beim Erstellen eines Objekts konnten wir Folgendes tun:
- Es kommt eine Anfrage an die Datenbank, einen Bucket zu erstellen, beispielsweise in Hotbox.
- Tarantool prüft in einer anderen Datenbank (in diesem Fall Icebox), dass es keinen solchen Bucket gibt;
- Wenn ein Bucket vorhanden ist, meldet die Datenbank, dass er nicht erstellt werden kann, und wird als vorhanden synchronisiert.

Bucket-Synchronisierung
Im Lager, das alle Daten aufnehmen sollte, wurde für Projekte und Buckets ein Schild angebracht, auf dem stand, wo dieses Objekt gelagert wurde. Es könnte lokal gespeichert sein, also in Hotbox, Icebox – dann sind im neuen Speicher keine Daten davon vorhanden oder es könnte sich in einem Migrationszustand befinden.
Wenn ein Projekt oder ein Bucket das Flag „Migrieren“ hatte, wurde die Anfrage während der Migration zuerst an den neuen Speicher ausgeführt, in dem sich die Daten befinden sollten. Wenn dieser nicht vorhanden war, wurden die Anfragen an einen alternativen Speicher umgeleitet.
Als nächstes haben wir den Verkehr umgestellt. Da die API sowohl Icebox- als auch Hotbox-Anfragen bedienen konnte, konnten wir den Datenverkehr ohne Ausfallzeiten umschalten, indem wir einfach die Hosts verschoben und die entsprechenden Einträge in Nginx hinzugefügt haben.
Sobald der Datenverkehr umgeleitet wurde, konnten Nginx und die Icebox-API entfernt werden.
Dann haben wir Icebox Nginx und die S3-API entfernt – und alles hat funktioniert:

Als nächstes haben wir einen Hintergrundmigrationsprozess gestartet, der innerhalb der Datenbank funktioniert – er geht Element für Element durch alle Projekte und ihre Buckets, setzt für sie das Migrationsflag, überträgt die Daten und setzt nach Abschluss der Übertragung das Lokalflag.

Nach der Datenmigration benötigen wir den alten Speicher nicht mehr und entfernen die verbleibenden Teile des alten Systems sowie die Unterstützung für den Migrationsstatus aus dem Code.

Das Resharding vom alten Speicher zum fragmentierten Speicher erfolgte nach den gleichen Prinzipien:
- Alle Eimer markiert als
Non-sharded. Alle an sie gerichteten Anfragen gingen an den ursprünglichen, nicht geshardten Speicher. - Im Status wurden sofort neue Buckets angelegt
Sharded. - Sie nahmen die Eimer einen nach dem anderen und stellten den Status ein
Migratingund die Daten übertragen.
Die Bearbeitung der Anfragen erfolgte nach folgendem Prinzip:
- Wir lesen das Neue ein, dann das Alte.
- Wir schaffen nur im Neuen.
- Wir aktualisieren in zwei Phasen: Wenn es nicht im neuen enthalten ist, übertragen wir es vom alten auf das neue und aktualisieren es dann.
Arbeiten mit SSL-Zertifikaten
Im Frontend verwenden wir Nginx. In unserem Fall handelt es sich nicht um gewöhnliches Nginx, sondern um OpenResty, Nginx mit LuaJIT-Unterstützung.
Ein weiterer Teil des Systems arbeitet mit SSL-Zertifikaten. Im S3-Speicher können Sie Ihre eigene Domäne so einrichten, dass sie auf einen bestimmten Bucket zugreift, indem Sie einfach verwenden CNAME. Doch ohne HTTPS geht es heute nicht mehr: Eine eigene Domain bedeutet ein eigenes SSL-Zertifikat.
Wie ich bereits sagte, ist Nginx für den Ausgleich und die Beendigung von SSL verantwortlich. In unserem Fall handelt es sich nicht um gewöhnliches Nginx, sondern um OpenResty, Nginx mit LuaJIT-Unterstützung.
Dadurch konnten wir unserem Nginx ganz einfach beibringen, beliebige Zertifikate auszustellen. Darüber hinaus mussten wir Zertifikate dynamisch ausstellen (ohne sie in der Konfigurationsdatei registrieren zu müssen). Wir haben die Erweiterung genutzt ssl_certificate_by_lua, mit dem Sie ein Zertifikat aus einer beliebigen Quelle direkt während eines TLS-Handshakes lesen können. Wir haben Tarantool auch als Zertifikatsspeicher verwendet: Dies ermöglicht die externe Verwaltung von Zertifikaten und liefert extrem schnelle Ergebnisse.
Außerdem wurde ein eigener Daemon implementiert, dessen Aufgabe es ist, Zertifikate, die mit Let’s Encrypt ausgestellt wurden, regelmäßig zu aktualisieren.

Was würde ich behalten und was würde ich anders machen, wenn ich das Repository von Grund auf neu entwickeln würde?
Was hätte von Anfang an verwendet werden sollen
Sofort teilen. Das Resharding verursachte ziemlich viele Probleme. Dies ist einfach, aber wenn Sie dennoch Projekte starten, die skaliert werden müssen, ist es besser, sofort einen Shard-Cluster zu verwenden, auch mit einem Minimum an Knoten. Die Implementierung von Sharding zu Beginn ist im Vergleich zur Einführung von Sharding in ein Produktionssystem nahezu kostenlos.
Arbeiten mit Tarantool über Balancer. Jetzt verbinden wir sofort alle neuen Datenbanken, um über Balancer zu arbeiten. Dadurch können Sie die Funktionalität erweitern und eine höhere Fehlertoleranz erreichen.
Automatisches Failover. Ich würde alle Tools installieren, die für Autofailover erforderlich sind, da die ersten Fehler nach dem Start mit dessen Fehlen verbunden waren. Nach den Erfahrungen mit S3 wurden alle Folgeprodukte unter diesem Gesichtspunkt auf den Markt gebracht.
Feature S3 „Versionierung“. Zunächst schien es, dass dies keine sehr beliebte Funktionalität sei. Es ist äußerst schwierig, diese Fähigkeit in die Architektur eines laufenden Systems zu integrieren.
Separate Abrechnung. Die Art und Weise, wie wir die Abrechnung in unser System integriert haben, hat am Anfang gut funktioniert, aber später begann es zu stören; es wäre besser gewesen, es als völlig separaten Dienst zu haben.
Was war eine gute Entscheidung?
Datenmodell. Die Geschichte hat gezeigt, dass wir uns im Zuge der Weiterentwicklung des Dienstes ziemlich genau an das Amazon-Datenmodell anpassen, sodass wir die vorhandenen Funktionen implementieren können.
Sharding-Schema. Ich würde das gleiche Bereichs-Sharding über Buckets hinweg unterstützen, da dies eine gute Verteilung von Anfragen aus verschiedenen Buckets über einen großen Cluster ermöglicht.
Verwendung von Tarantool. Tarantool hat bei der Entwicklung des Dienstes und seiner Modifikation sehr geholfen; wir konnten problemlos mit Daten arbeiten, den Speicher transformieren und fragmentieren, ohne dass wir auf die Anwendungsebene gehen mussten.
Dieser Bericht wurde erstmals vorgestellt bei von Mail.ru Cloud Solutions&Tarantool. Suchen andere Aufführungen und abonnieren Sie Veranstaltungsankündigungen auf Telegram .
Sie können sich auch meinen alten Bericht über S3 ansehen oder den Artikel meines Kollegen über Blockspeicher lesen.
- .
- .
Source: habr.com

