Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Ini adalah kelanjutan dari cerita panjang tentang jalan sulit kami untuk menciptakan sistem beban tinggi yang kuat yang menjamin pengoperasian Exchange. Bagian pertama ada di sini: habr.com/en/post/444300

Kesalahan misterius

Setelah beberapa kali pengujian, sistem perdagangan dan kliring yang diperbarui dioperasikan, dan kami menemukan bug yang dapat kami gunakan untuk menulis cerita detektif-mistis.

Sesaat setelah peluncuran di server utama, salah satu transaksi diproses dengan kesalahan. Namun, semuanya baik-baik saja di server cadangan. Ternyata operasi matematika sederhana menghitung eksponen di server utama memberikan hasil negatif dari argumen sebenarnya! Kami melanjutkan penelitian kami, dan dalam register SSE2 kami menemukan perbedaan dalam satu bit, yang bertanggung jawab untuk pembulatan saat bekerja dengan bilangan floating point.

Kami menulis utilitas pengujian sederhana untuk menghitung eksponen dengan set bit pembulatan. Ternyata pada versi RedHat Linux yang kami gunakan, terdapat bug dalam pengerjaan fungsi matematika saat bit naas dimasukkan. Kami melaporkan hal ini ke RedHat, setelah beberapa saat kami menerima patch dari mereka dan meluncurkannya. Kesalahan tidak lagi terjadi, namun tidak jelas dari mana bit ini berasal? Fungsi tersebut bertanggung jawab untuk itu fesetround dari bahasa C. Kami menganalisis kode kami dengan cermat untuk mencari kesalahan yang diduga: kami memeriksa semua kemungkinan situasi; melihat semua fungsi yang menggunakan pembulatan; mencoba mereproduksi sesi yang gagal; menggunakan kompiler berbeda dengan opsi berbeda; Analisis statis dan dinamis digunakan.

Penyebab kesalahan tidak dapat ditemukan.

Kemudian mereka mulai memeriksa perangkat keras: mereka melakukan pengujian beban pada prosesor; memeriksa RAM; Kami bahkan menjalankan pengujian untuk skenario kesalahan multi-bit yang sangat tidak mungkin terjadi dalam satu sel. Tidak berhasil.

Pada akhirnya, kami menetapkan teori dari dunia fisika energi tinggi: partikel berenergi tinggi terbang ke pusat data kami, menembus dinding casing, menabrak prosesor, dan menyebabkan kait pelatuk menempel di bagian tersebut. Teori absurd ini disebut β€œneutrino”. Jika Anda jauh dari fisika partikel: neutrino hampir tidak berinteraksi dengan dunia luar, dan tentunya tidak dapat mempengaruhi pengoperasian prosesor.

Karena penyebab kegagalan tidak dapat ditemukan, server yang β€œmelanggar” telah dihapus dari operasi untuk berjaga-jaga.

Setelah beberapa waktu, kami mulai meningkatkan sistem pencadangan panas: kami memperkenalkan apa yang disebut "cadangan hangat" (hangat) - replika asinkron. Mereka menerima aliran transaksi yang dapat ditempatkan di pusat data yang berbeda, namun Warms tidak berinteraksi secara aktif dengan server lain.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Mengapa hal ini dilakukan? Jika server cadangan gagal, maka server hangat yang terikat ke server utama menjadi cadangan baru. Artinya, setelah terjadi kegagalan, sistem tidak akan tetap berada di satu server utama hingga sesi perdagangan berakhir.

Dan ketika versi baru sistem diuji dan dioperasikan, kesalahan bit pembulatan terjadi lagi. Terlebih lagi, dengan bertambahnya jumlah server hangat, kesalahan mulai lebih sering muncul. Pada saat yang sama, vendor tidak dapat menunjukkan apa pun karena tidak ada bukti nyata.

Selama analisis situasi selanjutnya, muncul teori bahwa masalahnya mungkin terkait dengan OS. Kami menulis sebuah program sederhana yang memanggil fungsi dalam loop tanpa akhir fesetround, mengingat keadaan saat ini dan memeriksanya melalui mode tidur, dan ini dilakukan di banyak thread yang bersaing. Setelah memilih parameter untuk tidur dan jumlah utas, kami mulai mereproduksi kegagalan bit secara konsisten setelah sekitar 5 menit menjalankan utilitas. Namun, dukungan Red Hat tidak dapat mereproduksinya. Pengujian server kami yang lain menunjukkan bahwa hanya server dengan prosesor tertentu yang rentan terhadap kesalahan. Pada saat yang sama, beralih ke kernel baru memecahkan masalah tersebut. Pada akhirnya, kami hanya mengganti OS, dan penyebab sebenarnya dari bug tersebut masih belum jelas.

