Oder jede unglückliche Firma mit einem Monolithen ist auf ihre eigene Weise unglücklich.
Die Entwicklung des Dodo IS-Systems begann gleichzeitig mit dem Geschäft von Dodo Pizza — im Jahr 2011. Grundlage war die Idee einer vollständigen und totalen Digitalisierung der Geschäftsprozesse, wobei , was bereits 2011 viele Fragen und Skepsis aufwarf. Aber nun sind es schon 9 Jahre, in denen wir diesen Weg gehen — mit einer eigenen Entwicklung, die mit einem Monolithen begann.
Dieser Artikel ist eine «Antwort» auf die Fragen «Warum die Architektur umschreiben und so umfangreiche und langwierige Änderungen vornehmen?» zu dem vorhergehenden Artikel . Ich beginne damit, wie die Entwicklung von Dodo IS begann, wie die ursprüngliche Architektur aussah, wie neue Module entstanden und welche Probleme umfangreiche Änderungen erforderlich machten.

Die Artikelreihe «Was ist Dodo IS?» wird erzählen über:
Das frühe Monolith in Dodo IS (2011-2015 Jahre). (Sie sind hier)
.
Der Weg des Kundenbereichs: Fassade über der Datenbank (2016-2017 Jahre). (In Bearbeitung…)
Die Geschichte echter Mikrodienste. (2018-2019 Jahre). (In Bearbeitung…)
Vollendeter Schnitt des Monolithen und Stabilisierung der Architektur. (In Bearbeitung…)
Ursprüngliche Architektur
Im Jahr 2011 sah die Architektur von Dodo IS so aus:

Das erste Modul in der Architektur — die Bestellung annehmen. Der Geschäftsprozess war folgendermaßen:
Der Kunde ruft die Pizzeria an;
der Manager nimmt den Hörer ab;
nimmt die Bestellung am Telefon entgegen;
gibt sie parallel in die Schnittstelle zur Bestellannahme ein: Informationen über den Kunden, Daten zu den Bestelldetails, Lieferadresse werden berücksichtigt.
Die Benutzeroberfläche des Informationssystems sah ungefähr so aus…
Die erste Version vom Oktober 2011:
Die Ressourcen für die Entwicklung des ersten Moduls zur Bestellannahme waren begrenzt. Es musste viel, schnell und mit kleinem Team gemacht werden. Kleines Team — das sind 2 Entwickler, die das Fundament des gesamten zukünftigen Systems gelegt haben.
Ihre erste Entscheidung bestimmte das weitere Schicksal des Technologie-Stacks:
Backend auf ASP.NET MVC, Sprache C#. Die Entwickler waren .NET-Entwickler, dieser Stack war ihnen vertraut und angenehm.
Frontend auf Bootstrap und jQuery: Benutzeroberflächen basierend auf eigenen Stilen und Skripten.
Datenbank MySQL: ohne Lizenzkosten, einfach zu verwenden.
Server auf Windows Server, weil .NET damals nur unter Windows verfügbar war (Mono lassen wir außen vor).
Physisch äußerte sich das alles in einem „Dedizierten Server beim Hoster“.
Architektur der Bestellannahme-Anwendung
Damals sprach jeder von Mikrodiensten, während SOA schon seit etwa 5 Jahren in großen Projekten verwendet wurde, zum Beispiel kam WCF 2006 heraus. Aber damals wählte man eine zuverlässige und bewährte Lösung.
Hier ist es.

