Das Memory-Leak-Problem von Copilot
(stevenharman.net)Zusammenfassung des Lösungsprozesses für einen Memory Leak im Zusammenhang mit ActiveSupport::Notifications
-
Situation, in der der Memory Leak auftrat
- Ab einem bestimmten Zeitpunkt begann die Speichernutzung des
web-Dynos ungewöhnlich stark anzusteigen - Der Pager schlug Alarm, und es entstand eine Situation, die wie ein Memory Leak aussah
- Ab einem bestimmten Zeitpunkt begann die Speichernutzung des
-
Sofortige Reaktion
- Wenn auf Heroku ein Memory Leak vermutet wird, kann ein Neustart des Dynos vorübergehend Abhilfe schaffen
- Neustart passend zum normalen Deploy-Zyklus oder manuelles Neustarten von Dynos, die sich dem Memory-Limit nähern
-
Überprüfung verdächtiger Änderungen zur Ursachenfindung
- Prüfung der direkt vor dem Memory Spike deployten Codeänderungen
- Einige verdächtige Codeabschnitte wurden nacheinander deployt, um zu prüfen, ob der Memory Leak auftritt
- Da kein eindeutiger Verursacher im Code zu finden war, wurden auch Änderungen an den Tools zurückgerollt. Der Memory Leak blieb jedoch bestehen
-
Analyse des Musters des Speicheranstiegs
- Der Leak trat nur auf
web-Dynos auf. Sidekiq- und Delayed::Job-Dynos verhielten sich normal - Nicht alle
web-Dynos leakten ständig. Nach einigen Stunden normaler Nutzung begann der Leak bei ein oder zwei oder auch allen Dynos - Es entstand der Verdacht, dass nicht das Traffic-Volumen, sondern bestimmter Traffic der Auslöser ist
- Nicht alle Puma-Worker innerhalb eines Dynos leakten; stattdessen verbrauchten wenige Worker den Großteil des gesamten Speichers
- Der Leak trat nur auf
-
Sammlung und Analyse von Heap Dumps
- Mit
rbtracewurden Heap Dumps von Ruby-Prozessen gesammelt, bei denen der Leak gerade auftrat- Per
heroku ps:execSSH-Verbindung zu einem leakkenden Dyno aufgebaut - Mit dem Befehl
psden Ruby-Worker-Prozess mit dem höchsten Speicherverbrauch ausgewählt - Mit
rbtracean die betreffende PID angehängt und die Verfolgung der Speicherallokationen gestartet (ObjectSpace.trace_object_allocations_start) - Mit
ObjectSpace.dump_alleinen Heap Dump gesammelt, bei großer Dateigröße per gzip komprimiert - Mit
heroku ps:copydie Dump-Datei lokal heruntergeladen
- Per
- Mit
reapwurde der Heap Dump als Flamegraph visualisiert- Es wurde ein Thread gefunden, der 1,9 GB Speicher referenzierte, und darunter ein Array, das 32.067 Objekte referenzierte
- Mit
sheapwurden verdächtige Objekte untersucht- Es stellte sich heraus, dass der betreffende Thread ein Worker-Thread von Puma war
- Ein Objekt vom Typ
ActiveSupport::SubscriberQueueRegistryreferenzierte einHash, darunter befanden sichString- undArray-Objekte - In dem problematischen
Arrayhatten sich mehr als 32.000 Objekte vom TypActiveSupport::Notifications::Eventangesammelt
- Mit
-
Herleitung der Ursache
- Es wurde vermutet, dass
Event-Objekte vonActiveSupport::Notificationsfälschlich im Array#childrenangesammelt werden - Wenn innerhalb des Blocks von
ActiveSupport::Notifications.instrumentein Fehler auftritt, wird das betreffendeEventoffenbar nicht aus#childrenentfernt und verursacht so vermutlich den Memory Leak
- Es wurde vermutet, dass
-
Lokale Reproduktion
- Lokal wurde mit dem in der Produktion gefundenen verdächtigen Request-Pfad und den entsprechenden Parametern eine Anfrage gesendet
- Dabei trat zusammen mit
500 Internal Server ErroreinURI::InvalidURIErrorauf - Es wurde bestätigt, dass die Speichernutzung des Production-Dynos, der diesen Request verarbeitet hatte, sprunghaft anstieg
-
Analyse der konkreten Ursache
- In Rails 7.1 gab es einen behobenen Bug im Zusammenhang mit
Event#childreninActiveSupport::Notifications - Dazu kam ein Bug im Bugsnag-Gem, bei dem beim Bereinigen der Request-URL während
URI.parseeinURI::InvalidURIErrorausgelöst wurde, was zusammen den Memory Leak verursachte - Da ein im Block von
ActiveSupport::Notifications.subscribeausgelöster Fehler nicht abgefangen wurde, wurde das betreffendeEventnicht aus dem Array#childrenentfernt und sammelte sich weiter an, was zum Memory Leak führte
- In Rails 7.1 gab es einen behobenen Bug im Zusammenhang mit
-
Lösungsansatz
- Kurzfristig: Upgrade des Bugsnag-Gems auf eine Version, die bei
URI::InvalidURIErrorkeinen Fehler mehr auslöst - Langfristig: Upgrade auf Rails 7.x, in dem der Bug in
ActiveSupport::Notificationsbehoben ist
- Kurzfristig: Upgrade des Bugsnag-Gems auf eine Version, die bei
Meinung von GN⁺
- Beeindruckend ist vor allem, wie das Problem entdeckt und die Ursache systematisch eingegrenzt wurde. Der Beitrag fasst einen sinnvollen grundlegenden Analyseprozess zusammen, den man bei Verdacht auf einen Memory Leak ausprobieren kann
- Für das Sammeln, Visualisieren und Analysieren von Heap Dumps in Ruby scheint es eine Reihe aktiv entwickelter Open-Source-Tools zu geben (
rbtrace,reap,sheapusw.). Auch außerhalb von Ruby ist es wichtig, nützliche Memory-Analyse-Tools je nach Sprache zu kennen und in Problemen anwenden zu können - Tatsächlich liegen die Ursachen von Memory Leaks oft in Bugs bestimmter Bibliotheken oder Frameworks. Da man solche Bugs jedoch nicht immer direkt selbst analysieren und fixen sowie deployen kann, ist es wichtig, möglichst schnell praktikable Workarounds anzuwenden. Sinnvoll ist auch, zusammen mit einem Bug Report mögliche Alternativen bereitzustellen
- Positiv ist außerdem, dass es nicht beim Beheben des Memory Leaks blieb, sondern tief in die Root Cause eingestiegen wurde. Diese analytische Haltung, internen Framework-Code sorgfältig zu untersuchen und bis zur eigentlichen Ursache vorzudringen, wirkt für Entwickler sehr wertvoll
- Letztlich lag die Ursache des Memory Leaks in einem auf den ersten Blick völlig unbedeutenden Library-Versionsupgrade. Das zeigt, wie wichtig Dependency-Management und die Nachverfolgung von Änderungen sind. Selbst kleine Änderungen sollten hinsichtlich ihrer Auswirkungen sorgfältig analysiert und nach dem Deploy weiter überwacht werden
1 Kommentare
Hacker-News-Kommentar
Lässt sich ohne Angst vor manueller Speicherverwaltung durch technische Disziplin lösen
Beispiel für einen Verlust von 5 Millionen Dollar durch ein Memory Leak
Debugging-Tools und Lösungsansätze für Memory Leaks
Lob für den Schreibstil