Bug terkenal dan bagaimana menghindarinya pada contoh ClickHouse

Jika Anda menulis kode, bersiaplah untuk masalah. Mereka pasti akan, dan mereka harus diharapkan dari semua sisi: dari kode dan kompiler Anda, dari sistem operasi dan perangkat keras, dan pengguna kadang-kadang memunculkan "kejutan". Jika Anda mengubah skala cluster menjadi skala kosmik, maka perkirakan bug "space". Terutama ketika datang ke data dari lalu lintas internet.


Alexey Milovidov (o6CuFl2Q) akan berbicara tentang masalah yang paling konyol, mengecilkan hati dan putus asa dari pengalamannya dalam mengembangkan dan mendukung ClickHouse. Mari kita lihat bagaimana mereka harus didebug dan langkah-langkah apa yang harus diambil pengembang dari awal, sehingga akan ada lebih sedikit masalah.

Bug terkenal


Jika Anda menulis beberapa kode, segera bersiap untuk masalah.

Kesalahan dalam kode. Mereka akan diminta. Tetapi katakanlah Anda menulis kode yang sempurna, dikompilasi, tetapi bug akan muncul di kompiler dan kode tidak akan berfungsi dengan benar. Kami memperbaiki kompiler, semuanya dikompilasi - jalankan. Tapi (secara tak terduga) semuanya bekerja dengan tidak benar, karena ada bug di kernel OS juga .

Jika tidak ada bug di OS, mau tidak mau, mereka akan berada di perangkat keras . Sekalipun Anda menulis kode sempurna yang berfungsi sempurna pada perangkat keras sempurna, Anda masih akan menemui masalah, misalnya, kesalahan konfigurasi . Tampaknya Anda melakukan semuanya dengan benar, tetapi seseorang membuat kesalahan dalam file konfigurasi, dan semuanya tidak berfungsi lagi.

Ketika semua bug telah diperbaiki, pengguna akan menyelesaikannya, karena mereka terus-menerus menggunakan kode Anda "salah". Tetapi masalahnya jelas bukan pada pengguna, tetapi dalam kode: Anda menulis sesuatu yang sulit digunakan .

Mari kita lihat bug ini dengan beberapa contoh.

Bug konfigurasi


Penghapusan data . Kasus pertama dari latihan. Untungnya, bukan milikku dan bukan Yandex, jangan khawatir.

Pendahuluan terlebih dahulu. Arsitektur cluster pengurang peta (seperti Hadoop) terdiri dari beberapa server data (data node) yang menyimpan data, dan satu atau lebih server master yang mengetahui lokasi semua data di server.

Node data mengetahui alamat master dan terhubung ke sana. Wizard memantau di mana dan data apa yang harus ditemukan, dan memberikan perintah yang berbeda ke simpul data: "Unduh data X, Anda harus memiliki data Y, dan hapus data Z". Apa yang bisa salah?

Ketika file konfigurasi baru diunggah ke semua node data, mereka keliru terhubung ke master dari cluster lain, dan bukan ke mereka sendiri. Master melihat data tentang mana data node diinformasikan, memutuskan bahwa data itu tidak benar dan harus dihapus. Masalahnya diperhatikan ketika setengah dari data dihapus.


Bug paling epik adalah bug yang menyebabkan penghapusan data yang tidak disengaja.
Menghindari ini sangat sederhana.

Jangan hapus data . Misalnya, sisihkan di direktori terpisah atau hapus dengan penundaan. Pertama, kami mentransfer sehingga mereka tidak terlihat oleh pengguna, dan jika dia menemukan ada sesuatu yang hilang dalam beberapa hari, kami akan mengembalikannya kembali.

Jangan hapus data yang tidak terduga jika penyebabnya tidak diketahui . Secara terprogram membatasi dimulainya penghapusan data yang tidak dikenal: tak terduga, dengan nama aneh, atau jika jumlahnya terlalu banyak. Administrator akan melihat bahwa server tidak memulai dan menulis beberapa pesan, dan akan mengerti.

Jika program melakukan tindakan destruktif - isolasi pengujian dan produksi di tingkat jaringan(iptables). Misalnya, menghapus file atau mengirim e-mail adalah tindakan yang merusak karena akan "menghabiskan" perhatian seseorang. Beri ambang batas pada mereka: seratus surat dapat dikirim, dan untuk seribu tuliskan kotak centang keamanan, yang ditetapkan sebelum sesuatu yang buruk terjadi.

Konfigurasi . Contoh kedua sudah dari latihan saya.

