Heim  >  Artikel  >  Backend-Entwicklung  >  Wie ich über Einträge pro Anfrage verarbeitet habe – Mit Go!

Wie ich über Einträge pro Anfrage verarbeitet habe – Mit Go!

Barbara Streisand
Barbara StreisandOriginal
2024-11-05 04:45:02632Durchsuche

How I processed over Entries per request - With Go!

Bevor wir beginnen, ein wenig Kontext darüber, wer ich bin und warum es in diesem Fall wichtig ist. Ich bin Softwareentwickler bei Notebook Manufacturing Company und arbeite hier seit zwei Jahren. In dem Team, in dem ich gerade bin, bin ich der einzige Entwickler, der für die Erstellung, Überwachung und Wartung von Datenpipelines, Automatisierungen und einem Überwachungssystem mit Go und Grafana verantwortlich ist.
Ein weiterer Punkt, den ich hinzufügen darf: Im Vorjahr war ich Praktikant. Und ich bin Autodidakt.

Ok, aber warum ist das wichtig?

Nun, ich hatte keinen leitenden Entwickler oder irgendeine Form von interner Anleitung, die mich anleiten konnte, wenn ich mit Hindernissen oder Problemen konfrontiert war, die ich selbst nicht lösen konnte, und das ist der Hauptgrund, warum ich schreibe. Ich denke, es hat Spaß gemacht und ich möchte es teilen. Es wird keine großartige Software, kein Durchbruch oder ähnliches sein, sondern eine Erinnerung daran, dass es möglich ist, Dinge von Grund auf neu zu entwickeln. Sie müssen nicht einer von einer Million 10x-Entwickler oder so etwas in der Art sein.

P.S.: Ich verwende übrigens Neovim.

Eine Million Zeilen

Diese Zahl sieht vielleicht wie eine Übertreibung oder so etwas aus, und obwohl ich mir wünschte, dass es so wäre, ist es das nicht. Sehen Sie, wenn wir im Fertigungskontext arbeiten, berücksichtigen wir manchmal nicht, wie viele Artikel benötigt werden und wie viele Daten generiert werden, wenn man jeden Punkt der vielen Fertigungswarteschlangen für jede Komponente im Auge behalten muss.
Sei es eine kleine Schraube, sei es eine CPU der letzten Generation, sie muss verfolgt werden, mit Zeitstempeln!
Also ja, die Menge der generierten Daten ist korrekt, und das ist erst der Anfang. Hier sind die anderen coolen Punkte, die das Problem (aus meiner Sicht) cool machen:

  • Die Quelle dieser Daten war nicht komprimiert oder ähnliches, sodass die durchschnittliche Anfragezeit an einem guten Tag etwa 2 bis 3 Minuten betrug. Es könnte über 5 Minuten dauern, wenn die Quelle verzögert wäre (es ist ein älteres Java-System als ich).
  • Was das Format betrifft, konnte ich zwischen einer XML-Antwort oder einer CSV-Antwort wählen, natürlich habe ich den CSV-Weg gewählt. Ich bin kein Masochist.
  • Und es musste das Intervall für den Datenabruf ausgewählt werden, mit mindestens einer Stunde (Nein, ich weiß nicht warum, ja, ich habe kleinere Intervalle ausprobiert).
  • Die Quelle hatte kein Lazy-Loading- oder Cache-System, sodass zwei gleiche Anfragen dieselbe Antwort mit unterschiedlichen Zeiten generieren würden.
  • Und schließlich, um es noch spannender zu machen: Es handelt sich um eine Microsoft-Sache, bekannt als SQL Server Reporting Services, mit ihren eigenen obskuren Vorbehalten. Oh, und die Datenbank, die ich verwenden muss, ist MSSQL, was mühsam ist.

Das ist der ganze Kontext, und jetzt beginnt der lustige Teil.

Die erste Iteration – DMP

Meine Herangehensweise an Software ist ziemlich einfach. Machen Sie es hässlich und kaum funktionsfähig -> Halten Sie es hässlich und verbessern Sie die Funktionalität -> Immer noch hässlich, aber besser optimiert -> Hübsch, optimiert und funktionsfähig -> Und dann sagt Ihr Vorgesetzter, dass die Arbeit, die Sie geleistet haben, viel zu gut ist und dass die Quelle damit nicht zurechtkommt, was zu Ausfällen führt. FML.

Wie auch immer, DMP steht für Dumb Mode Protocol. Sorgen Sie dafür, dass es auf die dümmste Art und Weise funktioniert, die Sie können, was bedeutet, dass es kaum funktioniert.

