1 poin oleh GN⁺ 2024-05-12 | 1 komentar | Bagikan ke WhatsApp

Rangkuman proses penyelesaian memory leak terkait ActiveSupport::Notifications

  • Situasi saat memory leak terjadi

    • Sejak titik tertentu, penggunaan memori pada Dyno web mulai meningkat secara tidak normal
    • Pager mulai berbunyi, dan muncul situasi yang tampak seperti memory leak
  • Respons segera

    • Di Heroku, jika dicurigai ada memory leak, solusi sementara yang bisa dilakukan adalah me-restart Dyno
    • Me-restart sesuai siklus deploy normal atau me-restart secara manual Dyno yang mendekati batas memori
  • Meninjau kode yang dicurigai untuk mengidentifikasi penyebab

    • Meninjau perubahan kode yang dirilis tepat sebelum memory spike
    • Men-deploy beberapa potong kode yang dicurigai satu per satu untuk memeriksa apakah memory leak terjadi
    • Karena tidak ada kode yang tampak sebagai penyebab, perubahan tooling juga dibatalkan deploy-nya untuk diperiksa. Namun memory leak tetap berlanjut
  • Analisis pola peningkatan memori

    • Leak hanya terjadi pada Dyno web. Dyno Sidekiq dan Delayed::Job normal
    • Tidak semua Dyno web selalu mengalami leak. Setelah beberapa jam penggunaan normal, satu-dua atau semua Dyno mulai leak
    • Diduga dipicu oleh traffic tertentu, bukan oleh besarnya jumlah traffic
    • Leak tidak terjadi di semua Puma worker dalam Dyno, melainkan hanya sebagian kecil worker yang menggunakan sebagian besar total memori
  • Pengumpulan dan analisis heap dump

    • Mengumpulkan heap dump dari Ruby process yang sedang leak menggunakan rbtrace
      • SSH ke dyno yang sedang leak dengan heroku ps:exec
      • Memilih Ruby worker process yang menggunakan memori paling besar dengan perintah ps
      • Attach ke pid tersebut dengan rbtrace lalu mulai melacak alokasi memori (ObjectSpace.trace_object_allocations_start)
      • Mengumpulkan heap dump dengan ObjectSpace.dump_all. Jika ukurannya besar, kompres dengan gzip
      • Mengambil file dump ke lokal dengan heroku ps:copy
    • Memvisualisasikan heap dump sebagai flamegraph menggunakan reap
      • Ditemukan sebuah Thread yang mereferensikan memori sebesar 1.9GB dan sebuah Array di bawahnya yang mereferensikan 32.067 objek
    • Menelusuri objek yang mencurigakan menggunakan sheap
      • Thread tersebut ternyata adalah worker thread milik Puma
      • Objek ActiveSupport::SubscriberQueueRegistry mereferensikan Hash, dan di bawahnya terdapat objek String dan Array
      • Di dalam Array bermasalah tersebut menumpuk lebih dari 32.000 objek ActiveSupport::Notifications::Event
  • Inferensi penyebab

    • Diduga objek Event dari ActiveSupport::Notifications tertumpuk secara keliru di array #children
    • Diperkirakan jika terjadi error di dalam block ActiveSupport::Notifications.instrument, Event tersebut tidak dihapus dari #children dan tetap tertinggal sehingga menyebabkan memory leak
  • Reproduksi secara lokal

    • Mengirim request secara lokal dengan request path dan parameter mencurigakan yang ditemukan di production
    • Memastikan muncul 500 Internal Server Error disertai URI::InvalidURIError
    • Memastikan penggunaan memori pada dyno production yang menerima request tersebut meningkat tajam
  • Analisis penyebab yang lebih spesifik

    • Ada bug terkait Event#children di ActiveSupport::Notifications yang telah diperbaiki di Rails 7.1
    • Di saat yang sama, ada bug pada gem Bugsnag yang me-raise URI::InvalidURIError saat melakukan URI.parse dalam proses membersihkan request URL, dan kombinasi keduanya menyebabkan memory leak
    • Karena error yang di-raise di dalam block ActiveSupport::Notifications.subscribe tidak tertangani, Event tersebut tidak dihapus dari array #children dan terus menumpuk, menyebabkan memory leak
  • Solusi

    • Jangka pendek: upgrade versi gem Bugsnag agar tidak me-raise error meskipun terjadi URI::InvalidURIError
    • Jangka panjang: upgrade ke Rails 7.x tempat bug ActiveSupport::Notifications sudah diperbaiki

Opini GN⁺

  • Proses menemukan masalah lalu mengidentifikasi penyebabnya secara sistematis sangat mengesankan. Tulisan ini merangkum dengan baik proses analisis dasar yang layak dicoba saat mencurigai adanya memory leak
  • Berbagai alat open source untuk pengumpulan, visualisasi, dan analisis heap dump Ruby (rbtrace, reap, sheap, dll.) tampak dikembangkan dengan aktif. Bahkan jika bukan Ruby, penting untuk memahami alat analisis memori yang berguna di tiap bahasa dan mampu menerapkannya pada masalah nyata
  • Sering kali penyebab memory leak memang berupa bug pada library atau framework yang digunakan, tetapi biasanya tidak ada kondisi untuk menganalisis, memperbaiki, lalu merilis bug tersebut secara langsung. Karena itu, yang penting adalah secepat mungkin menerapkan cara untuk menghindarinya. Menyertakan alternatif yang memungkinkan bersama bug report juga merupakan pendekatan yang baik
  • Bukan hanya berhenti pada perbaikan memory leak, artikel ini juga bagus karena menggali root cause masalah secara mendalam. Sikap analitis untuk menelusuri hingga ke penyebab mendasar dengan mencermati kode internal framework tampak penting bagi developer
  • Pada akhirnya, penyebab memory leak ternyata berasal dari upgrade versi library kecil yang awalnya tampak sama sekali tidak terkait. Ini menunjukkan pentingnya pengelolaan dependensi dan pelacakan perubahan. Bahkan untuk perubahan kecil, dampaknya perlu dianalisis dengan hati-hati dan tetap dimonitor setelah deploy

1 komentar

 
GN⁺ 2024-05-12
Opini Hacker News

Bisa diatasi lewat pelatihan engineering tanpa rasa takut pada manajemen memori manual

  • Dengan hanya RAII dan aturan kepemilikan yang jelas, manajemen memori adalah pekerjaan engineering yang mudah
  • Justru framework yang ngotot memakai reference counting dan shared pointer membuat kepemilikan jadi ambigu dan lebih sulit
  • Jika membuat sesuatu maka lepaskan, jika dipindahkan maka tidak perlu dipikirkan lagi; ini adalah bagian dari disiplin engineering
  • Bug memori pada dasarnya tidak berbeda dari bug logika, jadi wajar jika harus diperbaiki
  • Resource OS (handle, socket, dll.) juga dikelola manual tanpa pengelola resource otomatis, jadi memori pun bisa didekati dengan cara yang sama

Kasus kerugian 5 juta dolar akibat memory leak

  • Memperkenalkan anekdot tentang bug memory leak pada driver printer Solaris di era 90-an
  • Saat itu, bank memverifikasi transaksi lewat faks lalu mencetaknya, membacakannya ke pihak lawan lewat telepon sambil merekamnya sebagai konfirmasi hukum
  • Karena memory leak membuat driver printer crash dan konfirmasi tidak tercetak, transaksi dibatalkan dan menimbulkan kerugian 5 juta dolar
  • Pada akhirnya, para developer memperbaiki bug itu setelah CEO Sun mengeluh

Alat debugging memory leak dan cara mengatasinya

  • Dengan memakai Valgrind, kebocoran di C bisa ditemukan dengan mudah
  • Jika desainnya benar, biasanya alokasi dan pelepasan ada di fungsi yang sama sehingga mudah diperbaiki
  • Diperkenalkan kasus memory leak pada server iklan Yahoo dan solusi tambal sulamnya
  • Kutipan lelucon dari perancang PHP menunjukkan sikap yang memilih pragmatisme ketimbang perfeksionisme
  • Disebutkan bahwa di Rails, menyelesaikan masalah dengan hardware demi produktivitas adalah hal yang umum

Pujian terhadap gaya penulisan

  • Komentar bahwa cara penulis menulis terasa menyenangkan, mungkin karena emotikon atau formatting