Satu perusahaan yang baik entah bagaimana memiliki kluster ClickHouse yang aneh. Keanehannya adalah bahwa replika tidak disinkronkan. Ketika server dimulai ulang, itu tidak dimulai dan muncul pesan bahwa semua data salah: “Ada banyak data yang tidak terduga, saya tidak akan memulai. Kita harus mengatur benderanya force_restore_datadan mencari tahu. ”

Tidak ada yang bisa menemukannya di perusahaan - mereka hanya memasang bendera. Pada saat yang sama, setengah dari data menghilang di suatu tempat, menghasilkan grafik dengan kesenjangan. Para pengembang menoleh ke saya, saya pikir sesuatu yang menarik sedang terjadi, dan memutuskan untuk menyelidiki. Ketika pagi datang beberapa jam kemudian dan burung-burung mulai bernyanyi di luar jendela, saya menyadari bahwa saya tidak mengerti apa-apa.

Server ClickHouse menggunakan layanan ZooKeeper untuk koordinasi. ClickHouse menyimpan data, dan ZooKeeper menentukan server data apa yang harus diletakkan: menyimpan metadata tentang data apa yang seharusnya menjadi replika. ZooKeeper juga merupakan kluster - ia mereplikasi menurut algoritma konsensus terdistribusi yang sangat baik, dengan konsistensi yang ketat.

Sebagai aturan, ZooKeeper adalah 3 mesin, kadang-kadang 5. Semua mesin terdaftar dalam konfigurasi ClickHouse sekaligus, koneksi dibuat dengan mesin acak, berinteraksi dengannya, dan server ini mereplikasi semua permintaan.

Apa yang terjadi? Perusahaan memiliki tiga server ZooKeeper. Tetapi mereka tidak bekerja sebagai cluster tiga node , tetapi sebagai tiga node independen - tiga cluster dari satu node. One ClickHouse terhubung ke satu server dan menulis data. Replika ingin mengunduh data ini, tetapi tidak dapat ditemukan. Saat memulai ulang, server terhubung ke ZooKeeper lain: server ini melihat bahwa data yang digunakannya berlebihan, harus ditunda di suatu tempat. Dia tidak menghapusnya, tetapi mentransfernya ke direktori terpisah - dalam data ClickHouse tidak begitu mudah dihapus.

Saya memutuskan untuk memperbaiki konfigurasi ZooKeeper. Saya mengganti nama semua data dan membuat permintaan untuk ATTACHbagian data dari direktori detached/unexpeted_*.

Akibatnya, semua data dipulihkan, replika disinkronkan, tidak ada kerugian, grafik terus menerus. Perusahaan puas, bersyukur, seolah-olah mereka sudah lupa bagaimana semuanya berjalan buruk sebelumnya.

Ini adalah bug konfigurasi sederhana. Lebih banyak bug akan ada dalam kode.

Bug dalam kode


Kami menulis kode dalam C ++. Ini artinya kita sudah punya masalah.
Contoh berikutnya adalah bug nyata dari produksi pada cluster Yandex.Metrica (2015) - konsekuensi dari kode C ++. Bugnya adalah bahwa kadang-kadang pengguna alih-alih menanggapi permintaan menerima pesan kesalahan:

  • “Checksum tidak cocok, data rusak” - jumlah cek tidak cocok, data rusak - menakutkan!
  • "LRUCache menjadi tidak konsisten. Pasti ada bug di dalamnya ”- cache menjadi tidak konsisten, kemungkinan besar bug di dalamnya.

Kode yang kami tulis menginformasikan bahwa ada bug di sana.

" Checksum tidak cocok, data rusak ." Periksa jumlah blok data terkompresi diperiksa sebelum didekompresi. Biasanya kesalahan ini muncul ketika data rusak pada sistem file. Karena berbagai alasan, beberapa file berubah menjadi sampah ketika server dihidupkan ulang.

Tapi di sini ada kasus lain: Saya membaca file secara manual, jumlah cek cocok, tidak ada kesalahan. Setelah muncul, kesalahan tersebut direproduksi secara stabil atas permintaan berulang. Ketika server restart, kesalahan menghilang untuk sementara waktu, dan kemudian muncul kembali dengan stabil.

Mungkin masalahnya ada di RAM? Situasi tipikal adalah ketika bit mengalahkannya. Saya mencari di dmesg(kern.log), tetapi tidak ada pengecualian pemeriksaan mesin - mereka biasanya menulis ketika ada sesuatu yang salah dengan RAM. Jika server telah mengalahkan RAM, maka tidak hanya program saya akan bekerja secara tidak benar, tetapi semua yang lain akan menghasilkan kesalahan secara acak. Namun, tidak ada manifestasi kesalahan lainnya.