Asp.Net MVC ist Razor, das auf Anfrage von einem Formular oder vom Client eine HTML-Seite mit Rendering auf dem Server ausgibt. Am Client zeigen dann CSS und JS-Skripte die Informationen an und führen bei Bedarf AJAX-Anfragen über JQuery aus.
Anfragen auf dem Server gelangen in Klassen *Controller, wo in der Methode die Verarbeitung und Generierung der finalen HTML-Seite erfolgt. Controller stellen Anfragen an die Logikschicht, die *Services genannt wird. Jeder der Dienste war für einen bestimmten Aspekt des Geschäfts zuständig:
Zum Beispiel gab der DepartmentStructureService Informationen über Pizzerien und Abteilungen aus. Eine Abteilung ist eine Gruppe von Pizzerien unter der Leitung eines Franchisenehmers.
Der ReceivingOrdersService nahm Bestellungen entgegen und berechnete die Zusammensetzung der Bestellung.
Und der SmsService versandte SMS, indem er API-Dienste zum SMS-Versand aufrief.
Die Services verarbeiteten Daten aus der Datenbank, speicherten Geschäftslogik. In jedem Service gab es ein oder mehrere *Repository mit entsprechendem Namen. In diesen befanden sich bereits Anfragen an gespeicherte Prozeduren in der Datenbank und eine Schicht von Mappern. In den gespeicherten Prozeduren war die Geschäftslogik untergebracht, besonders viel in denjenigen, die Berichtsdaten ausgaben. ORM wurde nicht verwendet, alle verließen sich auf manuell geschriebene SQL.
Es gab auch eine Schicht des Domänenmodells und allgemeiner Hilfsklassen, wie beispielsweise die Klasse Order, die die Bestellung speicherte. Auch hier gab es in dieser Schicht einen Helper zur Umwandlung des Anzeige-Texts in der gewählten Währung.
All dies kann man sich mit folgendem Modell vorstellen:

Bestellweg
Betrachten wir den vereinfachten ursprünglichen Weg zur Erstellung einer solchen Bestellung.

Ursprünglich war die Website statisch. Dort waren Preise angegeben, und oben war die Telefonnummer sowie der Schriftzug „Willst du Pizza, ruf die Nummer an und bestelle“. Für die Bestellung müssen wir einen einfachen Ablauf umsetzen:
Der Kunde besucht die statische Website mit den Preisen, wählt Produkte aus und ruft die Nummer an, die auf der Website angegeben ist.
Der Kunde nennt die Produkte, die er zur Bestellung hinzufügen möchte.
Er nennt seine Adresse und seinen Namen.
Der Operator nimmt die Bestellung entgegen.
Die Bestellung wird in der Benutzeroberfläche für eingegangene Bestellungen angezeigt.
Alles beginnt mit der Anzeige des Menüs. Der eingeloggte Benutzer-Operator kann zu einem bestimmten Zeitpunkt nur einen Auftrag annehmen. Daher kann der Entwurf-Warenkorb in seiner Sitzung gespeichert werden (die Benutzersitzung wird im Speicher gehalten). Dort befindet sich das Objekt Cart, in dem Produkte und Informationen über den Kunden enthalten sind.
Der Kunde nennt das Produkt, der Operator klickt auf + neben dem Produkt, und eine Anfrage wird an den Server gesendet. Die Informationen über das Produkt werden aus der Datenbank abgerufen und dem Warenkorb hinzugefügt.

Hinweis. Ja, man kann das Produkt hier auch nicht aus der Datenbank abfragen, sondern von der Frontend-Seite übermitteln. Aber zur Veranschaulichung habe ich genau den Weg aus der Datenbank gezeigt.
Dann geben wir die Adresse und den Namen des Kunden ein.

