Bagaimana Cara Menyelamatkan Black Friday Kami

Sebelumnya, kami berbicara tentang bagaimana, seiring bertambahnya beban, kami secara bertahap meninggalkan penggunaan Python di backend layanan kritis pada produksi, menggantikannya dengan Go. Dan hari ini saya, Denis Girko, ketua tim dari tim pengembangan Madmin, ingin berbagi perincian: bagaimana dan mengapa ini terjadi pada contoh salah satu layanan paling penting bagi bisnis kami - menghitung harga dengan memperhitungkan potongan harga pada kupon.



Mekanisme bekerja dengan kupon mungkin diwakili oleh siapa saja yang setidaknya pernah melakukan pembelian di toko online. Pada halaman khusus atau langsung di keranjang, Anda memasukkan nomor kupon, dan harga dihitung ulang sesuai dengan diskon yang dijanjikan. Perhitungan tergantung pada jenis diskon yang diberikan kupon - dalam persen, dalam bentuk jumlah tetap atau menggunakan beberapa matematika lainnya (misalnya, kami juga memperhitungkan poin akun dari program loyalitas, promosi toko, jenis barang, dll.). Tentu, pesanan sudah dikeluarkan dengan harga baru.

Bisnis senang dengan semua mekanisme ini bekerja dengan harga, tetapi kami ingin berbicara tentang layanan dari sudut pandang yang sedikit berbeda.

Bagaimana cara kerjanya


Untuk penetapan harga dengan mempertimbangkan semua kesulitan ini di backend, kami sekarang memiliki layanan terpisah. Namun, ia tidak selalu mandiri. Layanan muncul satu atau dua tahun setelah dimulainya toko online, dan pada 2016 itu adalah bagian dari monolit Python besar yang mencakup berbagai komponen untuk kegiatan pemasaran (Madmin). Dia berdiri sebagai "blok" independen kemudian, ketika ia bergerak ke arah arsitektur layanan mikro.

Seperti biasanya dengan monolit, Madmin dimodifikasi dan sebagian terkait dengan sejumlah besar pengembang. Perpustakaan pihak ketiga terintegrasi di sana, yang menyederhanakan pengembangan, tetapi seringkali tidak memiliki efek terbaik pada kinerja. Namun, pada saat itu, kami tidak terlalu peduli dengan penolakan terhadap beban berat selama penjualan, karena layanan melakukan pekerjaan dengan sangat baik. Namun 2016 telah mengubah segalanya.



Di AS, "Black Friday" telah dikenal sejak tahun 60-an abad terakhir. Di Rusia, itu mulai diluncurkan pada 2010-an, sementara tindakan harus dibuat dari awal - pasar tidak cukup siap untuk itu. Namun, upaya penyelenggara tidak sia-sia, dan setiap tahun lalu lintas pengguna ke situs kami meningkat selama hari penjualan. Dan oleh karena itu, tabrakan kami dengan beban, berlebihan untuk versi layanan perhitungan harga, hanya masalah waktu.

Black Friday 2016. Dan kami ketidurannya


Karena ide penjualan bekerja dengan potensi penuhnya, "Black Friday" berbeda dari hari-hari lain tahun ini, yaitu pada tengah malam, sekitar satu minggu pengunjung situs web datang ke toko. Ini adalah periode yang sulit untuk semua layanan. Bahkan di antara mereka yang beroperasi dengan lancar sepanjang tahun, masalah terkadang muncul.

Sekarang kami sedang mempersiapkan untuk setiap "Black Friday" baru, meniru beban yang diharapkan, tetapi pada tahun 2016 kami masih bertindak berbeda. Saat menguji Madmin sebelum hari penting, kami menguji ketahanan beban menggunakan skenario perilaku pengguna pada hari-hari biasa. Ternyata, tes ini tidak cukup mencerminkan situasi yang sebenarnya, karena pada "Black Friday" datang banyak orang dengan kupon yang sama. Akibatnya, layanan perhitungan harga dengan memperhitungkan diskon ini, tidak mampu mengatasi beban tiga kali lipat (dibandingkan dengan hari biasa), memblokir kemampuan untuk melayani pelanggan selama dua jam selama puncak terpanas penjualan.

Layanan "pergi" satu jam sebelum tengah malam. Semuanya dimulai dengan terputusnya koneksi ke database (MySQL pada waktu itu), setelah itu tidak semua salinan layanan perhitungan harga dapat terhubung kembali. Dan yang masih terhubung tidak tahan terhadap beban dan berhenti merespons, terjebak di kunci dasar.