"LRUCache menjadi tidak konsisten. Pasti ada bug di dalamnya. " Ini adalah kesalahan yang jelas dalam kode, dan kami menulis dalam C ++ - mungkin akses memori? Tetapi tes di bawah AddressSanitizer, ThreadSanitizer, MemorySanitizer, UndefinedBehaviorSanitizer di CI tidak menunjukkan apa pun.

Mungkin beberapa kasus uji tidak tercakup? Saya mengumpulkan server dengan AddressSanitizer, menjalankannya pada produksi - tidak menangkap apa pun. Untuk beberapa waktu, kesalahan dihapus dengan mengatur ulang beberapa cache mark (cache sachet).

Salah satu aturan pemrograman mengatakan: jika tidak jelas apa bug itu, perhatikan dengan cermat kode, berharap menemukan sesuatu di sana. Saya melakukannya, menemukan bug, memperbaikinya - itu tidak membantu. Saya melihat tempat lain dalam kode - ada juga bug. Diperbaiki, sekali lagi tidak membantu. Saya memperbaikinya lagi, kode menjadi lebih baik, tetapi kesalahan masih belum hilang!

Sebab. Mencoba menemukan pola berdasarkan server, berdasarkan waktu, berdasarkan sifat beban - tidak ada yang membantu. Kemudian dia menyadari bahwa masalahnya hanya muncul pada salah satu kelompok, dan tidak pernah pada yang lain. Kesalahan tidak direproduksi begitu sering, tetapi selalu muncul di satu cluster setelah restart, dan semuanya bersih di yang lain.

Ternyata alasannya adalah bahwa pada cluster "masalah" mereka menggunakan fitur baru - kamus cache. Mereka menggunakan pengalokasi memori yang ditulis tangan ArenaWithFreeLists . Kami tidak hanya menulis dalam C ++, tetapi juga melihat semacam pengalokasi khusus - kami mengalami masalah dua kali.

ArenaWithFreeLists adalah bagian dari memori di mana memori dialokasikan secara berurutan dalam ukuran yang dapat dibagi dua: 16, 32, 64 byte. Jika memori dibebaskan, maka mereka membentuk daftar blok FreeLists yang terhubung secara tunggal.

Mari kita lihat kodenya.

class ArenaWithFreeLists
{
    Block * free_lists[16] {};
    static auto sizeToPreviousPowerOfTwo(size_t size)
    {
        return _bit_scan_reverse(size - 1);
    }

    char * alloc(size_t size)
    {
        const auto list_idx = findFreeListIndex(size);
        free_lists[list_idx] ->...
    }
}

Ini menggunakan fungsi _bit_scan_reversegaris bawah di awal.
Ada aturan yang tidak tertulis: "Jika suatu fungsi memiliki satu garis bawah pada awalnya, baca dokumentasi di atasnya sekali, dan jika dua, baca dua kali."
Kami mendengarkan dan membaca dokumentasinya: “int _bit_scan_reverse (int a). Atur dst ke indeks set bit tertinggi dalam integer 32-bit a. Jika tidak ada bit yang diatur dalam maka dst tidak terdefinisi . " Kami sepertinya menemukan masalah.

Dalam C ++, situasi ini dianggap mustahil untuk kompiler. Kompiler dapat menggunakan perilaku tidak terdefinisi, ini "ketidakmungkinan", sebagai asumsi untuk mengoptimalkan kode.

Kompiler tidak melakukan kesalahan - ia dengan jujur ​​menghasilkan instruksi perakitan bsr %edi, %eax. Tetapi, jika operandnya nol, instruksi tersebut memiliki bsrperilaku yang tidak ditentukan bukan pada level C ++, tetapi pada level CPU. Jika register sumber nol, maka register tujuan tidak berubah: ada beberapa sampah pada input, sampah ini juga akan tetap pada output.

Hasilnya tergantung pada tempat kompiler meletakkan instruksi ini. Terkadang fungsi dengan instruksi ini sebaris, kadang tidak. Dalam kasus kedua akan ada sesuatu seperti kode ini:

bsrl %edi, %eax
retq

Kemudian saya melihat contoh kode serupa dalam biner saya gunakan objdump.



Menurut hasil, saya melihat bahwa kadang-kadang register sumber dan register tujuan sama. Jika ada nol, maka hasilnya juga akan menjadi nol - semuanya baik-baik saja. Tetapi kadang-kadang register berbeda, dan hasilnya akan menjadi sampah.

Bagaimana bug ini memanifestasikan dirinya?

  • Kami menggunakan sampah sebagai indeks dalam array FreeLists. Alih-alih array, kami pergi ke beberapa alamat yang jauh dan mendapatkan akses memori.
  • Kami beruntung, hampir semua alamat terdekat diisi dengan data dari cache - kami merusak cache. Cache berisi offset file.
  • Kami membaca file di offset yang salah. Dari offset yang salah, kami mendapatkan jumlah cek. Tetapi tidak ada check-sum, tetapi sesuatu yang lain - check-sum ini tidak akan bertepatan dengan data berikut.
  • Kami mendapatkan kesalahan "Checksum tidak cocok, data rusak".