Dan tiba-tiba tahun lalu sebuah artikel diterbitkan di HabrΓ© β€œBagaimana saya menemukan bug pada prosesor Intel Skylake" Situasi yang dijelaskan di dalamnya sangat mirip dengan situasi kita, tetapi penulis melakukan penyelidikan lebih jauh dan mengemukakan teori bahwa kesalahannya ada pada mikrokode. Dan ketika kernel Linux diperbarui, produsen juga memperbarui mikrokodenya.

Pengembangan sistem lebih lanjut

Meskipun kami telah menghilangkan kesalahan tersebut, cerita ini memaksa kami untuk mempertimbangkan kembali arsitektur sistem. Bagaimanapun, kami tidak terlindungi dari terulangnya bug serupa.

Prinsip-prinsip berikut menjadi dasar untuk perbaikan sistem reservasi selanjutnya:

  • Anda tidak bisa mempercayai siapa pun. Server mungkin tidak berfungsi dengan baik.
  • Reservasi mayoritas.
  • Memastikan konsensus. Sebagai tambahan logis untuk reservasi mayoritas.
  • Kegagalan ganda mungkin terjadi.
  • Daya hidup. Skema hot standby yang baru seharusnya tidak lebih buruk dari skema sebelumnya. Perdagangan harus dilanjutkan tanpa gangguan hingga server terakhir.
  • Sedikit peningkatan latensi. Setiap downtime menyebabkan kerugian finansial yang besar.
  • Interaksi jaringan minimal untuk menjaga latensi serendah mungkin.
  • Memilih server master baru dalam hitungan detik.

Tidak ada solusi yang tersedia di pasar yang cocok untuk kami, dan protokol Raft masih dalam tahap awal, jadi kami menciptakan solusi kami sendiri.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Jaringan

Selain sistem reservasi, kami mulai memodernisasi interaksi jaringan. Subsistem I/O terdiri dari banyak proses, yang memiliki dampak terburuk pada jitter dan latensi. Dengan ratusan proses yang menangani koneksi TCP, kami terpaksa terus-menerus beralih di antara proses tersebut, dan dalam skala mikrodetik, ini adalah operasi yang memakan waktu. Namun bagian terburuknya adalah ketika suatu proses menerima paket untuk diproses, ia mengirimkannya ke satu antrian SystemV dan kemudian menunggu kejadian dari antrian SystemV lainnya. Namun, ketika terdapat sejumlah besar node, kedatangan paket TCP baru dalam satu proses dan penerimaan data dalam antrian di proses lain mewakili dua peristiwa yang bersaing untuk OS. Dalam hal ini, jika tidak ada prosesor fisik yang tersedia untuk kedua tugas tersebut, yang satu akan diproses, dan yang kedua akan ditempatkan dalam antrian tunggu. Konsekuensinya tidak dapat diprediksi.

Dalam situasi seperti ini, kontrol prioritas proses dinamis dapat digunakan, namun hal ini memerlukan penggunaan panggilan sistem yang intensif sumber daya. Hasilnya, kami beralih ke satu thread menggunakan epoll klasik, ini sangat meningkatkan kecepatan dan mengurangi waktu pemrosesan transaksi. Kami juga menyingkirkan proses komunikasi jaringan terpisah dan komunikasi melalui SystemV, secara signifikan mengurangi jumlah panggilan sistem dan mulai mengontrol prioritas operasi. Pada subsistem I/O saja, penghematan sekitar 8-17 mikrodetik dapat dilakukan, bergantung pada skenario. Skema thread tunggal ini telah digunakan tanpa perubahan sejak saat itu; satu thread epoll dengan margin sudah cukup untuk melayani semua koneksi.

Pemrosesan Transaksi

Meningkatnya beban pada sistem kami memerlukan peningkatan hampir semua komponennya. Namun sayangnya, stagnasi pertumbuhan kecepatan clock prosesor dalam beberapa tahun terakhir tidak lagi memungkinkan untuk menskalakan proses secara langsung. Oleh karena itu, kami memutuskan untuk membagi proses Mesin menjadi tiga tingkat, dengan tingkat tersibuk adalah sistem pemeriksaan risiko, yang mengevaluasi ketersediaan dana di akun dan membuat transaksi itu sendiri. Namun uang bisa dalam mata uang yang berbeda, dan penting untuk mengetahui atas dasar apa pemrosesan permintaan harus dibagi.

