
Ini adalah artikel kedua dalam seri tentang bagaimana kami di Citymobil meningkatkan stabilitas layanan (Anda dapat membaca yang pertama di
sini ). Dalam artikel ini, saya akan mempelajari secara spesifik analisis kecelakaan. Tetapi sebelum itu saya akan membahas satu hal yang harus saya pikirkan terlebih dahulu dan membahas di artikel pertama, tetapi saya tidak memikirkannya. Dan tentang yang saya pelajari dari umpan balik pembaca. Artikel kedua memberi saya kesempatan untuk menghilangkan cacat yang mengganggu ini.
0. Prolog
Salah satu pembaca mengajukan pertanyaan yang sangat adil: "Apa yang sulit di backend dari layanan taksi?" Pertanyaannya bagus. Saya bertanya pada diri sendiri pada musim panas lalu sebelum mulai bekerja di Citymobil. Saya kemudian berpikir "pikir, taksi, aplikasi dengan tiga tombol." Apa yang bisa rumit tentang itu? Tetapi ternyata ini adalah layanan yang sangat berteknologi tinggi dan produk yang kompleks. Untuk setidaknya memperjelas tentang apa itu dan apa itu raksasa teknologi, saya akan berbicara tentang beberapa bidang kegiatan produk Citymobil:
- Harga Tim penetapan harga menangani masalah harga di setiap titik dan pada waktu tertentu. Harga ditentukan dengan memprediksi keseimbangan penawaran dan permintaan berdasarkan statistik dan data lainnya. Semua ini membuat layanan besar, kompleks dan terus berkembang berdasarkan pembelajaran mesin.
- Harga Penerapan berbagai metode pembayaran, logika biaya tambahan setelah perjalanan, menahan dana pada kartu bank, penagihan, interaksi dengan mitra dan driver.
- Distribusi pesanan. Ke mesin mana untuk mendistribusikan pesanan penjualan? Misalnya, opsi distribusi untuk yang terdekat bukan yang terbaik dalam hal meningkatkan jumlah perjalanan. Pilihan yang lebih tepat adalah membandingkan pelanggan dan mobil sedemikian rupa untuk memaksimalkan jumlah perjalanan, mengingat kemungkinan pembatalan oleh klien khusus ini dalam kondisi ini (karena itu membutuhkan waktu lama) dan pembatalan atau sabotase pesanan oleh pengemudi ini (karena terlalu lama atau terlalu rendah) periksa).
- Geo. Segala sesuatu yang berkaitan dengan pencarian dan saran alamat, titik pendaratan, penyesuaian waktu pengiriman (mitra kami, pemasok kartu dan kemacetan lalu lintas tidak selalu memberikan informasi yang akurat tentang ETA, dengan memperhitungkan kemacetan lalu lintas akun), meningkatkan akurasi geocoding langsung dan mundur, meningkatkan akurasi mesin. Ada banyak pekerjaan dengan data, banyak analitik, banyak layanan berdasarkan pembelajaran mesin.
- Antifraud. Perbedaan harga perjalanan untuk penumpang dan pengemudi (misalnya, dalam perjalanan singkat) menciptakan insentif ekonomi bagi penipu yang mencoba mencuri uang kita. Memerangi penipuan agak mirip dengan memerangi spam di layanan email - kelengkapan dan akurasi penting. Penting untuk memblokir jumlah maksimum penipu (kelengkapan), tetapi pengguna yang baik tidak boleh keliru untuk penipu (akurasi).
- Motivasi pengemudi. Tim motivasi pengemudi terlibat dalam pengembangan segala sesuatu yang berkaitan dengan peningkatan penggunaan platform kami oleh pengemudi dan loyalitas pengemudi karena berbagai jenis motivasi. Misalnya, buat perjalanan X dan dapatkan rubel Y tambahan untuk ini. Atau beli shift untuk Z rubel dan kendarai tanpa komisi.
- Aplikasi driver backend. Daftar pesanan, peta permintaan (petunjuk ke mana harus pergi ke driver untuk memaksimalkan pendapatan Anda), perubahan status prokidyvaniya, sistem komunikasi dengan driver dan banyak lagi.
- Bagian belakang aplikasi klien (ini mungkin adalah bagian yang paling jelas, dan apa yang biasanya dipahami oleh bagian belakang taksi): menempatkan pesanan, menggulir status tentang mengubah status pesanan, memastikan pergerakan mobil di peta pada pesanan dan pengiriman, tips backend dan dll.
Ini semua adalah puncak gunung es. Fungsionalitas jauh lebih banyak. Antarmuka yang ramah pengguna menyembunyikan bagian bawah air besar dari gunung es.
Dan sekarang kembali ke kecelakaan. Selama enam bulan dalam sejarah kecelakaan, kami telah menyusun kategorisasi berikut:
- rilis buruk, kesalahan ke-500;
- rilis buruk, kode suboptimal, memuat di pangkalan;
- intervensi manual yang gagal dalam sistem;
- telur paskah;
- penyebab eksternal;
- rilis buruk, fungsionalitas rusak.
Di bawah ini saya akan menuliskan kesimpulan apa yang kami buat tentang jenis kecelakaan yang paling umum.
1. Rilis buruk, kesalahan ke-500
Hampir semua backend kami ditulis dalam PHP, bahasa yang ditafsirkan dengan mengetik yang lemah. Itu terjadi bahwa Anda mengeluarkan kode, dan crash karena kesalahan dalam nama kelas atau fungsi. Dan ini hanya satu contoh ketika kesalahan ke-500 muncul. Mungkin juga muncul jika terjadi kesalahan logis dalam kode; menjilat cabang yang salah; folder secara tidak sengaja terhapus dengan kode; tersisa di kode artefak sementara yang diperlukan untuk pengujian; tidak mengubah struktur tabel sesuai dengan kode baru; tidak memulai ulang atau menghentikan skrip cron yang diperlukan.
Kami berjuang dengan masalah ini secara berurutan dalam beberapa tahap. Hilangnya perjalanan karena pembebasan yang buruk jelas sebanding dengan waktu digunakan. Artinya, kita harus melakukan yang terbaik untuk memastikan bahwa rilis yang buruk beroperasi sesedikit mungkin. Setiap perubahan dalam proses pengembangan yang mengurangi waktu rata-rata yang diperlukan untuk mendapatkan rilis yang buruk untuk digunakan setidaknya 1 detik adalah positif untuk bisnis dan perlu diimplementasikan.
Pelepasan yang buruk atau kecelakaan produksi umumnya melewati dua negara, yang kami sebut "tahap pasif" dan "tahap aktif". Tahap pasif adalah ketika kita belum menyadari kecelakaan itu. Tahap aktif adalah ketika kita sudah tahu. Kecelakaan itu dimulai pada tahap pasif, dan seiring waktu, ketika kita mengetahuinya, kecelakaan itu memasuki tahap aktif - kita mulai melawannya: pertama kita mendiagnosis dan kemudian memperbaikinya.
Untuk mengurangi durasi kecelakaan dalam produksi, perlu untuk mengurangi durasi rata-rata dari kedua tahap pasif dan aktif. Hal yang sama berlaku untuk rilis yang buruk, karena itu sendiri semacam kecelakaan.
Kami mulai menganalisis proses perbaikan kecelakaan kami saat ini. Rilis buruk yang kami temui pada saat dimulainya analisis menghasilkan rata-rata menganggur (penuh atau sebagian) 20-25 menit. Tahap pasif biasanya memakan waktu 15 menit, aktif 10 menit. Selama fase pasif, keluhan pengguna mulai diproses oleh pusat kontak, dan setelah beberapa ambang batas pusat kontak mengeluh tentang obrolan umum di Slack. Terkadang salah satu karyawan mengeluh ketika dia tidak bisa memesan taksi. Keluhan karyawan adalah sinyal bagi kami tentang masalah serius. Setelah transisi rilis buruk ke tahap aktif, kami mulai mendiagnosis masalah, menganalisis rilis terbaru, berbagai grafik dan log untuk menentukan penyebab kecelakaan. Setelah mengetahui alasannya, kami memutar kembali kode jika rilis buruk dipompa terakhir, atau melakukan rollback baru dengan rilis buruk melakukan pembalikan.
Berikut adalah proses untuk menangani rilis buruk, kami harus meningkatkan.
1.1. Pengurangan tahap pasif
Pertama-tama, kami perhatikan bahwa jika rilis yang buruk disertai dengan 500 kesalahan, maka kami dapat memahami tanpa keluhan bahwa masalah telah terjadi. Untungnya, semua kesalahan ke-500 dicatat dalam New Relic (ini adalah salah satu sistem pemantauan yang kami gunakan), dan itu hanya tinggal mengacaukan pemberitahuan SMS dan IVR tentang kelebihan frekuensi tertentu "lima ratus" (seiring waktu, ambang batas terus dikurangi).
Hal ini menyebabkan fakta bahwa tahap aktif dari kecelakaan seperti "Rilis buruk, kesalahan ke-500" dimulai segera setelah rilis. Proses jika terjadi kecelakaan mulai terlihat seperti ini:
- Programmer menyebarkan kode.
- Rilis ini menyebabkan kecelakaan (500-an masif).
- SMS datang.
- Pemrogram dan admin mulai memahami (kadang-kadang tidak langsung, tetapi setelah 2-3 menit: SMS mungkin tertunda, suara di telepon mungkin dimatikan, dan budaya tindakan segera setelah SMS tidak dapat muncul dalam satu hari).
- Fase aktif kecelakaan dimulai, yang berlangsung sama 10 menit seperti sebelumnya.
Dengan demikian, tahap pasif berkurang dari 15 menit menjadi 3.
1.2. Pengurangan lebih lanjut dari tahap pasif
Meskipun pengurangan tahap pasif menjadi 3 menit, bahkan tahap pasif singkat seperti itu lebih mengganggu kami daripada yang aktif, karena selama tahap aktif kami sudah melakukan sesuatu untuk menyelesaikan masalah, dan selama tahap pasif layanan tidak bekerja secara keseluruhan atau sebagian, tetapi β laki-laki tidak tahu. "
Untuk lebih mengurangi tahap pasif, kami memutuskan untuk mengorbankan waktu pengembang selama tiga menit setelah setiap rilis. Idenya sangat sederhana: Anda mengeluarkan kode dan melihat New Relic, Sentry, Kibana selama tiga menit untuk melihat apakah ada 500 kesalahan. Segera setelah Anda melihat masalah di sana, apriori Anda menganggap bahwa itu terkait dengan kode Anda dan Anda mulai mengerti.
Kami memilih tiga menit berdasarkan statistik: kadang-kadang masalah muncul pada grafik dengan penundaan 1-2 menit, tetapi tidak pernah lebih dari tiga menit.
Aturan ini dicatat dalam do's & dont's. Pada awalnya itu tidak selalu dilakukan, tetapi secara bertahap pengembang terbiasa dengan aturan sebagai kebersihan dasar: menyikat gigi di pagi hari juga membuang-buang waktu, tetapi Anda perlu melakukan ini.
Akibatnya, tahap pasif dikurangi menjadi 1 menit (jadwal kadang-kadang masih terlambat). Sebagai kejutan yang menyenangkan, ini secara bersamaan mengurangi tahap aktif. Bagaimanapun, pengembang menghadapi masalah dalam kondisi yang baik dan siap untuk segera memutar kembali kodenya. Meskipun ini tidak selalu membantu, karena masalahnya bisa muncul karena kode orang lain yang diluncurkan secara paralel. Tetapi, bagaimanapun, tahap aktif rata-rata dikurangi menjadi 5 menit.
1.3. Pengurangan lebih lanjut dalam tahap aktif
Kurang lebih puas dengan satu menit dari tahap pasif, kami mulai berpikir tentang pengurangan lebih lanjut pada tahap aktif. Pertama-tama, kami memperhatikan sejarah masalah (itu adalah landasan dalam membangun stabilitas kami!) Dan menemukan bahwa dalam banyak kasus kami tidak segera mundur karena kami tidak mengerti versi mana yang harus diputar kembali, karena ada banyak rilis paralel. Untuk mengatasi masalah ini, kami memperkenalkan aturan berikut (dan mencatatnya dalam do's & dont's): sebelum rilis, Anda menulis ke obrolan di Slack, untuk apa Anda bergulir dan untuk apa, dan jika terjadi kecelakaan Anda menulis ke obrolan "kecelakaan, jangan roll!". Selain itu, kami mulai melaporkan secara otomatis melalui SMS tentang fakta rilis untuk memberi tahu mereka yang tidak masuk obrolan.
Aturan sederhana ini secara tajam mengurangi jumlah rilis selama kecelakaan dan mengurangi tahap aktif - dari 5 menit menjadi 3.
1.4. Pengurangan bahkan lebih besar dalam tahap aktif
Terlepas dari kenyataan bahwa kami memperingatkan dalam obrolan tentang semua rilis dan crash, kadang-kadang kondisi lomba muncul - satu menulis tentang rilis, dan yang lainnya sudah diluncurkan pada saat itu; atau kecelakaan dimulai, mereka menulis tentang hal itu dalam obrolan, dan seseorang baru saja meluncurkan kode baru. Keadaan ini memperpanjang diagnosis. Untuk mengatasi masalah ini, kami menerapkan larangan otomatis untuk rilis paralel. Idenya sangat sederhana: setelah setiap rilis, sistem CI / CD melarang semua orang untuk meluncurkan selama 5 menit berikutnya, kecuali penulis rilis terakhir (sehingga ia dapat roll atau roll perbaikan terbaru jika perlu) dan beberapa pengembang berpengalaman (jika terjadi keadaan darurat). Selain itu, sistem CI / CD melarang peluncuran selama kecelakaan (yaitu, sejak saat diterimanya pemberitahuan awal kecelakaan hingga saat diterimanya pemberitahuan penyelesaiannya).
Dengan demikian, prosesnya menjadi seperti ini: pengembang meluncurkan, memantau grafik selama tiga menit, dan setelah itu selama dua menit lagi tidak ada yang bisa mengeluarkan apa pun. Jika ada masalah, maka pengembang memutar kembali rilis. Aturan ini secara radikal menyederhanakan diagnosis, dan total durasi tahapan aktif dan pasif menurun dari 3 + 1 = 4 menit menjadi 1 + 1 = 2 menit.
Tapi dua menit kecelakaan itu banyak. Karena itu, kami terus mengoptimalkan proses.
1.5. Deteksi crash dan rollback otomatis
Kami telah lama berpikir bagaimana mengurangi durasi kecelakaan karena rilis yang buruk. Mereka bahkan mencoba memaksakan diri untuk mencari di
tail -f error_log | grep 500
tail -f error_log | grep 500
. Tetapi pada akhirnya, mereka semua memilih solusi otomatis utama.
Singkatnya, ini adalah rollback otomatis. Kami membuat server web yang terpisah, tempat kami memuat 10 kali lebih sedikit dari penyeimbang daripada server web lainnya. Setiap rilis secara otomatis dikerahkan oleh sistem CI / CD ke server yang terpisah ini (kami menyebutnya preprod, meskipun, terlepas dari namanya, beban nyata dari pengguna nyata pergi ke sana). Dan kemudian otomasi melakukan
tail -f error_log | grep 500
tail -f error_log | grep 500
. Jika tidak ada kesalahan ke-500 terjadi dalam satu menit, maka CI / CD menyebarkan kode baru dalam produksi. Jika kesalahan muncul, maka sistem segera memutar kembali semuanya. Pada saat yang sama, pada tingkat penyeimbang, semua permintaan yang dilengkapi dengan 500 kesalahan pada preprod digandakan ke salah satu server produksi.
Ukuran ini mengurangi efek dari rilis ke lima ratus menjadi nol. Pada saat yang sama, jika ada bug dalam otomatisasi, kami tidak membatalkan aturan selama tiga menit untuk memantau grafik. Itu semua tentang rilis buruk dan bug ke-500. Kami melanjutkan ke jenis kecelakaan berikutnya.
2. Rilis buruk, kode suboptimal, beban dasar
Saya akan segera mulai dengan contoh nyata dari kecelakaan jenis ini. Kami meluncurkan pengoptimalan: kami menambahkan
USE INDEX
ke kueri SQL, selama pengujian kueri dipercepat ini, seperti dalam produksi, tetapi kueri panjang melambat. Perlambatan permintaan panjang hanya terlihat dalam produksi. Akibatnya, aliran permintaan panjang menempatkan seluruh basis induk selama satu jam. Kami benar-benar memahami cara kerja
USE INDEX
, menggambarkannya dalam file do & dont, dan memperingatkan pengembang agar tidak disalahgunakan. Kami juga menganalisis kueri dan menyadari bahwa itu terutama mengembalikan data historis, yang berarti dapat dijalankan pada replika terpisah untuk kueri historis. Bahkan jika replika ini berada di bawah beban, bisnis tidak akan berhenti.
Setelah kejadian ini, kami masih mengalami masalah yang sama, dan pada beberapa titik memutuskan untuk mendekati masalah secara sistematis. Kami memindai seluruh kode dengan sisir yang sering dan melakukan semua permintaan yang dapat dibuat di sana tanpa mengurangi kualitas layanan. Pada saat yang sama, kami membagi replika sendiri sesuai dengan tingkat kekritisan sehingga jatuhnya salah satu dari mereka tidak akan menghentikan layanan. Akibatnya, kami sampai pada arsitektur yang memiliki basis berikut:
- master base (untuk operasi tulis dan untuk kueri yang superkritis terhadap kesegaran data);
- replika produksi (untuk pertanyaan pendek yang sedikit kurang penting untuk kesegaran data);
- replika untuk menghitung rasio harga, yang disebut penetapan harga. Replika ini dapat tertinggal 30-60 detik - ini tidak penting, koefisiennya tidak terlalu sering berubah, dan jika replika ini jatuh, layanan tidak akan berhenti, hanya saja harga tidak akan cukup sesuai dengan keseimbangan penawaran dan permintaan;
- replika untuk panel admin pengguna bisnis dan pusat kontak (jika jatuh, bisnis utama tidak akan naik, tetapi dukungan tidak akan berfungsi dan kami tidak akan dapat melihat dan mengubah pengaturan untuk sementara waktu);
- banyak replika untuk analitik;
- Basis data MPP untuk analitik berat dengan irisan penuh sesuai dengan data historis.
Arsitektur ini memberi kami lebih banyak ruang untuk pertumbuhan dan mengurangi jumlah kerusakan dengan urutan besarnya karena kueri SQL yang tidak optimal. Tapi dia masih jauh dari ideal. Berencana untuk melakukan sharding sehingga Anda dapat mengukur pembaruan dan penghapusan, serta kueri pendek yang superkritis untuk kesegaran data ini. Margin keamanan MySQL tidak terbatas. Kami akan segera membutuhkan artileri berat dalam bentuk Tarantool. Tentang ini akan diperlukan dalam artikel berikut!
Dalam proses uji coba dengan kode dan permintaan yang tidak optimal, kami menyadari hal berikut: lebih baik untuk menghilangkan segala non-optimalitas sebelum rilis, dan bukan setelahnya. Ini mengurangi risiko kecelakaan dan mengurangi waktu yang dihabiskan oleh pengembang untuk pengoptimalan. Karena jika kode telah diunduh dan ada rilis baru di atasnya, maka itu jauh lebih sulit untuk dioptimalkan. Sebagai hasilnya, kami memperkenalkan pemeriksaan kode wajib untuk optimalitas. Ini dilakukan oleh pengembang yang paling berpengalaman, pada kenyataannya, pasukan khusus kami.
Selain itu, kami mulai mengumpulkan di do's & dont's metode optimisasi kode terbaik yang bekerja di realitas kami, mereka tercantum di bawah ini. Tolong jangan menganggap praktik-praktik ini sebagai kebenaran absolut dan jangan mencoba mengulanginya secara membuta dalam diri Anda. Setiap metode hanya masuk akal untuk situasi tertentu dan bisnis tertentu. Mereka diberikan di sini hanya sebagai contoh, sehingga spesifiknya jelas:
- Jika kueri SQL tidak bergantung pada pengguna saat ini (misalnya, peta permintaan untuk driver yang menunjukkan tingkat perjalanan minimum dan koefisien untuk poligon), maka kueri ini harus dilakukan oleh cron dengan frekuensi tertentu (dalam kasus kami, satu menit sudah cukup). Tulis hasilnya ke cache (Memcached atau Redis), yang sudah digunakan dalam kode produksi.
- Jika kueri SQL beroperasi dengan data yang simpanannya tidak penting untuk bisnis, maka hasilnya harus di-cache dengan beberapa TTL (misalnya, 30 detik). Dan kemudian dalam permintaan selanjutnya baca dari cache.
- Jika dalam konteks memproses permintaan di web (dalam kasus kami, dalam konteks implementasi metode server tertentu dalam PHP) Anda ingin membuat kueri SQL, Anda perlu memastikan bahwa data ini belum βtibaβ dengan kueri SQL lainnya (dan apakah mereka akan datang lebih jauh dengan kode). Hal yang sama berlaku untuk mengakses cache: itu juga dapat dibanjiri dengan permintaan jika Anda mau, oleh karena itu, jika data telah "tiba" dari cache, maka Anda tidak perlu pergi ke cache ke rumah Anda dan mengambilnya, yang sudah diambil.
- Jika dalam konteks pemrosesan kueri di web Anda ingin memanggil fungsi apa pun, maka Anda perlu memastikan bahwa tidak ada kueri SQL tambahan atau akses cache yang akan dibuat dalam jeroan ayam itiknya. Jika memanggil fungsi seperti itu tidak dapat dihindari, Anda perlu memastikan bahwa itu tidak dapat dimodifikasi atau logikanya terputus sehingga tidak membuat pertanyaan yang tidak perlu ke database / cache.
- Jika Anda masih perlu masuk ke SQL, maka Anda perlu memastikan bahwa Anda tidak bisa menambahkan bidang yang diperlukan lebih tinggi atau lebih rendah dalam kode ke permintaan yang ada dalam kode.
3. Intervensi manual yang gagal dalam sistem
Contoh-contoh kecelakaan seperti itu: ALTER yang gagal (yang membebani database atau memicu jeda replika) atau DROP yang tidak berhasil (mengalami bug di MySQL, memblokir database ketika sebuah tabel baru dijatuhkan); permintaan yang sangat besar untuk seorang master yang melakukan kesalahan dengan tangan; Kami melakukan pekerjaan pada server di bawah beban, meskipun kami pikir itu tidak berfungsi.
Untuk meminimalkan jatuh karena alasan ini, sayangnya, perlu untuk memahami sifat kecelakaan setiap kali. Kami belum menemukan aturan umum. Sekali lagi, coba contohnya. Katakanlah, pada titik tertentu, koefisien lonjakan berhenti bekerja (mereka melipatgandakan harga perjalanan di tempat dan waktu meningkatnya permintaan). Alasannya adalah bahwa pada replika database, dari mana data untuk menghitung koefisien berasal, skrip Python bekerja, yang memakan semua memori, dan replika turun. Script telah berjalan untuk waktu yang lama, ini bekerja pada replika hanya untuk kenyamanan. Masalahnya dipecahkan dengan memulai kembali skrip. Kesimpulannya adalah sebagai berikut: jangan jalankan skrip pihak ketiga pada mesin dengan database (direkam dalam do & jangan, jika tidak, ini adalah bidikan kosong!), Monitor akhir memori pada mesin dengan replika dan waspada melalui SMS jika memori segera habis.
Sangat penting untuk selalu menarik kesimpulan dan tidak tergelincir ke dalam situasi yang nyaman "mereka melihat masalah, memperbaikinya dan melupakannya." Layanan yang berkualitas hanya dapat dibangun jika kesimpulan diambil. Selain itu, peringatan SMS sangat penting - mereka menetapkan kualitas layanan pada tingkat yang lebih tinggi daripada itu, mencegahnya jatuh dan semakin meningkatkan keandalan. Sebagai pendaki dari setiap kondisi stabil, ia menarik dirinya ke atas dan tetap dalam kondisi stabil lain, tetapi pada ketinggian yang lebih tinggi.
Memantau dan mengingatkan dengan kait besi yang tidak terlihat tetapi kaku memotong batu ketidakpastian dan tidak pernah membiarkan kita jatuh di bawah tingkat stabilitas yang kita tetapkan, yang kita terus-menerus hanya menaikkan.
4. Telur paskah
Apa yang kita sebut "Telur Paskah" adalah bom waktu yang telah ada sejak lama, tetapi belum kita temui. Di luar artikel ini, istilah ini merujuk ke fitur tidak berdokumen yang dibuat dengan sengaja. Dalam kasus kami, ini bukan fitur sama sekali, melainkan bug, tetapi yang berfungsi seperti bom waktu dan yang merupakan efek samping dari niat baik.
Sebagai contoh: overflow
auto_increment
32 bit; tidak optimal dalam kode / konfigurasi, "tembakan" karena beban; replika lagging (biasanya karena permintaan suboptimal untuk replika yang dipicu oleh pola penggunaan baru, atau beban yang lebih tinggi, atau karena UPDATE suboptimal pada master yang dipanggil oleh pola beban baru dan memuat replika).
Jenis lain dari telur Paskah yang populer adalah kode yang tidak optimal, dan lebih khusus lagi, query SQL yang tidak optimal. Sebelumnya, tabel lebih kecil dan beban kurang - kueri bekerja dengan baik. Dan dengan peningkatan dalam tabel, linear dalam pertumbuhan waktu dan beban, linear dalam waktu, konsumsi sumber daya DBMS tumbuh kuadratik. Biasanya ini mengarah ke efek negatif yang tajam: semuanya "ok", dan bang.
Skenario yang lebih jarang adalah kombinasi bug dan telur paskah. Rilis dengan bug menyebabkan peningkatan ukuran tabel atau peningkatan jumlah catatan dalam tabel jenis tertentu, dan telur Paskah yang sudah ada menyebabkan beban berlebihan pada database karena permintaan yang lebih lambat ke tabel yang terlalu besar ini.
Meskipun, kami juga memiliki telur Paskah, tidak terkait dengan beban. Misalnya,
auto increment
32-bit: setelah dua dan beberapa miliar rekaman dalam tabel, sisipan berhenti dilakukan. Jadi bidang
auto increment
di dunia modern harus dibuat 64-bit. Kami mempelajari pelajaran ini dengan baik.
Bagaimana cara mengatasi telur Paskah? Jawabannya sederhana: a) mencari "telur" lama, dan b) mencegah yang baru muncul. Kami berusaha memenuhi kedua poin. Pencarian untuk "telur" lama di negara kita dikaitkan dengan optimasi kode konstan. Kami mengidentifikasi dua pengembang paling berpengalaman untuk pengoptimalan mendekati waktu penuh. Mereka menemukan di kuerilog lambat yang mengkonsumsi sebagian besar sumber daya basis data, mengoptimalkan kueri ini dan kode di sekitarnya. Kami mengurangi kemungkinan telur baru dengan memeriksa kode optimalitas setiap commit oleh sensei rezrabotchiki yang disebutkan di atas. Tugas mereka adalah menunjukkan kesalahan yang memengaruhi kinerja; memberi tahu Anda cara melakukan yang lebih baik, dan mentransfer pengetahuan ke pengembang lain.
Pada titik tertentu setelah telur paskah berikutnya yang kami temukan, kami menyadari bahwa mencari permintaan yang lambat itu baik, tetapi akan lebih baik jika mencari permintaan yang terlihat seperti lambat tetapi bekerja dengan cepat. Ini hanyalah kandidat berikutnya untuk meletakkan segala sesuatu jika terjadi ledakan pertumbuhan tabel berikut.
5. Penyebab eksternal
Ini adalah alasan yang menurut kami kurang terkontrol oleh kami. Sebagai contoh:
- Berlari dengan Google Maps. Anda dapat mengatasinya dengan memantau penggunaan layanan ini, mengamati tingkat beban tertentu, merencanakan pertumbuhan beban di muka, dan membeli perluasan layanan.
- Jatuhnya jaringan di pusat data. Anda dapat berkeliling dengan menempatkan salinan layanan di pusat data cadangan.
- Kecelakaan layanan pembayaran. Anda dapat melewati reservasi layanan pembayaran.
- Pemblokiran lalu lintas yang salah oleh layanan perlindungan DDoS. Anda dapat menyiasati dengan menonaktifkan layanan perlindungan DDoS default dan mengaktifkannya hanya jika terjadi serangan DDoS.
Karena menghilangkan penyebab eksternal adalah pekerjaan yang panjang dan mahal (menurut definisi), kami baru mulai mengumpulkan statistik kecelakaan karena penyebab eksternal dan menunggu akumulasi massa kritis. Tidak ada resep untuk menentukan massa kritis. Itu hanya bekerja dengan intuisi. Sebagai contoh, jika kita 5 kali dalam downtime penuh karena masalah, katakanlah, dari layanan kontrol DDoS, maka dengan setiap penurunan berikutnya akan menjadi lebih tajam dan lebih tajam untuk mengajukan pertanyaan tentang alternatif.
Di sisi lain, jika Anda dapat membuatnya bekerja dengan layanan eksternal yang tidak dapat diakses, maka kami pasti melakukannya. Dan ini membantu kita analisis post-mortem dari setiap musim gugur. Harus selalu ada kesimpulan. Itu berarti Anda selalu ingin-tidak-ingin, tetapi Anda dapat menemukan solusi.
6. Rilis buruk, fungsionalitas rusak
Ini adalah jenis kecelakaan yang paling tidak menyenangkan. Satu-satunya jenis kecelakaan yang tidak terlihat untuk gejala selain keluhan pengguna / bisnis. Oleh karena itu, kecelakaan seperti itu, terutama jika tidak besar, dapat terjadi tanpa disadari dalam produksi untuk waktu yang lama.
Semua jenis kecelakaan lainnya kurang lebih mirip dengan "rilis buruk, kesalahan ke-500". Hanya saja pemicunya bukan rilis, tetapi beban, operasi manual atau masalah di sisi layanan eksternal.
Untuk menggambarkan metode menangani jenis kecelakaan ini, cukup dengan mengingat anekdot berjanggut:
Matematika dan fisika ditawari tugas yang sama: merebus ketel. Alat bantu diberikan: kompor, ketel, keran air dengan air, korek api. Keduanya secara bergantian menuangkan air ke dalam ketel, menyalakan gas, menyalakannya dan menyalakan ketel. Kemudian tugas itu disederhanakan: sebuah ketel diisi dengan air dan kompor dengan gas pembakaran diusulkan. Tujuannya sama - untuk merebus air. Fisikawan itu membuat ketel terbakar. Matematikawan menuangkan air dari ketel, mematikan gas dan berkata: "Tugas telah dikurangi menjadi yang sebelumnya." anekdotov.net
Jenis kecelakaan ini harus dikurangi dengan segala cara menjadi "rilis buruk, kesalahan ke-500". Idealnya, jika bug dalam kode disimpan ke log sebagai kesalahan. Ya, atau setidaknya meninggalkan jejak dalam database. Dari penelusuran ini, Anda dapat memahami bahwa bug telah terjadi, dan segera memberi peringatan. Bagaimana cara berkontribusi ini? Kami mulai menganalisis setiap bug utama dan menawarkan solusi, pemantauan / peringatan SMS macam apa yang dapat dilakukan sehingga bug ini segera memanifestasikan dirinya dengan cara yang sama dengan kesalahan ke-500.
6.1. Contoh
Ada keluhan besar: pesanan yang dibayarkan melalui Apple Pay tidak ditutup. Mereka mulai mengerti, masalahnya terulang. Kami menemukan alasannya: kami membuat perbaikan pada format
expire date
untuk kartu bank ketika berinteraksi dengan perolehan, sebagai akibatnya mereka mulai mentransfernya secara khusus untuk pembayaran melalui Apple Pay dalam format yang diharapkan dari layanan pemrosesan pembayaran (pada kenyataannya, satu dapat diobati, melukai sesuatu yang lain), sehingga semua pembayaran melalui Apple Pay mulai menurun. Dengan cepat diperbaiki, diluncurkan, masalahnya hilang. Tapi mereka "hidup" dengan masalah selama 45 menit.
Mengikuti jejak masalah ini, kami memantau jumlah pembayaran yang gagal melalui Apple Pay, dan juga membuat peringatan SMS / IVR dengan beberapa ambang tidak nol (karena pembayaran yang gagal adalah norma dari sudut pandang layanan, misalnya, klien tidak memiliki uang pada kartu atau kartu diblokir) . Mulai saat ini, ketika ambang batas terlampaui, kami langsung belajar tentang masalah tersebut. Jika rilis baru memperkenalkan masalah APAPUN ke dalam pemrosesan Apple Pay, yang akan menyebabkan layanan tidak dapat beroperasi, bahkan sebagian, kami akan segera mempelajarinya dari pemantauan dan memutar kembali rilis tersebut dalam waktu tiga menit (penjelasan di atas menjelaskan cara kerja proses pengguliran manual). Itu adalah 45 menit downtime parsial, menjadi 3 menit. Untung
6.2. Contoh lainnya
Kami meluncurkan optimalisasi daftar pesanan yang ditawarkan kepada pengemudi. Bug menyusup ke dalam kode. Akibatnya, pengemudi dalam beberapa kasus tidak melihat daftar pesanan (kosong). Mereka mengetahui tentang bug secara tidak sengaja - salah satu karyawan melihat ke dalam aplikasi pengemudi. Digulung dengan cepat. Sebagai kesimpulan dari kecelakaan itu, kami membuat grafik jumlah rata-rata pesanan dalam daftar driver sesuai dengan basis data, melihat grafik secara retroaktif selama sebulan, melihat kegagalan di sana dan membuat peringatan SMS untuk kueri SQL, yang membentuk grafik ini ketika jumlah rata-rata pesanan di daftar di bawah ambang yang dipilih berdasarkan minimum historis untuk bulan tersebut.
Mengubah logika pemberian uang kembali kepada pengguna untuk perjalanan. Termasuk didistribusikan ke grup pengguna yang salah. Kami memperbaiki masalah, membangun jadwal pengembalian uang tunai, melihat peningkatan tajam di sana, juga melihat bahwa tidak pernah ada pertumbuhan seperti itu, membuat peringatan SMS.
Dengan rilis ini, fungsi penutupan pesanan rusak (pesanan ditutup selamanya, pembayaran dengan kartu tidak berfungsi, pengemudi meminta pembayaran tunai dari pelanggan). Masalahnya adalah 1,5 jam (total tahap pasif dan aktif). Kami belajar tentang masalah dari pusat kontak pengaduan. Mereka melakukan koreksi, melakukan pemantauan dan peringatan pada waktu penutupan pesanan dengan ambang batas yang ditemukan dari studi grafik historis.
Seperti yang Anda lihat, pendekatan untuk jenis kecelakaan ini selalu sama:
- Luncurkan rilis.
- Pelajari tentang masalahnya.
- Perbaiki
- Kami menentukan dengan jejak apa (dalam database, log, Kiban) yang dapat Anda temukan tanda-tanda masalahnya.
- Kami merencanakan tanda-tanda ini.
- Kami memundurkannya ke masa lalu dan melihat semburan / jatuh.
- Kami memilih ambang batas yang benar untuk lansiran.
- Ketika masalah muncul lagi, kami segera mempelajarinya melalui peringatan.
Apa yang baik tentang metode ini: kelas masalah besar ditutup segera dengan satu bagan dan peringatan (contoh kelas masalah: tidak ditutupnya pesanan, bonus tambahan, tidak dibayar melalui Apple Pay, dll.).
Seiring waktu, kami menjadikan peringatan bangunan dan pemantauan untuk setiap bug utama sebagai bagian dari budaya pembangunan. Untuk mencegah agar budaya ini tidak hilang, kami memformalkannya sedikit. Untuk setiap kecelakaan, mereka mulai menuntut laporan dari diri mereka sendiri. Laporan adalah formulir yang dilengkapi dengan jawaban atas pertanyaan-pertanyaan berikut: akar penyebab, metode penghapusan, dampak pada bisnis, kesimpulan. Semua item wajib diisi. Karena itu, apakah Anda mau atau tidak, Anda akan menulis kesimpulan. Perubahan proses ini, tentu saja, ditulis oleh do's & dont's.
7. Kotan
, , -, . - ( , ) «». «». :-)
«» :
. β , . , ( ), ( ) , . ( , ).
. , . , : β , β . , Β« 500- 1 %Β» β . Β« 500- 1 %, - , - , - Β» β . , . ( ). , : , «», , , , . β . ( , ). .
. . , ( , , ), , : , , , .
. , , ( , ).
8. ?
β . . : , . , , . , , , .. β , β ! , . , , ? , , .. , , .
. . ( , ), , : , , , . , , . . . -, , . , , , β : .
9.
, .
? | ? |
---|
.
| .
|
( ) post-mortem.
| .
|
do's & dont's.
| , , .
|
, 5 .
| .
|
, .
| .
|
.
| .
|
| .
|
.
| .
|
.
| .
|
SMS/IVR- .
| .
|
( ) .
| .
|
.
| - .
|
( β slow.log).
| - Β« Β».
|
.
| .
|
.
| .
|
.
| , , .
|
«» β .
| , .
|
.
| .
|
, ! , , , , !