Also, in der ersten Runde war mein Ziel einfach: authentifizieren, die Anfrage stellen, die Daten analysieren und an die Datenbank senden. Ziemlich einfach, oder? Und auf dem Papier war das so, bis ich herausfand, dass die Authentifizierungs- und Autorisierungsmethode, die ich verwenden musste, ntlmssp war, eine Challenge-Response-Authentifizierungsmethode, von deren Existenz ich nichts wusste. Tatsächlich musste ich in den alten .NET 4-Code eintauchen, um ihn zu finden. Ich habe nie C# gemacht.
Nachdem ich den Legacy-Code durchgesehen hatte, der älter war als ich, hatte ich Mühe, ihn zu verstehen, weil der Autor, der ihn geschrieben hatte, aus irgendeinem Grund der Meinung war, es sei eine gute Idee, ihn in fünf Ebenen aus Abstraktion, Konstruktoren und OOP-Dingen zu verstecken. Keine lustige, demütigende Erfahrung. Und dann habe ich es geschafft. Eine Stunde später wurde mein Benutzer gesperrt, da die Quelle offenbar eine Ratenbegrenzung hat. ABER KEIN CACHE ODER LAZY LOADING.

Nach all dem muss ich nur noch die Abfrageparameter übergeben und die Daten abrufen, keine Komplikationen mehr mit der Quelle. Ok, schauen wir uns die Dokumentation für die Abfrageparameter an.

...

Alles in allem haben Sie zum jetzigen Zeitpunkt wahrscheinlich richtig geraten. Dokumentation? Ist das eine Art exotisches Essen, das nur im Silicon Valley serviert wird?
Wie auch immer, nachdem ich mir den Kopf zerbrochen hatte, beschloss ich, die Site/Schnittstelle dieser SQL Server Reporting Services zu untersuchen, und zu meiner Freude, aber auch zu meinem Hass, hatte sie eine Möglichkeit, die Filter und ihre Werte zu kennen.

Nur ​​zu wissen, dass der Filter („Alle“) auf der Seite, um beispielsweise alle Fertigungslinien auszuwählen, nur eine Abstraktion davon ist, dass alle Kästchen aktiviert sind. Ok, lass uns die Filter kopieren und in den Abfragestring einfügen, codieren und glücklich sein!

ES FUNKTIONIERTE! Zumindest dachte ich das.

Erinnern Sie sich, als ich sagte, dass mein Benutzer blockiert wurde? Nun, es sieht so aus, als ob die Administratorrechte zum Ausführen solcher Aufgaben und die Autorisierung durch die C-Suite (tatsächlich wurde es von ihnen angefordert) nicht ausreichten, um mir die Bearbeitung mehrerer Anfragen zu ermöglichen. Mein Benutzer wurde blockiert, aber für meinen Manager war das dasselbe, als würde er sagen: „Es ist nichts passiert“. Zum Glück habe ich es ziemlich schnell geschafft, die Blockierung aufzuheben.

In der Zwischenzeit habe ich beschlossen, an der Herangehensweise an den Rest dieses Projekts zu arbeiten. Zu diesem Zeitpunkt hatte ich bereits eine Probe davon und sie wurde dem Titel des Beitrags gerecht. Als ich die sieben Ziffern sah, fragte ich mich, ob ich dazu in der Lage wäre, da ich mit diesem Datenvolumen keinerlei Erfahrung hatte.

Um meine Ideen in Form zu bringen, habe ich mich für Excelidraw entschieden und entworfen, was ich machen wollte. Ich kannte das Konzept von Worker-Pools, hatte es aber noch nie umgesetzt. Also habe ich darüber gelesen, einige Implementierungen gesehen und einen Freund gefragt, der mir sehr geholfen hat. Er ist der Senior, den ich nicht hatte.

Nachdem ich es in Pseudocode geschrieben hatte, dachte ich mir:

"Wow, das ist ziemlich nett, es wird mit all diesen Kanälen und Goroutinen sicherlich keine Speicherverluste erzeugen, oder?"

Ok, es waren vielleicht nicht genau diese Worte, aber es war in dieser Richtung. Nachdem ich die erste Iteration durchgeführt hatte, es geschafft hatte, die Anfrage auszuführen (ohne blockiert zu werden) und sie in den Speicher geladen hatte, wendete ich das Worker-Pool-Konzept an.

Und dann stand ich vor dem BSOD. Witzigerweise war es am selben Tag wie der CrowdStrike-Streik, ich hätte sicher nicht gedacht, dass ich größere Ausfälle in der Fabrik verursacht habe.
Außerdem ja, ich muss Windows verwenden, um zu arbeiten, aber keine Sorge! Ich verwende WSL2.