Solusi logisnya adalah membaginya berdasarkan mata uang: satu server memperdagangkan dolar, server lainnya dalam pound, dan server ketiga dalam euro. Namun jika dengan skema seperti itu, dua transaksi dikirim untuk pembelian mata uang yang berbeda, maka akan muncul masalah desinkronisasi dompet. Namun sinkronisasi itu sulit dan mahal. Oleh karena itu, adalah benar untuk melakukan sharding secara terpisah berdasarkan dompet dan secara terpisah berdasarkan instrumen. Omong-omong, sebagian besar bursa Barat tidak memiliki tugas untuk memeriksa risiko seserius yang kami lakukan, jadi paling sering hal ini dilakukan secara offline. Kami perlu menerapkan verifikasi online.

Mari kita jelaskan dengan sebuah contoh. Seorang pedagang ingin membeli $30, dan permintaannya masuk ke validasi transaksi: kami memeriksa apakah pedagang ini diperbolehkan menggunakan mode perdagangan ini dan apakah dia memiliki hak yang diperlukan. Jika semuanya beres, permintaan masuk ke sistem verifikasi risiko, mis. untuk memeriksa kecukupan dana untuk menyelesaikan transaksi. Ada catatan bahwa jumlah yang dibutuhkan saat ini diblokir. Permintaan tersebut kemudian diteruskan ke sistem perdagangan, yang menyetujui atau menolak transaksi tersebut. Katakanlah transaksi disetujui - kemudian sistem verifikasi risiko menandai bahwa uang tersebut tidak diblokir, dan rubel berubah menjadi dolar.

Secara umum, sistem pemeriksaan risiko berisi algoritma yang kompleks dan melakukan sejumlah besar perhitungan yang sangat intensif sumber daya, dan tidak hanya memeriksa β€œsaldo akun”, seperti yang terlihat pada pandangan pertama.

Saat kami mulai membagi proses Mesin ke dalam beberapa level, kami menemui masalah: kode yang tersedia pada saat itu secara aktif menggunakan kumpulan data yang sama pada tahap validasi dan verifikasi, sehingga memerlukan penulisan ulang seluruh basis kode. Hasilnya, kami meminjam teknik pemrosesan instruksi dari prosesor modern: masing-masing instruksi dibagi menjadi beberapa tahap kecil dan beberapa tindakan dilakukan secara paralel dalam satu siklus.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Setelah sedikit adaptasi kode, kami membuat saluran untuk pemrosesan transaksi paralel, di mana transaksi dibagi menjadi 4 tahap saluran: interaksi jaringan, validasi, eksekusi, dan publikasi hasilnya.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Mari kita lihat sebuah contoh. Kami memiliki dua sistem pemrosesan, serial dan paralel. Transaksi pertama tiba dan dikirim untuk validasi di kedua sistem. Transaksi kedua segera tiba: dalam sistem paralel, transaksi tersebut langsung digunakan, dan dalam sistem sekuensial, transaksi dimasukkan ke dalam antrian menunggu transaksi pertama melalui tahap pemrosesan saat ini. Artinya, keuntungan utama dari pemrosesan pipeline adalah kami memproses antrian transaksi lebih cepat.

Inilah cara kami menciptakan sistem ASTS+.

Benar, tidak semuanya berjalan lancar dengan konveyor. Katakanlah kita memiliki transaksi yang mempengaruhi array data dalam transaksi tetangga; ini adalah situasi yang umum untuk pertukaran. Transaksi seperti itu tidak dapat dijalankan dalam pipeline karena dapat mempengaruhi orang lain. Situasi ini disebut bahaya data, dan transaksi semacam itu hanya diproses secara terpisah: ketika transaksi β€œcepat” dalam antrian habis, jalur pipa berhenti, sistem memproses transaksi β€œlambat”, dan kemudian memulai jalur pipa lagi. Untungnya, proporsi transaksi tersebut dalam keseluruhan aliran sangat kecil, sehingga jalur pipa sangat jarang berhenti sehingga tidak mempengaruhi kinerja secara keseluruhan.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Kemudian kami mulai memecahkan masalah sinkronisasi tiga rangkaian eksekusi. Hasilnya adalah sistem berdasarkan buffer cincin dengan sel berukuran tetap. Dalam sistem ini, semuanya bergantung pada kecepatan pemrosesan; data tidak disalin.

  • Semua paket jaringan yang masuk memasuki tahap alokasi.
  • Kami menempatkannya dalam array dan menandainya sebagai tersedia untuk tahap #1.
  • Transaksi kedua sudah sampai, sudah tersedia lagi untuk tahap no 1.
  • Thread pemrosesan pertama melihat transaksi yang tersedia, memprosesnya, dan memindahkannya ke tahap berikutnya dari thread pemrosesan kedua.
  • Kemudian memproses transaksi pertama dan menandai sel yang sesuai deleted β€” sekarang tersedia untuk penggunaan baru.