Untungnya, bukan data yang rusak, tetapi hanya cache di RAM. Kami segera diberitahu tentang kesalahan tersebut, karena kami memeriksa jumlah data. Kesalahan diperbaiki pada 27 Desember 2015 dan pergi untuk merayakannya.

Seperti yang Anda lihat, kode yang salah setidaknya bisa diperbaiki. Tetapi bagaimana cara memperbaiki bug di perangkat keras?

Serangga dalam besi


Ini bahkan bukan bug, tetapi hukum fisik - efek yang tak terhindarkan. Menurut hukum fisika, zat besi tidak dapat dihindari adalah kereta.

Tulis non-atom ke RAID . Misalnya, kami membuat RAID1. Terdiri dari dua hard drive. Ini berarti bahwa satu server adalah sistem terdistribusi: data ditulis ke satu hard drive dan ke yang lain. Tetapi bagaimana jika data ditulis ke satu disk dan daya hilang saat merekam ke disk kedua? Data pada array RAID1 tidak akan konsisten. Kami tidak akan dapat memahami data mana yang benar, karena kami akan membaca satu byte atau yang lainnya.

Anda dapat mengatasinya dengan menempatkan log. Sebagai contoh, di ZFS masalah ini terpecahkan, tetapi lebih lanjut tentang itu nanti.

bit busuk pada HDD dan SSD. Bit pada hard drive dan SSD bisa menjadi buruk begitu saja. SSD modern, terutama yang memiliki sel multi-level, dirancang untuk memastikan bahwa sel akan terus memburuk. Kode koreksi kesalahan membantu, tetapi kadang-kadang sel memburuk begitu banyak sehingga bahkan ini tidak menyelamatkan. Kesalahan yang tidak terdeteksi diperoleh.

bit membalik RAM (tapi bagaimana dengan ECC?). Dalam RAM di server, bit juga rusak. Ini juga memiliki kode koreksi kesalahan. Ketika kesalahan terjadi, mereka biasanya terlihat dari pesan di log kernel Linux di dmesg. Ketika ada banyak kesalahan, kita akan melihat sesuatu seperti: "N juta kesalahan dengan memori telah diperbaiki." Tetapi bit individual tidak akan diperhatikan, dan pasti sesuatu akan buggy.

bit membalik pada level CPU dan jaringan . Ada kesalahan di tingkat CPU, dalam cache CPU dan, tentu saja, ketika mengirimkan data melalui jaringan.

Bagaimana kesalahan besi biasanya terwujud? Tiket " Znode cacat mencegah ClickHouse memulai " datang ke GitHub - data di simpul ZooKeeper rusak.

Di ZooKeeper, kami biasanya menulis beberapa metadata dalam teks biasa. Ada sesuatu yang salah dengannya - " replika " ditulis sangat aneh.



Jarang terjadi bahwa karena bug dalam kode, perubahan sedikit pun. Tentu saja, kita dapat menulis kode seperti itu: kita mengambil filter Bloom, mengubah bit di alamat tertentu, menghitung alamat secara tidak benar, mengubah bit yang salah, itu jatuh pada beberapa data. Itu dia, sekarang di ClickHouse itu bukan " replika" , tapi " repli b a " dan di dalamnya semua data salah. Tetapi biasanya, perubahan dalam satu bit adalah gejala masalah besi.

Mungkin Anda tahu contoh bitquatting. Artyom Dinaburg melakukan percobaan : ada domain di Internet yang memiliki banyak lalu lintas, meskipun pengguna tidak pergi ke domain ini sendiri. Misalnya, domain semacam itu FB-CDN.com adalah Facebook CDN.

Artyom mendaftarkan domain serupa (dan banyak lainnya), tetapi berubah sedikit. Misalnya, FA-CDN.com, bukan FB-CDN.com. Domain tidak dipublikasikan di mana pun, tetapi lalu lintas datang ke sana. Kadang-kadang host FB-CDN ditulis dalam header HTTP, dan permintaan pergi ke host lain karena kesalahan dalam RAM pada perangkat pengguna. RAM dengan koreksi kesalahan tidak selalu membantu. Kadang-kadang bahkan mengganggu dan menyebabkan kerentanan (baca tentang Rowhammer, ECCploit, RAMBleed).
Kesimpulan: selalu periksa-jumlah data sendiri.
Saat menulis ke sistem file, periksa jumlah tanpa gagal. Saat mentransmisikan melalui jaringan, periksa juga ringkasannya - jangan berharap bahwa ada jumlah pemeriksaan di sana.