Secara kebetulan, junior itu tetap bertugas saat itu, yang pada saat penurunan layanan sedang dalam perjalanan dari rumah kantor. Dia bisa terhubung ke masalah hanya ketika dia tiba di tempat itu dan memanggil "artileri berat" - petugas tugas darurat. Namun, bersama-sama mereka menormalkan situasi, hanya setelah dua jam.

Ketika proses dimulai, rincian mulai terbuka tentang betapa suboptimal layanan itu. Misalnya, ternyata untuk menghitung satu kupon, 28 kueri dibuat ke basis data (tidak mengherankan bahwa semuanya bekerja dengan pemanfaatan CPU 100%). Pengguna yang disebutkan di atas dengan kupon Black Friday yang sama tidak menyederhanakan situasinya, terlebih lagi untuk semua kupon kami memiliki penghitung aplikasi - sehingga setiap penggunaan menambah beban dengan merujuk ke penghitung ini.

2016 memberi kami banyak makanan untuk dipikirkan - terutama tentang bagaimana menyesuaikan pekerjaan kami dengan kupon dan tes sehingga situasi ini tidak terjadi lagi. Dan dalam angka-angka pada hari Jumat itu paling baik digambarkan oleh gambar ini:


Hasil Black Friday 2016

Black Friday 2017. Kami sedang mempersiapkan dengan serius, tetapi ...


Setelah menerima pelajaran yang baik, kami mempersiapkan terlebih dahulu untuk "Black Friday" berikutnya, setelah serius membangun kembali dan mengoptimalkan layanan. Sebagai contoh, kami akhirnya membuat dua jenis kupon: limit dan unlimited - untuk menghindari kunci pada akses simultan ke database, kami menghapus entri ke database dari skrip untuk menerapkan kupon populer. Secara paralel, 1-2 bulan sebelum "Black Friday", kami beralih dari MySQL ke PostgreSQL dalam layanan, yang, bersama dengan optimasi kode, mengurangi jumlah panggilan ke database dari 28 menjadi 4-5. Peningkatan ini memungkinkan untuk memperluas layanan pengujian ke persyaratan SLA - jawaban dalam 3 detik, 95 persen pada 600 RPS.

Tidak tahu berapa banyak perbaikan kami mempercepat pekerjaan versi lama dari layanan produksi, pada saat itu dua versi kode Python sedang dipersiapkan untuk Black Friday sekaligus - versi yang sudah sangat dioptimalkan dan kode yang benar-benar baru yang ditulis dari awal. Dalam produksi, yang kedua diluncurkan, yang diuji sebelum siang dan malam ini. Namun, ternyata "dalam pertempuran", sedikit kurang teruji.

Pada hari "darurat" dengan kedatangan arus utama pelanggan, beban pada layanan mulai tumbuh secara eksponensial. Beberapa permintaan diproses hingga dua menit. Karena pemrosesan yang lama dari beberapa permintaan, beban pada pekerja lain tumbuh.

Tugas utama kami adalah melayani lalu lintas yang sangat berharga untuk bisnis. Tetapi menjadi jelas bahwa "casting dengan besi" tidak menyelesaikan masalah dan dari waktu ke waktu jumlah pekerja yang sibuk akan mencapai 100%. Tidak tahu kemudian apa sebenarnya yang kita hadapi, kami memutuskan untuk mengaktifkan harakiri di uWSGI dan cukup memakukan permintaan panjang (yang diproses lebih dari 6 detik) untuk membebaskan sumber daya untuk yang normal. Dan itu sangat membantu untuk menolak - para pekerja mulai dibebaskan hanya beberapa menit sebelum mereka benar-benar kelelahan.

Beberapa saat kemudian, kami menemukan situasinya ... Ternyata ini adalah permintaan dengan keranjang yang sangat besar - dari 40 hingga 100 barang - dan dengan kupon tertentu yang memiliki batasan pada kisaran. Situasi ini tidak berhasil dengan kode baru. Ini menunjukkan pekerjaan yang salah dengan array, yang berubah menjadi rekursi tak terbatas. Sangat mengherankan bahwa kami kemudian menguji sebuah case dengan keranjang besar, tetapi tidak dalam kombinasi dengan kupon yang rumit. Sebagai solusi, kami hanya beralih ke versi kode yang berbeda. Benar, ini terjadi tiga jam sebelum akhir Black Friday. Mulai saat ini, semua keranjang mulai diproses dengan benar. Dan walaupun kami menyelesaikan rencana penjualan pada waktu itu, kami menghindari masalah global yang ajaib karena memuat lima kali sehari seperti biasanya.