Seluruh antrian diproses dengan cara ini.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Pemrosesan setiap tahapan membutuhkan waktu satuan atau puluhan mikrodetik. Dan jika kita menggunakan skema sinkronisasi OS standar, maka kita akan kehilangan lebih banyak waktu untuk sinkronisasi itu sendiri. Itu sebabnya kami mulai menggunakan spinlock. Namun, ini adalah bentuk yang sangat buruk dalam sistem real-time, dan RedHat sangat tidak menyarankan melakukan hal ini, jadi kami menerapkan spinlock selama 100 ms, dan kemudian beralih ke mode semaphore untuk menghilangkan kemungkinan kebuntuan.

Hasilnya, kami mencapai kinerja sekitar 8 juta transaksi per detik. Dan secara harfiah dua bulan kemudian Artikel tentang LMAX Disruptor kami melihat deskripsi rangkaian dengan fungsi yang sama.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Sekarang mungkin ada beberapa rangkaian eksekusi dalam satu tahap. Semua transaksi diproses satu per satu, sesuai urutan penerimaannya. Hasilnya, performa puncak meningkat dari 18 ribu menjadi 50 ribu transaksi per detik.

Sistem manajemen risiko pertukaran

Tidak ada batasan untuk kesempurnaan, dan kami segera memulai modernisasi lagi: dalam kerangka ASTS+, kami mulai memindahkan sistem manajemen risiko dan operasi penyelesaian ke dalam komponen yang otonom. Kami mengembangkan arsitektur modern yang fleksibel dan model risiko hierarki baru, dan mencoba menggunakan kelas tersebut sedapat mungkin fixed_point daripada double.

Namun masalah segera muncul: bagaimana cara menyinkronkan semua logika bisnis yang telah bekerja selama bertahun-tahun dan mentransfernya ke sistem baru? Akibatnya, versi pertama dari prototipe sistem baru harus ditinggalkan. Versi kedua, yang saat ini sedang dalam tahap produksi, didasarkan pada kode yang sama, yang berfungsi di bagian perdagangan dan risiko. Selama pengembangan, hal tersulit untuk dilakukan adalah menggabungkan dua versi. Rekan kami Evgeniy Mazurenok melakukan operasi ini setiap minggu dan setiap kali dia mengumpat dalam waktu yang sangat lama.

Saat memilih sistem baru, kami harus segera menyelesaikan masalah interaksi. Saat memilih bus data, penting untuk memastikan jitter yang stabil dan latensi minimal. Jaringan InfiniBand RDMA paling cocok untuk ini: waktu pemrosesan rata-rata 4 kali lebih singkat dibandingkan jaringan Ethernet 10 G. Namun yang benar-benar membuat kami terpesona adalah perbedaan persentilnya - 99 dan 99,9.

Tentu saja InfiniBand mempunyai tantangan tersendiri. Pertama, API yang berbeda - ibverbs, bukan soket. Kedua, hampir tidak ada solusi perpesanan sumber terbuka yang tersedia secara luas. Kami mencoba membuat prototipe kami sendiri, tetapi ternyata sangat sulit, jadi kami memilih solusi komersial - Confinity Low Latency Messaging (sebelumnya IBM MQ LLM).

Kemudian muncul tugas untuk membagi sistem risiko dengan benar. Jika Anda hanya menghapus Mesin Risiko dan tidak membuat node perantara, maka transaksi dari dua sumber dapat tercampur.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Solusi yang disebut Latensi Ultra Rendah memiliki mode pemesanan ulang: transaksi dari dua sumber dapat diatur dalam urutan yang diperlukan setelah diterima; ini diimplementasikan menggunakan saluran terpisah untuk bertukar informasi tentang pesanan. Namun kami belum menggunakan mode ini: mode ini memperumit seluruh proses, dan dalam sejumlah solusi, mode ini tidak didukung sama sekali. Selain itu, setiap transaksi harus diberi stempel waktu yang sesuai, dan dalam skema kami mekanisme ini sangat sulit untuk diterapkan dengan benar. Oleh karena itu, kami menggunakan skema klasik dengan perantara pesan, yaitu dengan operator yang mendistribusikan pesan antar Mesin Risiko.