Lebih banyak bug! ..


Metrik Cluster Produksi . Pengguna dalam menanggapi permintaan terkadang mendapatkan pengecualian: "Checksum tidak cocok: data rusak" - jumlah cek tidak benar, data rusak.



Pesan kesalahan menampilkan data terperinci: jumlah cek yang diharapkan, jumlah cek apa yang sebenarnya ada dalam data ini, ukuran blok tempat kami memeriksa jumlah cek dan konteks pengecualian.

Ketika kami menerima paket melalui jaringan dari beberapa server, pengecualian muncul - itu tampak akrab. Mungkin lagi melewati ingatan, kondisi balapan, atau yang lainnya.

Pengecualian ini muncul pada 2015. Bug telah diperbaiki, tidak lagi muncul. Pada Februari 2019, dia tiba-tiba muncul lagi. Pada saat ini saya berada di salah satu konferensi, kolega saya menangani masalah tersebut. Kesalahan direproduksi beberapa kali sehari di antara 1000 server dengan ClickHouse: tidak mungkin untuk mengumpulkan statistik di satu server, kemudian di yang lain. Pada saat yang sama, tidak ada rilis baru saat ini. Itu tidak berhasil dan menyelesaikan masalah, tetapi setelah beberapa hari kesalahan itu sendiri hilang.

Mereka lupa tentang kesalahan itu, dan pada 15 Mei 2019, itu berulang. Kami terus berurusan dengannya. Hal pertama yang saya lakukan adalah melihat semua log dan grafik yang tersedia. Dia mempelajarinya sepanjang hari, tidak mengerti apa-apa, tidak menemukan pola. Jika masalah tidak dapat direproduksi, satu-satunya pilihan adalah mengumpulkan semua kasus, cari poladan kecanduan. Mungkin kernel Linux tidak bekerja dengan benar dengan prosesor, menyimpan atau memuat register yang salah.

Hipotesis dan pola


7 dari 9 server dengan E5-2683 v4 gagal. Tetapi dari kesalahan cenderung, hanya sekitar setengah dari E5-2683 v4 adalah hipotesis kosong.

Kesalahan biasanya tidak berulang . Selain cluster mtauxyz, di mana memang ada data terkorupsi (data buruk pada disk). Ini adalah kasus lain, kami menolak hipotesis.

Kesalahan tidak tergantung pada kernel Linux - diperiksa pada server yang berbeda, tidak menemukan apa pun. Tidak ada yang menarik di kern.log, machine check exceptiontidak ada pesan . Dalam grafik jaringan, termasuk retransmitter, CPU, IO, Network, tidak ada yang menarik. Semua adapter jaringan pada server tempat kesalahan terjadi dan tidak muncul adalah sama.

Tidak ada pola . Apa yang harus dilakukan? Terus mencari pola. Usaha kedua.

Saya melihat server uptime:uptime tinggi, server bekerja dengan stabil , segfault dan sesuatu seperti itu tidak. Saya selalu bersukacita ketika saya melihat bahwa program crash dengan segfault - setidaknya itu crash. Lebih buruk lagi, ketika ada kesalahan, itu merusak sesuatu, tetapi tidak ada yang memperhatikannya.

Kesalahan dikelompokkan berdasarkan hari dan terjadi dalam beberapa hari. Dalam 2 hari, lebih banyak muncul, dalam beberapa kurang, sekali lagi lebih - tidak mungkin untuk secara akurat menentukan waktu terjadinya kesalahan.

Beberapa kesalahan cocok dengan paket dan jumlah cek yang kami harapkan. Sebagian besar kesalahan hanya memiliki dua opsi paket. Saya beruntung karena dalam pesan kesalahan kami menambahkan nilai dari jumlah cek, yang membantu menyusun statistik.

Tidak ada pola servertempat kami membaca data dari. Ukuran blok terkompresi yang kami periksa jumlahnya kurang dari satu kilobyte. Tampak pada ukuran paket di HEX. Ini tidak berguna bagi saya - representasi biner dari ukuran paket dan jumlah cek tidak terlihat.

Saya tidak memperbaiki kesalahan - saya lagi mencari pola. Upaya ketiga.

Untuk beberapa alasan, kesalahan hanya muncul pada salah satu cluster - pada replika ketiga di Vladimir DC (kami suka memanggil pusat data dengan nama kota). Pada bulan Februari 2019, kesalahan juga muncul di Vladimirs DC, tetapi pada versi ClickHouse yang berbeda. Ini adalah argumen lain yang menentang hipotesis bahwa kita menulis kode yang salah. Kami sudah menulis ulang tiga kali dari Februari hingga Mei - kesalahannya mungkin tidak ada dalam kode .

Semua kesalahan saat membaca paket melalui jaringan -while receiving packet from. Paket di mana kesalahan terjadi tergantung pada struktur permintaan. Untuk permintaan yang berbeda dalam struktur, kesalahan pada jumlah cek berbeda. Tetapi dalam permintaan di mana kesalahan pada jumlah cek yang sama, konstanta berbeda.

Semua permintaan dengan kesalahan, kecuali satu, adalah GLOBAL JOIN. Tetapi untuk perbandingan, ada satu permintaan sederhana yang tidak biasa, dan ukuran blok terkompresi untuk itu hanya 75 byte.

SELECT max(ReceiveTimestamp) FROM tracking_events_all 
WHERE APIKey = 1111 AND (OperatingSystem IN ('android', 'ios'))

Kami menolak hipotesis pengaruh GLOBAL JOIN.

Yang paling menarik adalah bahwa server yang terkena dikelompokkan ke dalam rentang dengan nama mereka :
mtxxxlog01-{39..44 57..58 64 68..71 73..74 76}-3.

Saya lelah dan putus asa, mulai mencari pola yang sepenuhnya delusi. Ada baiknya saya tidak bisa men-debug kode menggunakan numerologi. Tapi masih ada petunjuk.

  • Grup server bermasalah sama dengan pada bulan Februari.
  • Server bermasalah terletak di bagian tertentu dari pusat data. Di DC Vladimir ada garis yang disebut - bagian yang berbeda: VLA-02, VLA-03, VLA-04. Kesalahan jelas dikelompokkan: dalam beberapa antrian itu baik (VLA-02), dalam masalah lain (VLA-03, VLA-04).

Mengetik debugging


Hanya tinggal debug menggunakan metode "tombak". Ini berarti membentuk hipotesis "Apa yang terjadi jika Anda mencoba melakukannya?" dan mengumpulkan data. Misalnya, saya menemukan query_logkueri sederhana dengan kesalahan pada tabel yang ukuran paketnya size of compressed blocksangat kecil (= 107).



Saya menerima permintaan itu, menyalinnya dan menjalankannya secara manual menggunakan clickhouse-lokal.

strace -f -e trace=network -s 1000 -x \
clickhouse-local --query "
    SELECT uniqIf(DeviceIDHash, SessionType = 0)
    FROM remote('127.0.0.{2,3}', mobile.generic_events)
    WHERE StartDate = '2019-02-07' AND APIKey IN (616988,711663,507671,835591,262098,159700,635121,509222)
        AND EventType = 1 WITH TOTALS" --config config.xml

Dengan bantuan strace saya menerima snapshot (dump) blok melalui jaringan - paket yang sama persis yang diterima ketika permintaan ini dieksekusi, dan saya bisa mempelajarinya. Anda dapat menggunakan tcpdump untuk ini, tetapi tidak nyaman: sulit untuk mengisolasi permintaan spesifik dari lalu lintas produksi.

Dengan menggunakan strace, Anda dapat melacak server ClickHouse itu sendiri. Tetapi server ini berfungsi dalam produksi, jika saya melakukan ini saya akan mendapatkan berbagai informasi yang tidak dapat dipahami. Oleh karena itu, saya meluncurkan program terpisah yang mengeksekusi tepat satu permintaan. Sudah untuk program ini saya menjalankan strace dan mendapatkan apa yang ditransmisikan melalui jaringan.

Permintaan dieksekusi tanpa kesalahan - kesalahan tidak direproduksi . Jika direproduksi, masalah akan teratasi. Oleh karena itu, saya menyalin paket ke file teks dan secara manual mulai menguraikan protokol.



Jumlah cek sama dengan yang diharapkan. Ini persis paket di mana kadang-kadang, di waktu lain, dalam permintaan lain, kesalahan terjadi. Namun sejauh ini tidak ada kesalahan.

Saya menulis sebuah program sederhana yang mengambil paket dan memeriksa jumlah cek ketika mengganti satu bit dalam setiap byte. Program melakukan bit flip pada setiap posisi yang memungkinkan dan membaca jumlah cek.



Saya memulai program dan menemukan bahwa jika Anda mengubah nilai bit, Anda mendapatkan jumlah check-broken yang persis sama, yang mana ada keluhan

Masalah perangkat keras


Jika ada kesalahan dalam perangkat lunak (misalnya, mengemudi melalui memori), flip bit tunggal tidak mungkin. Karena itu, sebuah hipotesis baru muncul - masalahnya ada di kelenjar.

Seseorang dapat menutup tutup laptop dan berkata: "Masalahnya bukan di pihak kita, tetapi di perangkat keras, kita tidak melakukan ini." Tapi tidak, mari kita coba memahami di mana masalahnya: di RAM, di hard drive, di prosesor, di kartu jaringan atau di RAM kartu jaringan di peralatan jaringan.

Bagaimana cara melokalkan masalah perangkat keras?

  • Masalah muncul dan menghilang pada tanggal-tanggal tertentu.
  • Server yang terkena dikelompokkan dengan nama mereka: mtxxxlog01-{39..44 57..58 64 68..71 73..74 76}-3.
  • Grup server bermasalah sama dengan Februari.
  • Server bermasalah hanya dalam antrian tertentu dari pusat data.

Ada pertanyaan untuk insinyur jaringan - data berdetak pada switch jaringan. Ternyata insinyur jaringan bertukar switch untuk orang lain tepat pada tanggal tersebut. Setelah sebuah pertanyaan, mereka menggantinya dengan yang sebelumnya dan masalahnya hilang.

Masalahnya teratasi, tetapi masih ada pertanyaan (tidak lagi untuk insinyur).

Mengapa ECC (memori koreksi kesalahan) tidak membantu switch jaringan? Karena beberapa bit flip dapat saling mengimbangi - Anda mendapatkan kesalahan yang tidak terdeteksi.

Mengapa TCP tidak memeriksa jumlah membantu? Mereka lemah. Jika hanya satu bit telah berubah dalam data, maka jumlah pemeriksaan TCP akan selalu melihat perubahan. Jika dua bit telah berubah, maka perubahan itu mungkin tidak terdeteksi - mereka membatalkan satu sama lain.

Hanya satu bit yang berubah dalam paket kami, tetapi kesalahannya tidak terlihat. Itu karena 2 bit berubah di segmen TCP: mereka menghitung jumlah cek dari itu, itu bertepatan. Tetapi dalam satu segmen TCP lebih dari satu paket aplikasi kita berada. Dan untuk salah satu dari mereka, kami sudah mempertimbangkan jumlah cek kami. Hanya satu bit yang berubah dalam paket ini.

Mengapa jumlah pemeriksaan Ethernet tidak membantu - apakah mereka lebih kuat dari TCP? Jumlah Periksa Ethernetperiksa-rangkum data sehingga mereka tidak rusak selama transmisi melalui satu segmen (saya bisa salah dengan terminologi, saya bukan insinyur jaringan). Peralatan jaringan meneruskan paket-paket ini dan dapat meneruskan sejumlah data selama penerusan. Oleh karena itu, jumlah cek hanya dihitung ulang. Kami memeriksa - pada kabel paket tidak berubah. Tetapi jika mereka mengalahkan pada switch jaringan itu sendiri, itu akan menghitung ulang jumlah cek (akan berbeda), dan meneruskan paket lebih lanjut.
Tidak ada yang akan menyelamatkan Anda - cek jumlah Anda sendiri. Jangan berharap seseorang melakukan ini untuk Anda.
Untuk blok data, jumlah cek 128-bit dipertimbangkan (kerja keras ini untuk berjaga-jaga). Kami dengan benar memberi tahu pengguna tentang kesalahan tersebut. Data ditransmisikan melalui jaringan, itu rusak, tapi kami tidak merekamnya di mana saja - semua data kami dalam urutan, Anda tidak bisa khawatir.

Data yang disimpan di ClickHouse tetap konsisten. Gunakan jumlah cek di ClickHouse. Kami sangat menyukai jumlah cek sehingga kami segera mempertimbangkan tiga opsi:

  • Untuk blok data terkompresi saat menulis ke file, ke jaringan.
  • Pemeriksaan total adalah jumlah data terkompresi untuk verifikasi rekonsiliasi.
  • Pemeriksaan total adalah jumlah data yang tidak terkompresi untuk verifikasi rekonsiliasi.

Ada bug dalam algoritma kompresi data, ini adalah kasus yang diketahui. Oleh karena itu, ketika data direplikasi, kami juga mempertimbangkan jumlah cek total data terkompresi dan jumlah total data yang tidak terkompresi.
Jangan takut untuk menghitung jumlah cek, mereka tidak memperlambat.
Tentu saja, itu tergantung pada yang mana dan bagaimana cara menghitungnya. Ada nuansa, tetapi pastikan untuk mempertimbangkan jumlah cek. Misalnya, jika Anda menghitung dari data yang dikompresi, maka akan ada lebih sedikit data, mereka tidak akan melambat.

Pesan kesalahan yang ditingkatkan


Bagaimana menjelaskan kepada pengguna ketika ia menerima pesan kesalahan sedemikian rupa sehingga ini merupakan masalah perangkat keras?