Nachdem ich die enorme Stapelspur meines ersten Stapelüberlaufs durchgesehen hatte, entdeckte ich den Fehler. Beim Senden der Daten an den Verbraucherkanal habe ich nicht berücksichtigt, dass einige der Daten fehlerhaft sein könnten, hauptsächlich aufgrund einer Verletzung des Primärschlüssels oder einfach, weil ich Fehler möglicherweise nicht richtig behandelt habe.

Lektion gelernt: Verwenden Sie einen Fehlerkanal, um größere Probleme zu vermeiden. Und behandeln Sie Ihre Fehler, sei es durch eine einfache Stringprüfung (hässlich, funktioniert aber) oder einfach durch Protokollieren auf oberster Ebene, und seien Sie zufrieden. Da die Fehler, mit denen ich konfrontiert war, nicht kritisch waren, konnte ich weitermachen.

Die zweite Iteration. Speicherlecks.

Die Menge an Speicherlecks, die ich in diesem Schritt erzeugt habe, ließ mich glauben, ich würde C machen. Aber es waren nur große Fähigkeitsprobleme.
Wie auch immer, Sie denken vielleicht:

"Wie haben Sie in einem so einfachen Prozess Speicherlecks verursacht?"

Es ist ganz einfach, ich habe gelernt und versucht.

Das Hauptproblem bei diesem Schritt bestand darin, dass ich naiverweise nicht dafür gesorgt habe, dass die Daten richtig eingefügt wurden, und dass das Ausmaß der Verletzung von Primärschlüsseln mein Gedächtnis beeinträchtigte. Dies war ein Problem, von dem ich wusste, wie ich es lösen konnte. Lassen Sie uns die Daten zwischenspeichern!
Warten Sie, wie erstelle ich für jede Zeile einen eindeutigen Bezeichner, wenn man bedenkt, dass jede Zeile eindeutig ist?

Das ist der Teil, über den mich viele auslachen werden, denn um ehrlich zu sein, ist das der Teil, der mich am meisten zum Lachen bringt. Ich habe einfach die aktuelle Informationszeile zusammengefügt und sie in eine Hash-Funktion analysiert, und dieser Hash wurde zu meinen Schlüsseln in der Karte.

„Warum nicht Redis?“ – Sie fragen sich vielleicht.

Es ist ganz einfach, Bürokratie. Es ist kein kleines Unternehmen. Tatsächlich verwenden viele von Ihnen wahrscheinlich einen von ihnen hergestellten Laptop. Das einfache Anfordern einer Redis-Instanz würde mindestens drei Arbeitstage, vier Besprechungen, ein Opfer für den Gott der Bürokratie und ein vollständiges Dokument erfordern, in dem erklärt wird, warum und wie.
Also, ja, lasst uns mit einer einfachen Hash-Map beginnen und sie vor dem ersten Durchlauf vorinitialisieren. Dies erhöht die Gesamtladezeit, ist aber schneller als die Anfrage.

Auf diese Weise verbesserte sich der Gesamtprozess, als hätte er einen neuen Motor, die Memleaks hörten auf, die Chargen schlugen nicht jedes Mal fehl, und auch die Anzahl der Fehler und Unterbrechungen ging zurück, ganz nett, oder? Richtig?

Inzwischen wissen Sie, dass etwas durcheinander geraten ist.

Die dritte Iteration. Das Leben könnte gut sein.

Ein Punkt, den ich nicht berücksichtigt habe, war die Tatsache, dass durch Batch-Einfügungen die Daten validiert werden. Hier ist eine einfache Darstellung des Ablaufs.

Daten abrufen -> Überprüfen Sie, ob der Hash der Daten in der Hashmap vorhanden ist -> Stapeln und einfügen

Und was ist das Problem daran? Was passiert, wenn eine einzelne Einfügung in meinem Stapel fehlschlägt? Würde es dann ohne den Eintrag erneut versuchen? Wenn ja, wie viele Wiederholungsversuche kann ich durchführen, ohne das System zu überladen und die Vorteile der Worker-Pool-Implementierung zu verlieren?
Es gibt nur einen Weg, das herauszufinden! Lass es uns überprüfen.
Ein Punkt, den ich hinzufügen möchte, ist die Tatsache, dass diese Quelle mehr als 25 Spalten zurückgegeben hat. Daher musste ich bei der Datenmenge, die ich pro Stapel einfügte, vorsichtig sein, um 2100 Parameter nicht zu überschreiten, was dem MSSQL-Limit entspricht.

Zu diesem Zeitpunkt habe ich bereits Dinge in einem Docker-Container ausgeführt, der den Produktionsraum mit begrenzten Ressourcen nachahmt. Um den Kontext hinzuzufügen: Dieser Prozess wurde mit 1 GB RAM und etwa 0,5 CPU ausgeführt. Ich hätte mehr Ressourcen bereitstellen können, aber das wäre einfach brutal, wenn ich mir den Ausweg erzwingen würde.
Indem Sie diese neue Iteration im Container ausführen, einige Zeitstempel hinzufügen und sie zur späteren Analyse in einer Datei protokollieren. Aufgrund der Anzahl der Wiederholungsversuche habe ich eine Verlängerung um etwa 5 Minuten festgestellt. Das würde nicht funktionieren, das Entfernen des „schmutzigen“ Eintrags war keine Option.