Masalah kedua terkait dengan akses klien: jika ada beberapa Gerbang Risiko, klien perlu terhubung ke masing-masing Gerbang Risiko, dan ini memerlukan perubahan pada lapisan klien. Kami ingin menghindari hal ini pada tahap ini, sehingga desain Risk Gateway saat ini memproses seluruh aliran data. Ini sangat membatasi throughput maksimum, namun sangat menyederhanakan integrasi sistem.

duplikasi

Sistem kita tidak boleh memiliki satu titik kegagalan pun, artinya semua komponen harus terduplikasi, termasuk perantara pesan. Kami memecahkan masalah ini menggunakan sistem CLLM: sistem ini berisi cluster RCMS di mana dua operator dapat bekerja dalam mode master-slave, dan jika salah satu gagal, sistem secara otomatis beralih ke yang lain.

Bekerja dengan pusat data cadangan

InfiniBand dioptimalkan untuk pengoperasian sebagai jaringan lokal, yaitu untuk menghubungkan peralatan yang dipasang di rak, dan jaringan InfiniBand tidak dapat diletakkan di antara dua pusat data yang tersebar secara geografis. Oleh karena itu, kami menerapkan jembatan/pengirim, yang menghubungkan ke penyimpanan pesan melalui jaringan Ethernet biasa dan meneruskan semua transaksi ke jaringan IB kedua. Saat kita perlu bermigrasi dari pusat data, kita dapat memilih pusat data mana yang akan digunakan sekarang.

Hasil

Semua hal di atas tidak dilakukan sekaligus; dibutuhkan beberapa iterasi untuk mengembangkan arsitektur baru. Kami membuat prototipenya dalam waktu satu bulan, namun butuh waktu lebih dari dua tahun untuk membuatnya berfungsi. Kami mencoba mencapai kompromi terbaik antara peningkatan waktu pemrosesan transaksi dan peningkatan keandalan sistem.

Karena sistem telah banyak diperbarui, kami menerapkan pemulihan data dari dua sumber independen. Jika penyimpanan pesan tidak berfungsi dengan benar karena alasan tertentu, Anda dapat mengambil log transaksi dari sumber kedua - dari Mesin Risiko. Prinsip ini diamati di seluruh sistem.

Antara lain, kami dapat mempertahankan API klien sehingga baik broker maupun orang lain tidak memerlukan pengerjaan ulang yang signifikan untuk arsitektur baru. Kami harus mengubah beberapa antarmuka, tetapi tidak perlu melakukan perubahan signifikan pada model operasi.

Kami menyebut versi platform kami saat ini Rebus - sebagai singkatan dari dua inovasi paling mencolok dalam arsitektur, Risk Engine dan BUS.

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Awalnya, kami hanya ingin mengalokasikan bagian kliring saja, namun hasilnya adalah sistem terdistribusi yang sangat besar. Klien sekarang dapat berinteraksi dengan Trade Gateway, Clearing Gateway, atau keduanya.

Apa yang akhirnya kami capai:

Evolusi arsitektur sistem perdagangan dan kliring Bursa Moskow. Bagian 2

Mengurangi tingkat latensi. Dengan volume transaksi yang kecil, sistem bekerja sama seperti versi sebelumnya, namun pada saat yang sama dapat menahan beban yang jauh lebih tinggi.

Performa puncak meningkat dari 50 ribu menjadi 180 ribu transaksi per detik. Peningkatan lebih lanjut terhambat oleh satu-satunya aliran pencocokan pesanan.

Ada dua cara untuk perbaikan lebih lanjut: memparalelkan pencocokan dan mengubah cara kerjanya dengan Gateway. Sekarang semua Gateway beroperasi sesuai dengan skema replikasi, yang, di bawah beban seperti itu, berhenti berfungsi secara normal.

Terakhir, saya dapat memberikan beberapa saran kepada mereka yang sedang menyelesaikan sistem perusahaan:

  • Bersiaplah untuk kemungkinan terburuk setiap saat. Masalah selalu muncul secara tidak terduga.
  • Biasanya tidak mungkin mengubah arsitektur dengan cepat. Terutama jika Anda perlu mencapai keandalan maksimum di berbagai indikator. Semakin banyak node, semakin banyak sumber daya yang dibutuhkan untuk dukungan.
  • Semua solusi khusus dan eksklusif akan memerlukan sumber daya tambahan untuk penelitian, dukungan, dan pemeliharaan.
  • Jangan menunda penyelesaian masalah keandalan dan pemulihan sistem setelah kegagalan; pertimbangkan hal tersebut pada tahap desain awal.

Sumber: www.habr.com

Tambah komentar