Beim Klicken auf „Bestellung erstellen“:
Senden wir die Anfrage an OrderController.SaveOrder().
Wir erhalten den Warenkorb aus der Sitzung, in dem die Produkte in der benötigten Menge liegen.
Wir ergänzen den Warenkorb mit den Informationen über den Kunden und übergeben ihn an die Methode AddOrder der Klasse ReceivingOrderService, wo er in die Datenbank gespeichert wird.
In der Datenbank gibt es Tabellen mit Aufträgen, dem Auftragsinhalt und den Kunden, und sie sind alle miteinander verbunden.
Die Schnittstelle zur Anzeige von Bestellungen wird ausgeführt und ruft die letzten Bestellungen ab und zeigt sie an.
Neue Module
Die Annahme von Bestellungen war wichtig und notwendig. Man kann kein Geschäft für den Verkauf von Pizzen aufbauen, wenn es keine Möglichkeit zur Annahme von Bestellungen gibt. Daher begann das System, sich etwa von 2012 bis 2015 mit Funktionen zu erweitern. In dieser Zeit kamen viele verschiedene Module des Systems hinzu, die ich als Modulebezeichnen werde, im Gegensatz zu dem Begriff Dienst oder Produkt.
Ein Modul ist eine Gruppe von Funktionen, die durch ein gemeinsames Geschäftsziel vereint sind. Dabei befinden sie sich physisch in einer Anwendung.
Module können als Blöcke des Systems betrachtet werden. Zum Beispiel gibt es das Modul Berichte, die Admin-Schnittstellen, , die Autorisierung. All dies sind verschiedene Schnittstellen für den Benutzer, einige haben sogar unterschiedliche visuelle Stile. Dabei bleiben sie alle innerhalb einer Anwendung, eines arbeitenden Prozesses.
Technisch wurden die Module als Area strukturiert (eine solche Idee blieb sogar in ). Dort gab es separate Dateien für das Frontend, Modelle sowie eigene Controller-Klassen. Infolgedessen hat sich das System von einem solchen…

…zu einem solchen entwickelt:

Einige Module sind als separate Websites (ausführbares Projekt) implementiert, aufgrund von völlig separaten Funktionen und teilweise aufgrund einer etwas anderen, fokussierten Entwicklung. Dazu zählen:
Site — seite dodopizza.ru.
Export: Datenexport aus Dodo IS für 1C.
Personal — persönlicher Bereich für Mitarbeiter. Wurde separat entwickelt und hat seinen eigenen Zugangspunkt sowie ein eigenes Design.
fs — Projekt für das Hosting von statischen Inhalten. Später haben wir uns davon entfernt und alle statischen Inhalte auf das CDN Akamai übertragen.
Die anderen Blöcke waren in der BackOffice-Anwendung.

Erläuterung zu den Bezeichnungen:
Cashier — Restaurantkasse.
ShiftManager — Schnittstellen für die Rolle „Schichtmanager“: aktuelle Statistiken über den Pizzaverkauf, Möglichkeit, Produkte auf die Sperrliste zu setzen und Bestellungen zu ändern.
OfficeManager — Schnittstellen für die Rolle „Pizzamanager“ und „Franchisenehmer“. Hier sind Funktionen zur Einstellung der Pizzeria, ihrer Bonusaktionen, Bearbeitung von Mitarbeitern und Berichten zusammengefasst.
PublicScreens — Schnittstellen für Fernseher und Tablets, die in den Pizzerien hängen. Auf den Fernsehern werden das Menü, Werbeinformationen und der Status der Bestellungen während der Ausgabe angezeigt.
Sie verwendeten eine gemeinsame Serviceschicht, einen gemeinsamen Block von Domainklassen Dodo.Core sowie eine gemeinsame Datenbank. Manchmal konnten sie auch durch Übergänge zueinander geführt werden. Auch einzelne Websites wie dodopizza.ru oder personal.dodopizza.ru griffen auf die gemeinsamen Services zu.
Bei der Einführung neuer Module wurde versucht, den bereits erstellten Code von Services, gespeicherten Prozeduren und Tabellen in der Datenbank maximal wiederzuverwenden.
Um das Ausmaß der in das System implementierten Module besser zu verstehen, hier ein Diagramm aus dem Jahr 2012 mit Entwicklungsplänen:

Bis 2015 war alles im Diagramm und sogar mehr in der Produktion.
Die Auftragsannahme entwickelte sich zu einem separaten Block des Contact Centers, wo die Bestellungen von einem Operator entgegengenommen werden.
Öffentlich zugängliche Bildschirme mit dem Menü und Informationen wurden in den Pizzerien installiert.
In der Küche gibt es ein Modul, das automatisch die Sprachmeldung „Neue Pizza“ abspielt, wenn eine neue Bestellung eingeht, und auch die Quittung für den Kurier druckt. Dies vereinfacht die Abläufe in der Küche erheblich und entlastet die Mitarbeiter von einer Vielzahl einfacher Aufgaben.
Der Lieferblock wurde zu einer separaten Lieferkasse, wo die Bestellung an den Kurier ausgegeben wurde, der zuvor einen Schichtwechsel vorgenommen hatte. Seine Arbeitszeit wurde für die Gehaltsabrechnung berücksichtigt.
Parallel zwischen 2012 und 2015 kamen über 10 Entwickler hinzu, es wurden 35 Pizzerien eröffnet, das System wurde auf Rumänien ausgeweitet und die Eröffnung von Standorten in den USA vorbereitet. Die Entwickler beschäftigten sich bereits nicht mehr mit allen Aufgaben, sondern waren in Teams aufgeteilt, die jeweils auf ihren Teil des Systems spezialisiert waren.
Probleme
Unter anderem aufgrund der Architektur (aber nicht nur).
Chaos in der Datenbank
Eine Datenbank — das ist praktisch. Damit kann man Konsistenz erreichen, und zwar durch Mittel, die in relationale Datenbanken integriert sind. Der Umgang mit ihr ist vertraut und bequem, besonders wenn es wenig Tabellen und wenig Daten gibt.
Aber in den 4 Jahren der Entwicklung gab es in der Datenbank etwa 600 Tabellen, 1500 gespeicherte Prozeduren, in vielen davon war auch Logik enthalten. Leider bieten gespeicherte Prozeduren bei der Arbeit mit MySQL keinen besonderen Vorteil. Sie werden nicht von der Datenbank zwischengespeichert, und die Speicherung von Logik darin erschwert die Entwicklung und das Debugging. Die Wiederverwendung von Code ist ebenfalls erschwert.
Bei vielen Tabellen gab es keine passenden Indizes, woanders hingegen gab es sehr viele Indizes, was das Einfügen erschwerte. Etwa 20 Tabellen mussten modifiziert werden — die Transaktion zur Erstellung einer Bestellung konnte etwa 3-5 Sekunden in Anspruch nehmen.
Die Daten in den Tabellen waren nicht immer in der am besten geeigneten Form. An manchen Stellen musste eine Denormalisierung durchgeführt werden. Ein Teil der regelmäßig erhaltenen Daten war in einer Spalte in Form einer XML-Struktur, was die Ausführungszeit erhöhte, die Abfragen verlängerte und die Entwicklung erschwerte.
Auf die gleichen Tabellen wurden sehr heterogene Abfragen ausgeführt. Besonders litten die beliebten Tabellen, wie beispielsweise die erwähnte Tabelle orders oder die Tabelle pizzeria. Sie wurden für die Darstellung operativer Schnittstellen in der Küche und für die Analytik verwendet. Außerdem wurde auf sie von der Website (), die jederzeit viele plötzliche Anfragen erhalten konnte, zugegriffen.
Die Daten waren nicht aggregiert und viele Berechnungen fanden in Echtzeit durch die Datenbank statt. Das führte zu unnötigen Berechnungen und zusätzlicher Last.
Oftmals wurde der Code in die Datenbank aufgerufen, wenn er es nicht hätte tun müssen. An manchen Stellen fehlten Bulk-Operationen, an anderen hätte man eine Anfrage über den Code in mehrere aufteilen müssen, um die Geschwindigkeit zu erhöhen und die Zuverlässigkeit zu verbessern.
Verknüpfung und Komplexität im Code
Die Module, die für ihren Geschäftsbereich verantwortlich sein sollten, erfüllten dies nicht verlässlich.. Einige von ihnen hatten Funktionalitäten für Rollen, die sich überschnitten. Zum Beispiel musste ein lokaler Marketingmitarbeiter, der für die Marketingaktivitäten des Netzwerks in seiner Stadt verantwortlich war, sowohl das „Admin“-Interface (für die Erstellung von Aktionen) als auch das „Büromanager“-Interface (für die Anzeige der Auswirkungen von Aktionen auf das Geschäft) nutzen. Natürlich verwendeten beide Module intern denselben Service, der mit den Bonusaktionen arbeitete.
Dienste (Klassen innerhalb eines großen monolithischen Projekts) konnten sich gegenseitig aufrufen, um ihre Daten zu bereichern.
Mit den Modellklassen, die die Daten speichern, wurde unterschiedlich im Code gearbeitet. An einigen Stellen gab es Konstruktoren, über die man die erforderlichen Felder angeben konnte. An anderen Stellen wurde dies durch öffentliche Eigenschaften erledigt. Natürlich war das Abrufen und Transformieren von Daten aus der Datenbank vielfältig.
Die Logik lag entweder in den Controllern oder in den Dienstklassen.
Das sind zwar scheinbar kleine Probleme, aber sie verzögerten die Entwicklung erheblich und verringerten die Qualität, was zu Instabilität und Fehlern führte.
Die Komplexität großer Entwicklungen
Schwierigkeiten traten auch in der eigentlichen Entwicklung auf. Es mussten verschiedene Systemblöcke erstellt werden, und das parallel. Es wurde immer schwieriger, die Bedürfnisse der einzelnen Komponenten in einen einheitlichen Code zu integrieren. Es war nicht einfach, sich zu einigen und allen Komponenten gleichzeitig gerecht zu werden. Hinzu kamen technologische Einschränkungen, insbesondere in Bezug auf die Datenbank und das Frontend. Man musste auf JQuery zugunsten hochgradiger Frameworks verzichten, insbesondere im Bereich der Client-Services (Webseite).
An bestimmten Stellen im System könnten besser geeignete Datenbanken eingesetzt werden. Beispielsweise hatten wir später einen Fall, in dem wir von Redis auf CosmosDB umgestiegen sind, um den Warenkorb zu speichern.
Die Teams und Entwickler, die sich mit ihrem Bereich beschäftigen, wollten deutlich mehr Unabhängigkeit für ihre Dienste, sowohl in Bezug auf die Entwicklung als auch auf das Deployment. Konflikte beim Merge, Probleme bei den Releases. Wenn dieses Problem für 5 Entwickler geringfügig ist, wird es bei 10, und erst recht bei geplantem Wachstum, ernster. Und in naher Zukunft stand die Entwicklung einer mobilen Anwendung an (sie begann 2017 und 2018 gab es ).
Verschiedene Teile des Systems erforderten unterschiedliche Stabilitätsmerkmale., aber aufgrund der starken Verknüpfung des Systems konnten wir das nicht sicherstellen. Ein Fehler bei der Entwicklung einer neuen Funktion im Admin-Bereich hätte durchaus Auswirkungen auf die Bestellannahme auf der Website haben können, denn der Code ist gemeinsam genutzt und wiederverwendbar, ebenso wie die Datenbank und die Daten.
Wahrscheinlich hätte man auch innerhalb solch einer monolithisch-modularen Architektur diese Fehler und Probleme vermeiden können: Verantwortung aufteilen, sowohl den Code als auch die Datenbank refaktorisieren, Schichten klar voneinander trennen und täglich auf Qualität achten. Aber die gewählten Architekturlösungen und der Fokus auf eine schnelle Erweiterung der Systemfunktionen führten zu Problemen in Bezug auf die Stabilität.
Wie der Blog von Sila umma in Restaurants Kassen gebracht hat
Wenn das Wachstum der Pizzakette (und die Last) im gleichen Tempo weitergegangen wäre, wären die Einbrüche irgendwann so gravierend geworden, dass das System nicht mehr hochgefahren werden könnte. Eine solche Geschichte veranschaulicht gut die Probleme, mit denen wir ab 2015 konfrontiert waren.
Im Blog „“ gab es ein Widget, das die Einnahmedaten der gesamten Kette für ein Jahr anzeigte. Das Widget sprach die öffentliche API von Dodo an, die diese Daten bereitstellt. Diese Statistiken sind jetzt verfügbar unter . Das Widget wurde auf jeder Seite angezeigt und führte alle 20 Sekunden Anfragen über einen Timer durch. Die Anfrage ging an api.dodopizza.ru und fragte an:
die Anzahl der Pizzerien im Netzwerk;
die Gesamteinnahmen des Netzwerks seit Jahresbeginn;
die Einnahmen für heute.
Die Anfrage nach den Einnahmen-Statistiken ging direkt an die Datenbank und begann, die Bestelldaten abzufragen, aggregierte die Daten in Echtzeit und gab die Summe aus.
In diese Tabelle der Bestellungen flossen auch die Kassen in den Restaurants, die die Liste der heute angenommenen Bestellungen exportierten, und neue Bestellungen wurden ebenfalls hinzugefügt. Die Kassen stellten alle 5 Sekunden oder bei einer Aktualisierung der Seite ihre Anfragen.
Das Schema sah so aus:

Eines Herbstes schrieb Fedor Owtschinnikow einen langen und beliebten Artikel in seinem Blog. Der Blog wurde von sehr vielen Leuten aufgerufen und alle begannen, sorgfältig zu lesen. Während jede Person, die gekommen war, den Artikel las, arbeitete das Widget mit den Einnahmen einwandfrei und fragte alle 20 Sekunden die API ab.
Die API rief eine gespeicherte Prozedur zur Berechnung der Gesamtsumme aller Bestellungen seit Jahresbeginn in allen Pizzerien der Kette auf. Die Aggregation erfolgte über die Tabelle orders, die sehr beliebt ist. In sie mündeten alle Kassen aller zu diesem Zeitpunkt geöffneten Restaurants. Die Kassen hörten auf zu antworten, Bestellungen wurden nicht angenommen. Zudem wurden sie auch nicht von der Website angenommen und erschienen nicht im Tracker, der Schichtmanager konnte sie nicht in seiner Benutzeroberfläche sehen.
Dies ist nicht die einzige Geschichte. Bis zum Herbst 2015 war die Last auf dem System jeden Freitag kritisch. Mehrmals schalteten wir die öffentliche API aus, und einmal mussten wir sogar die Website abschalten, weil nichts mehr half. Es gab sogar eine Liste von Diensten mit einer Reihenfolge der Abschaltung bei ernsthaften Belastungen.
Von diesem Zeitpunkt an begann unser Kampf gegen die Lasten und für die Stabilisierung des Systems (von Herbst 2015 bis Herbst 2018). Genau zu dieser Zeit geschah das „“. Auch danach gab es gelegentlich Ausfälle, einige waren ziemlich empfindlich, aber den allgemeinen Zeitraum der Instabilität kann man jetzt als überstanden betrachten.
Starker Wachstum des Geschäfts
Warum konnte man nicht „sofort alles gut machen“? Es reicht aus, sich die folgenden Grafiken anzusehen.