Die vierte Iteration. Das Leben ist gut.

Um dieses Problem zu lösen, habe ich die Anzahl der Arbeiter erhöht. Ich habe ungefähr 50 Worker eingesetzt und dank einer zufälligen Schätzung eines Discord-Benutzers in ThePrimagen Top Shelf habe ich die Zahl auf 1000 Worker erhöht und dafür gesorgt, dass jeder Worker jeden Eintrag in einer Transaktion validiert. Falls die Transaktion fehlschlug, habe ich sie einfach zurückgesetzt.
Dadurch konnte ich das Kernproblem lösen und die Gesamtgeschwindigkeit dieses Prozesses insgesamt verbessern. Jetzt war es an der Zeit, es in die Produktion zu integrieren und zu überwachen, denn, wie Sie wissen, könnten die Produktionskobolde Ihre Software durcheinander bringen. (Sie werden auch als Fertigkeitsprobleme bezeichnet, dieser Name ist jedoch verboten.)

Da ich wusste, dass ich das Abrufintervall dieses Systems, bei dem es darum ging, es nahezu in Echtzeit zu machen (was bedeutet, dass es schnell genug ist, damit sie keine Verzögerungen bemerken), verkürzt hat, habe ich einen neuen Container erstellt, diesmal mit etwas robusteren Prüfen Sie, ob die Last ausgehalten werden kann, und stellen Sie das Intervall auf 3 Minuten ein. Wenn man die durchschnittliche Zeit zum Abholen bedenkt, würde es bedeuten, dass es einige Überschneidungen geben könnte, aber ich wollte unbedingt sehen, was passieren würde.

Ich habe es über Nacht laufen lassen, die Ergebnisse protokolliert, um es später zu überprüfen, und zu meiner Überraschung erhielt ich noch vor Ende des Arbeitstages einen Anruf von meinem Vorgesetzten. Allerdings ist er kein Technikmensch oder so etwas in der Art.

„Hey, haben wir etwas bereitgestellt, das mit dem [Systemnamen, den ich nicht offenlegen kann] interagiert?“

„Ja, ich habe das angeforderte Datenabrufsystem in Echtzeit bereitgestellt. Warum?“

„Kannst du, hm, damit aufhören? Es hat einen Ausfall verursacht und wir können hier nicht arbeiten.“_

Das, Gadies und Lentleman, ist eine ganz andere Ebene des Breaking Prod. Ich habe buchstäblich über 20 Produktionslinien für etwa eine Minute angehalten. Wie auch immer, ich habe das System gestoppt und alles hat sich wieder normalisiert.
Am nächsten Tag wurde ich gebeten, das Intervall zwischen den Abrufen auf 30 Minuten statt auf 3 zu erhöhen, ok, gut, denke ich. Ich werde nicht lügen, ich war ein bisschen traurig, dass ich es nicht bei Höchstgeschwindigkeit sehen konnte, aber hey, zumindest habe ich es geschafft.

Mit diesem neuen System verringerte sich die durchschnittliche Aktualisierungszeit der Berichte, die diese Daten verwendeten, auf 8 bis 10 Sekunden, was dazu führte, dass Manager viel mehr Berichte derselben Quelle auf die gleiche Weise anforderten. Gute Arbeit wird mit mehr Arbeit belohnt!

Überlegungen

Es war eine unterhaltsame Erfahrung, vor allem weil es mir wirklich klar gemacht hat, wie mächtig Go wirklich ist. Mit weniger Arbeitsspeicher als Google Chrome, weniger Speicher als eine Microsoft-App und weniger CPU-Leistung als der Windows-Rechner konnte ich einen alten Prozess verbessern, der sich buchstäblich brutal durchschlug (er überprüfte buchstäblich jede Zeile in der Datenbank, bevor er eingefügt wurde. Ich nicht Ich weiß nicht, wie die vorherige Person es für eine gute Idee hielt.). Es hat wirklich Spaß gemacht.

Wie auch immer, teilen Sie uns gerne Ihre Gedanken zu diesem gesamten Prozess mit, wie würden Sie vorgehen und was hätten Sie anders gemacht? Da ich keine Entwicklerkollegen habe, möchte ich mehr Meinungen dazu erfahren.

Das obige ist der detaillierte Inhalt vonWie ich über Einträge pro Anfrage verarbeitet habe – Mit Go!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn