Programer tukang ledeng, atau kisah satu kebocoran dan kesulitan mengatasinya

Itu Selasa, 25 Februari. Rilis versi sulit pada hari Sabtu, 22 Februari, sudah di masa lalu. Tampaknya semua yang terburuk ada di belakang, dan tidak ada yang menandakan masalah. Tetapi semuanya berubah pada satu waktu ketika kesalahan pemantauan muncul tentang kebocoran memori pada proses koordinator layanan kontrol akses.

Darimana? Perubahan besar terakhir dalam basis kode koordinator ada di versi sebelumnya lebih dari dua bulan lalu, dan setelah itu tidak ada yang luar biasa terjadi pada memori. Tetapi, sayangnya, jadwal pemantauannya bersikeras - ingatan koordinator itu mulai bocor di suatu tempat, genangan air besar dipamerkan di lantai layanan, yang berarti bahwa tim pipa ledeng punya banyak pekerjaan yang harus dilakukan.



Pertama kita membuat penyimpangan kecil. Antara lain, VLSI memungkinkan Anda untuk melacak jam kerja dan memantau akses dengan model wajah, sidik jari atau kartu akses. Pada saat yang sama, VLSI berkomunikasi dengan pengontrol titik akhir (kunci, pintu putar, terminal akses, dll.). Layanan terpisah berkomunikasi dengan perangkat. Ini pasif, berinteraksi dengan perangkat kontrol akses berdasarkan protokol mereka sendiri diimplementasikan melalui HTTP (S). Ini ditulis berdasarkan tumpukan standar untuk layanan di perusahaan kami: Database PostgreSQL, Python 3 digunakan untuk logika bisnis, diperluas dengan metode C / C ++ dari platform kami.

Node layanan web tipikal terdiri dari proses berikut:

  • Monitor adalah proses root.
  • Koordinator adalah proses anak dari monitor.
  • Proses kerja.

Monitor adalah proses layanan web pertama. Tugasnya adalah memanggil fork (), memulai proses koordinator anak dan memantau pekerjaannya. Koordinator adalah proses utama dari layanan web, dialah yang menerima permintaan dari perangkat eksternal, mengirim tanggapan dan menyeimbangkan beban. Koordinator mengirimkan permintaan ke proses kerja untuk dieksekusi, mereka mengeksekusinya, mentransfer respons ke memori bersama dan memberi tahu koordinator bahwa tugas telah selesai dan Anda dapat mengambil hasilnya.

Siapa yang harus disalahkan dan apa yang harus dilakukan?


Jadi, koordinator layanan kontrol akses berbeda dari koordinator layanan lain di perusahaan kami dengan keberadaan server web. Koordinator layanan lain bekerja tanpa kebocoran, jadi masalahnya harus dicari dalam konfigurasi kami. Lebih buruk lagi, pada tes berdiri, versi baru ada untuk waktu yang lama, dan tidak ada yang memperhatikan masalah memori pada mereka. Mereka mulai melihat lebih dekat dan menemukan bahwa ingatan mengalir hanya pada salah satu stan, dan itupun dengan berbagai keberhasilan - yang berarti bahwa masalahnya masih belum begitu mudah direproduksi.



Apa yang harus dilakukan? Bagaimana menemukan alasannya? Pertama-tama, kami mengambil dump memori dan mengirimkannya ke spesialis dari platform untuk dianalisis. Hasilnya - tidak ada apa-apa di dump: tidak ada alasan, tidak ada petunjuk ke arah mana untuk melihat lebih jauh. Kami memeriksa perubahan dalam kode koordinator dari versi sebelumnya - tiba-tiba kami melakukan beberapa pengeditan yang mengerikan, tetapi tidak segera memahami hal ini? Tapi tidak - hanya beberapa komentar yang ditambahkan ke kode koordinator, tetapi beberapa metode dipindahkan ke file baru - secara umum, tidak ada yang kriminal.

Mereka mulai melihat ke arah kolega kami - pengembang inti dari layanan kami. Mereka dengan yakin menyangkal kemungkinan keterlibatan dalam kemalangan kita, tetapi menawarkan untuk menanamkan pemantauan tracemalloc dalam layanan. Tidak lama setelah selesai, pada perbaikan terbaru kami menyelesaikan layanan, dengan cepat menguji, melepaskan ke pertempuran.

Dan apa yang kita lihat? Sekarang ingatan kita mengalir tidak hanya dengan cepat, tetapi juga sangat cepat - pertumbuhan telah menjadi eksponensial. Kami mengaitkan puncak pertama dengan kekuatan jahat dan faktor-faktor yang menyertainya, tetapi puncak kedua, beberapa jam setelah yang pertama, memperjelas bahwa butuh waktu terlalu lama untuk menunggu perbaikan terbaru berikutnya dilepaskan dengan perilaku darurat layanan tersebut. Oleh karena itu, kami mengambil hasil tracemalloc dan menambal layanan, memutar kembali pengeditan dengan pemantauan untuk mengembalikan setidaknya ke pertumbuhan linear.



