Tiba-tiba, sistem pengumpulan sampah saja tidak cukup

Berikut adalah cerita pendek tentang kegagalan server misterius yang saya harus debug setahun yang lalu (artikel bertanggal 05 Desember 2018, kira-kira). Server berfungsi dengan baik untuk sementara waktu, dan kemudian di beberapa titik mulai crash. Setelah ini, upaya untuk menjalankan hampir semua program yang ada di server gagal dengan kesalahan "Tidak ada ruang pada perangkat", meskipun sistem file melaporkan hanya beberapa gigabyte yang diduduki pada disk ~ 20 GB.

Ternyata masalahnya disebabkan oleh sistem logging. Ini adalah aplikasi Ruby yang mengambil file log, mengirim data ke server jauh, dan menghapus file lama. Kesalahannya adalah bahwa file log terbuka tidak ditutup secara eksplisit. Sebagai gantinya, aplikasi tersebut memungkinkan pengumpul sampah otomatis Ruby untuk membersihkan objek File. Masalahnya adalah bahwa objek File tidak mengkonsumsi banyak memori, sehingga sistem logging secara teoritis dapat menjaga jutaan log terbuka sebelum pengumpulan sampah diperlukan.

* Sistem file Nix memisahkan nama file dan data dalam file. Data pada disk dapat memiliki beberapa nama file yang menunjuk ke mereka (mis. Tautan keras), dan data dihapus hanya ketika tautan terakhir dihapus. Deskriptor file terbuka dianggap sebagai tautan, jadi jika file tersebut dihapus saat program sedang membaca, nama file menghilang dari direktori, tetapi data file tetap hidup sampai program menutupnya. Inilah yang terjadi pada penebang. Perintah du ("penggunaan disk") mencari file menggunakan daftar direktori, sehingga tidak melihat gigabytes data file untuk ribuan file log yang masih terbuka. File-file ini ditemukan hanya setelah menjalankan lsof ("daftar buka file").

Tentu saja, kesalahan serupa terjadi dalam kasus serupa lainnya. Beberapa bulan yang lalu, saya harus menjalankan aplikasi Java yang mogok setelah beberapa hari karena kebocoran koneksi jaringan.

Saya biasa menulis sebagian besar kode saya di C, dan kemudian di C ++. Pada masa itu, saya pikir manajemen sumber daya manual sudah cukup. Betapa rumitnya itu? Setiap malloc () membutuhkan fungsi gratis (), dan setiap open () perlu close (). Secara sederhana. Kecuali bahwa tidak semua program sederhana, sehingga manajemen sumber daya manual dari waktu ke waktu telah menjadi jaket pengikat. Lalu suatu hari saya menemukan penghitungan tautan dan pengumpulan sampah. Saya pikir itu menyelesaikan semua masalah saya, dan benar-benar berhenti memperhatikan manajemen sumber daya. Sekali lagi, untuk program sederhana, ini normal, tetapi tidak semua program sederhana.

Anda tidak dapat mengandalkan pengumpulan sampah karena hanya menyelesaikan masalah manajemen memori, dan program yang kompleks harus berurusan dengan lebih dari sekadar memori. Ada meme populer yang menjawab ini dengan fakta bahwa memori adalah 95% dari masalah sumber daya . Anda bahkan bisa mengatakan bahwa semua sumber daya adalah 0% dari masalah Anda - sampai Anda kehabisan salah satunya. Maka sumber daya ini menjadi 100% dari masalah Anda.

Tetapi pemikiran seperti itu masih menganggap sumber daya sebagai kasus khusus. Masalah yang lebih dalam adalah bahwa ketika program menjadi lebih kompleks, semuanya cenderung menjadi sumber daya. Misalnya, ambil program kalender. Program kalender canggih memungkinkan banyak pengguna untuk mengelola beberapa kalender bersama, dan dengan acara yang dapat dibagikan di banyak kalender. Setiap bagian dari data pada akhirnya akan mempengaruhi beberapa bagian dari program, dan harus relevan dan benar. Karena itu, untuk semua data dinamis, Anda memerlukan pemilik, dan bukan hanya untuk manajemen memori. Ketika fitur baru ditambahkan, semakin banyak bagian dari program yang perlu diperbarui. Jika Anda waras, Anda hanya akan memperbolehkan Anda memperbarui data dari satu bagian program sekaligus,sehingga hak dan tanggung jawab untuk memperbarui data menjadi sumber daya yang terbatas. Pemodelan data yang bermutasi menggunakan struktur yang tidak dapat diubah tidak mengarah pada hilangnya masalah ini, tetapi hanya menerjemahkannya ke dalam paradigma lain.

Merencanakan kepemilikan dan sumber daya seumur hidup adalah bagian tak terhindarkan dari desain perangkat lunak yang kompleks. Ini lebih mudah jika Anda menggunakan beberapa pola umum. Salah satu polanya adalah sumber daya yang dapat dipertukarkan. Contohnya adalah string "foo" yang tidak berubah, yang secara semantik sama dengan "foo" yang tidak berubah lainnya. Jenis sumber daya ini tidak memerlukan masa hidup atau kepemilikan yang telah ditentukan sebelumnya. Bahkan, untuk membuat sistem sesederhana mungkin, lebih baik tidak memiliki seumur hidup atau kepemilikan yang telah ditentukan (hi Rust, kira-kira). Pola lainnya adalah sumber daya yang tidak dapat dipertukarkan, tetapi memiliki rentang hidup yang ditentukan. Ini termasuk koneksi jaringan, serta konsep yang lebih abstrak, seperti hak untuk mengontrol bagian dari data.Hal yang paling masuk akal adalah memastikan umur hal-hal seperti itu ketika menyandikan.

Perhatikan bahwa pengumpulan sampah otomatis sangat baik untuk menerapkan pola pertama, tetapi tidak yang kedua, sementara teknik manajemen sumber daya manual (seperti RAII) bagus untuk menerapkan pola kedua, tetapi buruk untuk yang pertama. Kedua pendekatan ini menjadi pelengkap dalam program yang kompleks.

Source: https://habr.com/ru/post/undefined/


All Articles