Black Friday 2018


Pada 2018, untuk layanan yang sangat dimuat yang melayani situs, kami secara bertahap mulai menerapkan Go. Mengingat sejarah Black Friday sebelumnya, layanan perhitungan diskon adalah salah satu kandidat pertama untuk diproses.



Tentu saja, kita bisa menyimpan versi Python yang sudah "diuji dalam pertempuran", dan sebelum "Black Friday" yang baru kita bisa mematikan perpustakaan berat dan membuang kode yang tidak optimal. Namun, Golang sudah berakar pada saat itu dan terlihat lebih menjanjikan.

Kami beralih ke layanan baru musim panas ini, jadi sebelum penjualan berikutnya kami berhasil mengujinya dengan baik, termasuk pada peningkatan profil muatan.

Selama pengujian, ternyata kelemahan dalam hal beban tinggi tetap menjadi basis kami. Transaksi yang terlalu lama menyebabkan kami memilih seluruh kumpulan koneksi, dan permintaan mengantri. Jadi kami harus mengulang logika aplikasi sedikit, mengurangi penggunaan database seminimal mungkin (merujuk hanya ketika tidak ada yang bisa dilakukan tanpanya) dan menyimpan direktori dari database dan data kupon yang populer di Black Friday.

Benar, tahun ini kami membuat kesalahan besar dengan perkiraan beban: kami sedang mempersiapkan pertumbuhan 6-8 kali lipat di puncak dan mencapai kinerja layanan yang baik hanya untuk volume permintaan seperti itu (penambahan cache, fungsi eksperimental yang dinonaktifkan di muka, disederhanakan beberapa hal, disebarkan node Kubernetes tambahan dan bahkan server database untuk replika, yang pada akhirnya tidak diperlukan). Bahkan, lonjakan minat pengguna kurang, jadi semuanya berjalan seperti biasa. Waktu respons layanan tidak melebihi 50 ms pada 95 persentil.

Bagi kami, salah satu karakteristik terpenting adalah bagaimana skala aplikasi ketika tidak ada sumber daya yang cukup untuk satu salinan. Go menggunakan sumber daya perangkat keras lebih efisien, jadi dengan beban yang sama Anda perlu menjalankan lebih sedikit salinan (pada akhirnya melayani lebih banyak permintaan pada sumber daya perangkat keras yang sama). Tahun ini, di puncak penjualan, 16 contoh aplikasi bekerja, yang memproses rata-rata 300 permintaan per detik dengan puncak hingga 400 permintaan per detik, yang kira-kira dua kali lebih tinggi dari beban normal. Perhatikan bahwa tahun lalu, layanan Python membutuhkan 102 instance.

Tampaknya layanan on Go dari pendekatan pertama menutup semua kebutuhan kita. Tetapi Golang bukanlah "solusi satu atap untuk semua masalah." Itu tidak dapat dilakukan tanpa beberapa fitur. Sebagai contoh, kami harus membatasi jumlah utas yang dapat dimulai layanan pada simpul multiprosesor Kubernetes, sehingga ketika menskal tidak mengganggu aplikasi "tetangga" pada produksi (secara default, Go tidak memiliki batasan pada berapa banyak prosesor yang akan diambil). Untuk melakukan ini, kami menetapkan GOMAXPROCS di semua aplikasi di Go. Kami akan dengan senang hati mengomentari betapa berharganya hal ini - di tim kami ini hanyalah salah satu hipotesis mengenai cara menangani degradasi "tetangga".

"Pengaturan" lain adalah jumlah koneksi yang diadakan sebagai Keep-Alive. Klien http dan DB reguler di Go secara default hanya memiliki dua koneksi, jadi jika ada banyak permintaan kompetitif dan Anda perlu menghemat lalu lintas pengaturan koneksi TCP, masuk akal untuk meningkatkan nilai ini dengan mengatur MaxIdleConnsPerHost dan SetMaxIdleConns, masing-masing.

Namun, bahkan dengan "tikungan" manual ini, Golang memberi kami margin kinerja yang besar untuk penjualan di masa mendatang.

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


All Articles