Tampaknya kami memiliki hasil tracemalloc yang bekerja pada memori yang dialokasikan dalam Python, sekarang kita akan melihat mereka dan menemukan penyebab kebocoran, tetapi tidak ada di sana - data yang dikumpulkan tidak memiliki puncak 5,5 GB yang kami lihat pada grafik pemantauan. Memori maksimum yang digunakan hanya 250MB, dan bahkan traceMalloc memakannya 130MB. Ini sebagian dapat dijelaskan - tracemalloc memungkinkan Anda untuk melihat dinamika memori dalam Python, tetapi ia tidak tahu tentang alokasi memori dalam paket C dan C ++ yang diimplementasikan oleh platform kami. Dalam data yang diperoleh, itu tidak mungkin untuk menemukan sesuatu yang menarik, memori dialokasikan dalam volume yang dapat diterima untuk benda-benda biasa seperti aliran, string dan kamus - secara umum, tidak ada yang mencurigakan. Kemudian kami memutuskan untuk menghapus semua yang tidak perlu dari data, hanya menyisakan total konsumsi memori dan waktu, dan memvisualisasikannya.Meskipun visualisasi tidak membantu menjawab pertanyaan "apa yang terjadi" dan "mengapa", dengan bantuannya kami melihat korelasi dengan data dari pemantauan - yang berarti bahwa kami pasti memiliki masalah di suatu tempat, dan kami perlu mencarinya.



Pada saat itu, tim tukang ledeng kami kehabisan ide di mana mencari kebocoran. Untungnya, burung itu bernyanyi kepada kami bahwa satu perubahan besar dari platform memang terjadi - versi Python berubah dari 3,4 menjadi 3,7, dan ini adalah bidang pencarian yang sangat besar.

Kami memutuskan untuk mencari masalah yang berkaitan dengan kebocoran memori di Python 3.7 di Internet, karena pasti seseorang sudah menjumpai perilaku ini. Namun, Python 3.7 telah diterbitkan sejak lama, kami beralih hanya dengan pembaruan saat ini. Untungnya, jawaban atas pertanyaan kami ditemukan dengan cepat, dan ada juga masalah dan tarik-permintaan untuk memperbaiki masalah, dan dia sendiri dalam perubahan yang dibuat oleh pengembang Python.
Apa yang terjadi?

Dimulai dengan versi 3.7, perilaku kelas ThreadingMixIn telah berubah, dari mana kami mewarisi dari server web kami untuk memproses setiap permintaan dalam utas terpisah. Di kelas ThreadingMixIn, mereka menambahkan entri semua utas yang dibuat ke dalam array. Karena perubahan tersebut, instance kelas yang menangani koneksi perangkat tidak dibebaskan setelah selesai, dan pengumpul sampah di Python tidak dapat menghapus memori dari utas yang dihabiskan. Inilah yang menyebabkan pertumbuhan linier dari memori yang dialokasikan dalam proporsi langsung ke jumlah permintaan ke server kami.

Ini dia, kode berbahaya dari modul Python dengan lubang besar (kode dalam Python 3.5 ditampilkan di sebelah kiri sebelum perubahan, di sebelah kanan - di 3.7, setelah):



Mengetahui alasannya, kami dengan mudah menghilangkan kebocoran: di kelas pewaris kami, kami mengubah nilai bendera yang mengembalikan perilaku lama, dan hanya itu - kemenangan! Streaming dibuat seperti sebelumnya, tanpa menulis ke variabel kelas, tetapi kami mengamati gambar yang menyenangkan pada grafik pemantauan - kebocoran telah diperbaiki!



Sangat menyenangkan untuk menulis tentang ini setelah kemenangan. Kami mungkin bukan yang pertama kali mengalami masalah ini setelah beralih ke Python 3.7, tetapi kemungkinan besar bukan yang terakhir. Bagi kami sendiri, kami menyimpulkan bahwa kami membutuhkan:

  • Ambil pendekatan yang lebih serius untuk menilai konsekuensi yang mungkin dari perubahan besar, terutama jika keputusan lain yang diterapkan bergantung pada kita.
  • Jika terjadi perubahan global pada platform, seperti, misalnya, mengubah versi Python, periksa kode Anda untuk kemungkinan masalah.
  • Menanggapi setiap perubahan yang mencurigakan dalam jadwal pemantauan tidak hanya layanan tempur, tetapi juga yang menguji. Meskipun pengumpul sampah saat ini, ada kebocoran memori di Python juga.
  • Ada kebutuhan untuk berhati-hati dengan alat analisis memori seperti tracemalloc, karena menggunakannya secara salah dapat memperburuk keadaan.
  • Anda harus siap menghadapi kenyataan bahwa deteksi kebocoran memori akan membutuhkan kesabaran, ketekunan, dan sedikit pekerjaan detektif.

Baiklah, saya ingin mengucapkan terima kasih kepada semua orang yang telah membantu mengatasi pekerjaan pipa leding darurat dan sekali lagi kembali ke kapasitas kerja semula untuk layanan kami!

All Articles