Auch in den Jahren 2014-2015 gab es eine Eröffnung in Rumänien und die Eröffnung in den USA wurde vorbereitet.
Das Netzwerk wuchs sehr schnell, neue Länder wurden eröffnet, neue Formate von Pizzerien kamen hinzu, zum Beispiel wurde eine Pizzeria im Food Court eröffnet. All dies erforderte erhebliche Aufmerksamkeit für die Erweiterung der Funktionen von Dodo IS. Ohne all diese Funktionen, ohne Tracking in der Küche, Produkt- und Verluststatistik im System, sowie die Anzeige der Bestellabgabe im Food Court, würden wir wohl heute nicht über die „richtige“ Architektur und den „richtigen“ Ansatz in der Entwicklung sprechen.
Ein weiteres Hindernis für eine zeitnahe Überarbeitung der Architektur und überhaupt für die Aufmerksamkeit auf technische Probleme war die Krise von 2014. Solche Dinge treffen die Wachstumsfähigkeiten von Teams hart, besonders für junges Geschäft, wie Dodo Pizza es war.
Schnelle Lösungen, die geholfen haben
Die Probleme verlangten nach Lösungen. Grob gesagt, kann man die Lösungen in zwei Gruppen unterteilen:
Schnelle, die das Feuer löschen und eine kleine Sicherheitsmarge bieten und uns Zeit für Veränderungen verschaffen.
Systematische und daher langwierige. Reinventing mehrerer Module, Trennung der monolithischen Architektur in separate Dienste (die meisten sind durchaus keine Mikro-, sondern eher Makrodienste, und darüber gibt es) ).
Die kurze Liste der schnellen Änderungen ist wie folgt:
Master-Datenbank skalieren
Natürlich ist das Erste, was zur Bekämpfung von Belastungen getan wird, die Erhöhung der Serverleistung. Das wurde sowohl für die Master-Datenbank als auch für die Web-Server durchgeführt. Leider ist dies nur bis zu einem bestimmten Punkt möglich, danach wird es zu teuer.
Seit 2014 haben wir auf Azure umgeschaltet, über dieses Thema haben wir auch damals in dem Artikel "" geschrieben. Aber nach einer Reihe von Servererhöhungen für die Datenbank haben wir die Kosten überschritten.
Lesereplikate der Datenbank
Es wurden zwei Replikate für die Datenbank erstellt:
ReadReplica für Anfragen an Verzeichnisse. Es wird für das Lesen von Verzeichnissen verwendet, wie Städte, Straßen, Pizzerien, Produkte (slowly changed domain) und in den Schnittstellen, wo eine geringe Verzögerung zulässig ist. Es gab 2 dieser Replikate, wir haben deren Verfügbarkeit wie die der Master-Instanz sichergestellt.
ReadReplica für Berichtsanfragen. Diese Datenbank hatte eine geringere Verfügbarkeit, aber alle Berichte liefen darauf. Auch wenn sie schwere Anfragen für enorme Datenberechnungen hatten, beeinflussten sie nicht die Hauptdatenbank und die Betriebsoberflächen.
Caches im Code
Es gab keine Caches im Code (überhaupt). Das führte zu zusätzlichen, nicht immer notwendigen Anfragen an die belastete Datenbank. Zunächst waren Caches sowohl im Speicher als auch auf einem externen Cache-Dienst vorhanden, das war Redis. Alle wurden zeitlich invalidiert, die Einstellungen wurden im Code angegeben.
Mehrere Server für das Backend
Das Backend der Anwendung musste ebenfalls skaliert werden, um die gestiegenen Belastungen zu bewältigen. Es war notwendig, aus einem IIS-Server ein Cluster zu bilden. Wir haben die von der Speicherung im Speicher auf RedisCache übertragen, was die Einrichtung mehrerer Server hinter einem einfachen Lastenausgleich mit Round Robin ermöglichte. Zunächst wurde dasselbe Redis verwendet wie für die Caches, später haben wir es auf mehrere verteilt.
Insgesamt hat sich die Architektur verkompliziert…

…aber ein Teil der Anspannung konnte abgebaut werden.
Dann mussten die belasteten Komponenten überarbeitet werden, woran wir uns gemacht haben. Darüber werden wir im nächsten Teil berichten.
Quelle: habr.com
