1 puan yazan GN⁺ 2024-05-12 | 1 yorum | WhatsApp'ta paylaş

ActiveSupport::Notifications ile ilgili bellek sızıntısını çözme sürecinin özeti

  • Bellek sızıntısının ortaya çıktığı durum

    • Belirli bir andan itibaren web Dyno'nun bellek kullanımı anormal biçimde artmaya başladı
    • Pager çalmaya başladı ve bellek sızıntısı gibi görünen bir durum oluştu
  • Anlık müdahale

    • Heroku'da bellek sızıntısından şüpheleniliyorsa Dyno'yu yeniden başlatmak geçici bir çözüm olabilir
    • Normal deploy döngüsüne göre yeniden başlatmak veya bellek sınırına yaklaşan Dyno'ları elle yeniden başlatmak
  • Nedeni belirlemek için şüpheli kodların incelenmesi

    • Bellek sıçramasından hemen önce deploy edilen kod değişiklikleri incelendi
    • Neden olabileceğinden şüphelenilen bazı kodlar tek tek deploy edilerek bellek sızıntısı oluşturup oluşturmadıkları kontrol edildi
    • Neden gibi görünen bir kod bulunamadığı için tooling değişiklikleri de geri alınıp kontrol edildi. Ancak bellek sızıntısı devam etti
  • Bellek artış deseninin analizi

    • Sızıntı yalnızca web Dyno'da oluşuyordu. Sidekiq ve Delayed::Job Dyno'ları normaldi
    • Tüm web Dyno'larda her zaman sızıntı olmuyordu. Birkaç saat normal kullanımın ardından bir veya iki Dyno'da ya da tüm Dyno'larda sızıntı başlıyordu
    • Trafik miktarından çok belirli bir trafik türü tarafından tetiklendiğinden şüphelenildi
    • Dyno içindeki tüm Puma worker'larında sızıntı oluşmuyordu; az sayıdaki worker toplam belleğin büyük bölümünü kullanıyordu
  • Heap dump toplama ve analiz

    • rbtrace kullanılarak sızıntı yaşanan Ruby process'inin heap dump'ı toplandı
      • heroku ps:exec ile sızıntı yaşanan dyno'ya ssh bağlantısı kuruldu
      • ps komutuyla en çok bellek kullanan Ruby worker process'i seçildi
      • rbtrace ile ilgili pid'e attach olunup bellek tahsisi izleme başlatıldı (ObjectSpace.trace_object_allocations_start)
      • ObjectSpace.dump_all ile heap dump toplandı. Boyut büyükse gzip ile sıkıştırıldı
      • heroku ps:copy ile dump dosyası lokale alındı
    • reap kullanılarak heap dump flamegraph olarak görselleştirildi
      • 1.9GB belleğe referans veren bir Thread ve onun altında 32.067 nesneye referans veren bir Array bulundu
    • sheap kullanılarak şüpheli nesneler incelendi
      • İlgili Thread'in Puma'nın worker thread'i olduğu ortaya çıktı
      • ActiveSupport::SubscriberQueueRegistry nesnesinin bir Hash'e referans verdiği, onun altında da String ve Array nesnelerinin bulunduğu görüldü
      • Sorunlu Array içinde 32.000'den fazla ActiveSupport::Notifications::Event nesnesi birikmişti
  • Nedene dair çıkarım

    • ActiveSupport::Notifications içindeki Event nesnelerinin #children array'ine yanlış şekilde biriktiği tahmin edildi
    • ActiveSupport::Notifications.instrument block'u içinde hata oluşursa ilgili Event nesnesinin #children içinden çıkarılmadan kaldığı ve bunun bellek sızıntısına yol açtığı düşünüldü
  • Lokalde yeniden üretim

    • Production'da bulunan şüpheli request path ve parametrelerle lokal ortamda istek gönderildi
    • 500 Internal Server Error ile birlikte URI::InvalidURIError oluştuğu doğrulandı
    • Bu isteği alan production dyno'nun bellek kullanımının hızla arttığı görüldü
  • Somut neden analizi

    • Rails 7.1'de düzeltilmiş olan ActiveSupport::Notifications içindeki Event#children ile ilgili bir bug vardı
    • Buna ek olarak Bugsnag gem'inde request URL'ini temizleme sırasında URI.parse içinde URI::InvalidURIError fırlatan bir bug da üst üste gelince bellek sızıntısı oluştu
    • ActiveSupport::Notifications.subscribe block'u içinde fırlatılan hata yakalanmadığı için ilgili Event nesnesi #children array'inden çıkarılmadan birikmeye devam etti ve bellek sızıntısı oluştu
  • Çözüm yaklaşımı

    • Kısa vadede: URI::InvalidURIError oluştuğunda da hata fırlatmayan bir sürüme geçmek için Bugsnag gem'i upgrade edildi
    • Uzun vadede: ActiveSupport::Notifications bug'ının düzeltildiği Rails 7.x sürümüne upgrade edilecek