Jika jumlah cek tidak cocok, sebelum mengirim pengecualian, saya mencoba mengubah setiap bit - untuk jaga-jaga. Jika jumlah cek konvergen ketika berubah dan satu bit diubah, maka masalahnya kemungkinan besar adalah perangkat keras.

Jika kita dapat mendeteksi kesalahan ini, dan jika itu berubah ketika satu bit diubah, mengapa tidak memperbaikinya? Kita dapat melakukan ini, tetapi jika kita memperbaiki kesalahan setiap saat, pengguna tidak akan tahu bahwa peralatan tersebut bermasalah.

Ketika kami mengetahui bahwa ada masalah di sakelar, orang-orang dari departemen lain mulai melaporkan: “Dan kami memiliki satu kesalahan penulisan pada Mongo! Dan ada sesuatu yang sampai kepada kita di PostgreSQL! ” Ini bagus, tetapi lebih baik melaporkan masalah sebelumnya.

Ketika kami merilis rilis diagnostik baru, pengguna pertama yang bekerja menulis satu minggu kemudian: "Inilah pesannya - apa masalahnya?" Sayangnya, dia tidak membacanya. Tetapi saya membaca dan menyarankan dengan probabilitas 99% bahwa jika kesalahan muncul pada satu server, maka masalahnya adalah pada perangkat keras. Saya meninggalkan persentase yang tersisa jika saya salah menulis kode - ini terjadi. Akibatnya, pengguna mengganti SSD, dan masalahnya hilang.

"Delirium" dalam data


Masalah yang menarik dan tak terduga ini membuat saya khawatir. Kami memiliki data Yandex.Metrica. JSON sederhana ditulis ke database di salah satu kolom - parameter pengguna dari kode JavaScript penghitung.

Saya membuat semacam permintaan dan server ClickHouse hancur dengan segfault. Dari jejak tumpukan, saya menyadari apa masalahnya - komitmen baru dari kontributor eksternal kami dari negara lain. Komit yang diperbaiki, segfault menghilang.

Saya menjalankan permintaan yang sama: SELECTdi ClickHouse, untuk mendapatkan JSON, tapi sekali lagi, omong kosong, semuanya bekerja lambat. Saya mendapatkan JSON, dan ini 10 MB. Saya menampilkannya dan terlihat lebih penuh perhatian: {"jserrs": cannot find property of object undefind...dan kemudian satu megabyte kode biner terjatuh.



Ada pemikiran bahwa ini sekali lagi merupakan bagian dari ingatan atau kondisi ras. Banyak data biner yang buruk, bisa berisi apa saja. Jika demikian, sekarang saya akan menemukan kata sandi dan kunci pribadi di sana. Tetapi saya tidak menemukan apa-apa, jadi saya langsung menolak hipotesis itu. Mungkin ini adalah bug di program saya di server ClickHouse? Mungkin dalam program yang menulis (ini juga ditulis dalam C ++) - tiba-tiba dia secara tidak sengaja memasukkan memori dump-nya ke ClickHouse? Di neraka ini, saya mulai memperhatikan surat-surat itu dan menyadari bahwa itu tidak sesederhana itu.

Jalur petunjuk


Sampah yang sama dicatat pada dua kelompok, terpisah satu sama lain. Data ini sampah, tetapi valid UTF-8. UTF-8 ini memiliki beberapa URL aneh, nama font, dan banyak huruf "I" berturut-turut.

Apa yang istimewa tentang si kecil Cyrillic "I"? Tidak, ini bukan Yandex. Faktanya adalah bahwa dalam pengkodean Windows 1251 itu adalah karakter ke-255. Dan di server Linux kami, tidak ada yang menggunakan pengkodean Windows 1251.

Ternyata ini adalah dump browser: kode JavaScript dari penghitung metrik mengumpulkan kesalahan JavaScript. Ternyata, jawabannya sederhana - semuanya berasal dari pengguna .

Dari sini juga, kesimpulan bisa ditarik.

Bug dari seluruh Internet


Yandex.Metrica mengumpulkan lalu lintas dari 1 miliar perangkat di Internet: browser di PC, ponsel, tablet. Sampah akan datang tak terhindarkan : ada bug di perangkat pengguna, di mana-mana RAM tidak dapat diandalkan dan perangkat keras yang terlalu panas.

Basis data menyimpan lebih dari 30 triliun baris (tampilan halaman). Jika Anda menganalisis data dari tabel ini, Anda dapat menemukan apa pun di sana.

Karena itu, cukup menyaring sampah ini sebelum menulis ke basis data. Tidak perlu menulis sampah ke database - dia tidak menyukainya.

HighLoad++ ( 133 ), - , , ++ PHP Russia 2020 Online.

Badoo, PHP Russia 2020 Online . PHP Russia 2020 Online 13 , .

, .

All Articles