GN⁺ görüşü

  • Sorunun fark edilmesi ve nedenin sistematik biçimde adım adım bulunma süreci etkileyici. Bellek sızıntısından şüphelenildiğinde uygulanabilecek temel analiz adımları iyi özetlenmiş
  • Ruby'de heap dump toplama, görselleştirme ve analiz için çeşitli açık kaynak araçların (rbtrace, reap, sheap vb.) aktif biçimde geliştirildiği görülüyor. Yalnızca Ruby için değil, farklı dillerde de yararlı bellek analiz araçlarını bilmek ve probleme uygulayabilmek önemli görünüyor
  • Aslında bellek sızıntılarının nedeni çoğu zaman kullanılan belirli bir kütüphane veya framework bug'ı oluyor; ancak bu bug'ı doğrudan analiz edip düzeltip deploy etmek her zaman mümkün olmadığından, dolaşılabilir bir çözümü mümkün olduğunca hızlı uygulamak önemli. Bug raporuyla birlikte uygulanabilir alternatifler sunmak da iyi bir yaklaşım
  • Yalnızca bellek sızıntısını gidermekle kalmayıp problemin root cause'unu derinlemesine araştırmış olmaları da değerli. Framework'ün iç kodunu dikkatle inceleyip temel nedene kadar iz sürmeye çalışan analiz yaklaşımı geliştiriciler için gerekli görünüyor
  • Sonuçta bellek sızıntısının nedeni, başta tamamen alakasız görünen küçük bir kütüphane sürüm güncellemesiydi. Bu da bağımlılık yönetimi ve değişiklik takibinin önemini gösteren bir örnek. Küçük değişikliklerde bile etki analizi dikkatle yapılmalı ve deploy sonrasında da izleme sürdürülmeli

1 yorum

 
GN⁺ 2024-05-12
Hacker News yorumu

Manuel bellek yönetimi korkusu olmadan mühendislik disipliniyle çözülebilir

  • Yalnızca RAII ve net sahiplik kuralları varsa bellek yönetimi kolay bir mühendislik işidir
  • Hatta referans sayımı ve paylaşımlı pointer'larda ısrar eden framework'ler sahipliği belirsizleştirerek işi daha da zorlaştırır
  • Oluşturursan serbest bırakırsın, devredersen artık düşünmezsin; bu mühendislik disiplininin bir parçasıdır
  • Bellek hataları mantık hatalarından farklı değildir, bu yüzden düzeltmek doğaldır
  • OS kaynakları da (handle, socket vb.) otomatik kaynak yöneticileri olmadan manuel yönetildiği için belleğe de aynı şekilde yaklaşılabilir

Bellek sızıntısı nedeniyle 5 milyon dolarlık kayıp vakası

  • 90'larda Solaris yazıcı sürücüsündeki bir bellek sızıntısı hatasından kaynaklanan bir anekdot anlatılıyor
  • O dönemde bankalar işlemleri faksla doğruluyor, yazıcıdan çıktı alıp karşı tarafa telefonda okuyarak ve bunu kaydederek yasal onay alıyordu
  • Bellek sızıntısı yüzünden yazıcı sürücüsü çökünce çıktısı alınamayan teyit belgesi nedeniyle işlem iptal edildi ve 5 milyon dolar zarar edildi
  • Sonunda Sun CEO'sunun şikâyeti üzerine geliştiriciler hatayı düzeltmek zorunda kaldı

Bellek sızıntısı hata ayıklama araçları ve çözüm yolları

  • Valgrind kullanılırsa C'de sızıntıları kolayca bulmak mümkündür
  • Tasarım düzgün yapılmışsa çoğu zaman allocation ve free aynı fonksiyonda olur, bu yüzden düzeltmesi kolaydır
  • Yahoo reklam sunucusundaki bellek sızıntısı vakası ve geçici çözüm anlatılıyor
  • PHP tasarımcısının şaka alıntısı üzerinden mükemmeliyetçilikten ziyade pragmatizmi seçen bir tutum gösteriliyor
  • Rails'te üretkenlik için sorunu donanımla çözmenin yaygın olduğu söyleniyor

Yazı stiline övgü

  • Yazarın yazı tarzının emojiler ya da biçimlendirme nedeniyle keyifli olduğuna dair bir yorum