WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Saya cadangkan anda membaca transkrip laporan awal 2020 oleh Georgy Rylov "WAL-G: peluang baharu dan pengembangan komuniti"

Penyelenggara sumber terbuka menghadapi banyak cabaran semasa mereka berkembang. Bagaimana untuk menulis lebih banyak ciri yang diperlukan, membetulkan lebih banyak isu dan menguruskan untuk melihat lebih banyak permintaan tarik? Menggunakan WAL-G (alat sandaran untuk PostgreSQL) sebagai contoh, saya akan memberitahu anda cara kami menyelesaikan masalah ini dengan melancarkan kursus tentang pembangunan Sumber Terbuka di universiti, apa yang kami capai dan ke mana kami akan bergerak seterusnya.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Hello sekali lagi semua! Saya seorang pemaju Yandex dari Yekaterinburg. Dan hari ini saya akan bercakap tentang WAL-G.

Tajuk laporan tidak mengatakan bahawa ia adalah sesuatu tentang sandaran. Ada sesiapa tahu apa itu WAL-G? Atau semua orang tahu? Angkat tangan kalau tak tahu. Sial, anda datang ke laporan itu dan tidak tahu tentang apa itu.

Biar saya beritahu anda apa yang akan berlaku hari ini. Kebetulan pasukan kami telah membuat sandaran untuk sekian lama. Dan ini adalah satu lagi laporan dalam siri di mana kita bercakap tentang cara kita menyimpan data dengan selamat, selamat, mudah dan cekap.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Dalam siri sebelumnya terdapat banyak laporan oleh Andrei Borodin dan Vladimir Leskov. Terdapat ramai daripada kami. Dan kami telah bercakap tentang WAL-G selama bertahun-tahun.

clck.ru/F8ioz β€” https://www.highload.ru/moscow/2018/abstracts/3964

clck.ru/Ln8Qw β€” https://www.highload.ru/moscow/2019/abstracts/5981

Laporan ini akan berbeza sedikit daripada yang lain kerana ia lebih kepada bahagian teknikal, tetapi di sini saya akan bercakap tentang bagaimana kami menghadapi masalah yang berkaitan dengan pertumbuhan komuniti. Dan bagaimana kami menghasilkan idea kecil yang membantu kami mengatasi perkara ini.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Beberapa tahun yang lalu, WAL-G adalah projek yang agak kecil yang kami dapat daripada Citus Data. Dan kami hanya mengambilnya. Dan ia dibangunkan oleh satu orang.

Dan hanya WAL-G yang tidak mempunyai:

  • Sandaran daripada replika.
  • Tiada sandaran tambahan.
  • Tiada sandaran WAL-Delta.
  • Dan masih banyak yang hilang.

Dalam beberapa tahun ini, WAL-G telah berkembang dengan pesat.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Dan menjelang 2020, semua perkara di atas telah pun muncul. Dan kepada ini telah ditambahkan apa yang kita ada sekarang:

  • Lebih daripada 1 bintang di GitHub.
  • 150 garpu.
  • Kira-kira 15 PR terbuka.
  • Dan ramai lagi penyumbang.
  • Dan isu terbuka sepanjang masa. Dan ini walaupun kami benar-benar pergi ke sana setiap hari dan melakukan sesuatu mengenainya.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Dan kami membuat kesimpulan bahawa projek ini memerlukan lebih perhatian kami, walaupun kami sendiri tidak perlu melaksanakan apa-apa untuk perkhidmatan Pangkalan Data Terurus kami di Yandex.

Dan di suatu tempat pada musim luruh tahun 2018, satu idea datang ke fikiran kami. Biasanya pasukan mempunyai beberapa cara untuk membangunkan beberapa ciri atau membetulkan pepijat jika anda tidak mempunyai tangan yang mencukupi. Sebagai contoh, anda boleh mengupah pemaju lain dan membayar wang kepadanya. Atau anda boleh mengambil pelatih untuk seketika dan juga membayar dia sedikit gaji. Tetapi masih terdapat sekumpulan besar orang, sesetengah daripada mereka sudah benar-benar tahu cara menulis kod. Cuma anda tidak selalu tahu kualiti kod itu.

Kami memikirkannya dan memutuskan untuk cuba menarik pelajar. Tetapi pelajar tidak akan mengambil bahagian dalam segala-galanya dengan kami. Mereka hanya akan melakukan sebahagian daripada kerja itu. Dan mereka akan, sebagai contoh, menulis ujian, membetulkan pepijat, melaksanakan ciri yang tidak menjejaskan fungsi utama. Fungsi utama ialah membuat sandaran dan memulihkan sandaran. Jika kami tersilap membuat sandaran, kami akan mengalami kehilangan data. Dan tiada siapa yang mahu ini, sudah tentu. Semua orang mahu semuanya selamat. Oleh itu, sudah tentu, kami tidak mahu membiarkan kod yang kami percayai kurang daripada kod kami sendiri. Iaitu, sebarang kod yang tidak kritikal ialah apa yang kami ingin terima daripada pekerja tambahan kami.

Dalam keadaan apa PR pelajar diterima?

  • Mereka dikehendaki menutup kod mereka dengan ujian. Semuanya harus berlaku di CI.
  • Dan kami juga melalui 2 ulasan. Satu oleh Andrey Borodin dan satu oleh saya.
  • Selain itu, untuk memastikan bahawa ini tidak akan memecahkan apa-apa dalam perkhidmatan kami, saya memuat naik perhimpunan secara berasingan dengan komit ini. Dan kami menyemak ujian hujung ke hujung bahawa tiada apa yang gagal.

Kursus khas mengenai Sumber Terbuka

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Sedikit tentang mengapa ini diperlukan dan mengapa ini, nampaknya saya, adalah idea yang bagus.

Bagi kami, keuntungan adalah jelas:

  • Kami mendapat tangan tambahan.
  • Dan kami sedang mencari calon untuk pasukan dalam kalangan pelajar pintar yang menulis kod pintar.

Apakah faedah untuk pelajar?

Mereka mungkin kurang jelas, kerana pelajar, sekurang-kurangnya, tidak menerima wang untuk kod yang mereka tulis, tetapi hanya menerima gred untuk rekod pelajar mereka.

Saya bertanya kepada mereka tentang perkara ini. Dan dalam kata-kata mereka:

  • Pengalaman penyumbang dalam Sumber Terbuka.
  • Dapatkan baris dalam CV anda.
  • Buktikan diri anda dan lulus temu duga dalam Yandex.
  • Jadi ahli GSoC.
  • +1 kursus khas untuk mereka yang ingin menulis kod.

Saya tidak akan bercakap tentang bagaimana kursus itu disusun. Saya hanya akan mengatakan bahawa WAL-G adalah projek utama. Kami juga memasukkan projek seperti Odyssey, PostgreSQL dan ClickHouse dalam kursus ini.

Dan mereka memberi masalah bukan sahaja dalam kursus ini, tetapi juga memberikan diploma dan kerja kursus.

Bagaimana dengan faedah untuk pengguna?

Sekarang mari kita beralih ke bahagian yang paling menarik minat anda. Apakah kebaikan ini kepada anda? Maksudnya ialah pelajar membetulkan banyak pepijat. Dan kami membuat ciri permintaan yang anda minta kami lakukan.

Dan izinkan saya memberitahu anda tentang perkara yang telah lama anda inginkan dan telah direalisasikan.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Sokongan ruang meja. Ruang meja dalam WAL-G telah dijangka mungkin sejak keluaran WAL-G, kerana WAL-G ialah pengganti kepada alat sandaran WAL-E yang lain, di mana sandaran pangkalan data dengan ruang meja disokong.

Izinkan saya mengingatkan anda secara ringkas apa itu dan mengapa ia semua diperlukan. Biasanya, semua data Postgres anda menduduki satu direktori pada sistem fail, dipanggil pangkalan. Dan direktori ini sudah mengandungi semua fail dan subdirektori yang diperlukan oleh Postgres.

Tablespaces ialah direktori yang mengandungi data Postgres, tetapi ia tidak terletak di luar direktori asas. Slaid menunjukkan bahawa tablespacs terletak di luar direktori asas.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Apakah rupa ini untuk Postgres sendiri? Terdapat subdirektori berasingan pg_tblspc dalam direktori asas. Dan ia mengandungi symlink ke direktori yang sebenarnya mengandungi data Postgres di luar direktori asas.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Apabila anda menggunakan semua ini, maka bagi anda arahan ini mungkin kelihatan seperti ini. Iaitu, anda mencipta jadual dalam beberapa ruang jadual tertentu dan melihat di mana ia sekarang. Ini adalah dua baris terakhir, dua arahan terakhir dipanggil. Dan di sana adalah jelas bahawa terdapat beberapa cara. Tetapi pada hakikatnya, ini bukan cara sebenar. Ini ialah laluan awalan dari direktori asas ke ruang meja. Dan dari situ ia dipadankan dengan symlink yang membawa kepada data sebenar anda.

Kami tidak menggunakan semua ini dalam pasukan kami, tetapi ia digunakan oleh ramai pengguna WAL-E lain yang menulis kepada kami bahawa mereka mahu berpindah ke WAL-G, tetapi ini telah menghalang mereka. Ini kini disokong.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Satu lagi ciri yang dibawa oleh kursus khas kami ialah mengejar. Orang yang mungkin telah bekerja lebih banyak dengan Oracle daripada dengan Postgres tahu tentang catchup.

Secara ringkas tentang apa itu. Topologi kelompok dalam perkhidmatan kami biasanya kelihatan seperti ini. Kami mempunyai tuan. Terdapat replika yang menstrim log tulis ke hadapan daripadanya. Dan replika memberitahu master LSN yang sedang digunakan. Dan di suatu tempat selari dengan ini, log boleh diarkibkan. Dan selain mengarkibkan log, sandaran juga dihantar ke awan. Dan sandaran delta dihantar.

Apa yang boleh menjadi masalah? Apabila anda mempunyai pangkalan data yang agak besar, mungkin ternyata replika anda mula ketinggalan jauh di belakang tuan. Dan dia ketinggalan jauh sehingga dia tidak dapat mengejarnya. Masalah ini biasanya perlu diselesaikan entah bagaimana.

Dan cara paling mudah ialah mengalih keluar replika dan memuat naiknya semula, kerana ia tidak akan dapat dikejar, dan masalah itu perlu ditangani. Tetapi ini adalah masa yang agak lama, kerana memulihkan keseluruhan sandaran pangkalan data 10 TB adalah masa yang sangat lama. Dan kami mahu melakukan semua ini secepat mungkin jika masalah sedemikian timbul. Dan itulah gunanya catchup.

Catchup membolehkan anda menggunakan sandaran delta, yang disimpan dalam awan dengan cara ini. Anda menyatakan LSN yang mana replika ketinggalan sedang dihidupkan dan tentukannya dalam perintah tangkapan untuk mencipta sandaran delta antara LSN itu dan LSN di mana kluster anda berada pada masa ini. Dan selepas itu anda memulihkan sandaran ini kepada replika yang ketinggalan.

Pangkalan lain

Pelajar juga membawa kami banyak ciri sekaligus. Memandangkan di Yandex kami memasak bukan sahaja Postgres, kami juga mempunyai MySQL, MongoDB, Redis, ClickHouse, pada satu ketika kami perlu dapat membuat sandaran dengan pemulihan titik-dalam-masa untuk MySQL, dan supaya ada peluang untuk memuat naik mereka ke awan.

Dan kami mahu melakukannya dengan cara yang sama seperti yang dilakukan oleh WAL-G. Dan kami memutuskan untuk mencuba dan melihat bagaimana ia akan kelihatan.

Dan pada mulanya, tanpa berkongsi logik ini dalam apa cara sekalipun, mereka menulis kod dalam garpu. Mereka melihat bahawa kami mempunyai beberapa jenis model yang berfungsi dan ia boleh terbang. Kemudian kami fikir komuniti utama kami adalah postgresist, mereka menggunakan WAL-G. Oleh itu, kita perlu memisahkan bahagian-bahagian ini. Iaitu, apabila kami mengedit kod untuk Postgres, kami tidak memecahkan MySQL; apabila kami mengedit MySQL, kami tidak memecahkan Postgres.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Idea pertama tentang cara memisahkan ini ialah idea menggunakan pendekatan yang sama yang digunakan dalam sambungan PostgreSQL. Dan, sebenarnya, untuk membuat sandaran MySQL anda perlu memasang beberapa jenis perpustakaan dinamik.

Tetapi di sini asimetri pendekatan ini dapat dilihat dengan serta-merta. Apabila anda membuat sandaran Postgres, anda meletakkan sandaran biasa untuk Postgres padanya dan semuanya baik-baik saja. Dan untuk MySQL ternyata anda memasang sandaran untuk Postgres dan juga memasang perpustakaan dinamik untuk MySQL untuknya. Bunyinya agak pelik. Kami juga berfikir begitu dan memutuskan bahawa ini bukan penyelesaian yang kami perlukan.

Pelbagai binaan untuk Postgres, MySQL, MongoDB, Redis

Tetapi ini membolehkan kami, nampaknya kami, membuat keputusan yang betul - untuk memperuntukkan perhimpunan yang berbeza untuk pangkalan yang berbeza. Ini memungkinkan untuk mengasingkan logik yang terikat pada sandaran pelbagai pangkalan data yang akan mengakses API biasa yang WAL-G laksanakan.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Ini adalah bahagian yang kami tulis sendiri - sebelum memberikan masalah kepada pelajar. Iaitu, ini adalah bahagian yang mereka boleh melakukan sesuatu yang salah, jadi kami memutuskan bahawa kami lebih baik melakukan sesuatu seperti ini dan semuanya akan baik-baik saja.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Selepas itu kami memberikan masalah. Mereka segera dibongkar. Pelajar dikehendaki menyokong tiga pangkalan.

Ini adalah MySQL, yang telah kami sandarkan menggunakan WAL-G dengan cara ini selama lebih daripada setahun.

Dan kini MongoDB menghampiri pengeluaran, di mana mereka menyelesaikannya dengan fail. Malah, kami menulis rangka kerja untuk semua ini. Kemudian pelajar menulis beberapa perkara yang boleh dilaksanakan. Dan kemudian kami membawa mereka ke keadaan yang boleh kami terima dalam pengeluaran.

Masalah ini tidak kelihatan seperti pelajar perlu menulis alat sandaran yang lengkap untuk setiap pangkalan data ini. Kami tidak mempunyai masalah seperti itu. Masalah kami ialah kami mahukan pemulihan titik dalam masa dan kami mahu membuat sandaran ke awan. Dan mereka meminta pelajar menulis beberapa kod yang akan menyelesaikan masalah ini. Pelajar menggunakan alat sandaran yang sedia ada, yang entah bagaimana mengambil sandaran, dan kemudian melekatkannya bersama-sama dengan WAL-G, yang memajukan semuanya ke awan. Dan mereka juga menambah pemulihan titik dalam masa ini.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Apa lagi yang dibawa oleh pelajar? Mereka membawa sokongan penyulitan Libsodium ke WAL-G.

Kami juga mempunyai dasar storan sandaran. Kini sandaran boleh ditandakan sebagai kekal. Dan entah bagaimana lebih mudah untuk perkhidmatan anda mengautomasikan proses menyimpannya.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Apakah keputusan eksperimen ini?

Lebih daripada 100 orang pada mulanya mendaftar untuk kursus tersebut. Pada mulanya saya tidak mengatakan bahawa universiti di Yekaterinburg adalah Universiti Persekutuan Ural. Kami mengumumkan segala-galanya di sana. 100 orang mendaftar. Pada hakikatnya, lebih sedikit orang mula melakukan sesuatu, kira-kira 30 orang.

Malah lebih sedikit orang yang menamatkan kursus, kerana perlu menulis ujian untuk kod yang sudah wujud. Dan juga membetulkan beberapa pepijat atau membuat beberapa ciri. Dan beberapa pelajar masih menutup kursus.

Pada masa ini, semasa kursus ini, pelajar telah membetulkan kira-kira 14 isu dan membuat 10 ciri pelbagai saiz. Dan, nampaknya saya, ini adalah penggantian penuh satu atau dua pembangun.

Antaranya, kami mengeluarkan diploma dan kerja kursus. Dan 12 menerima diploma. 6 daripada mereka telah pun mempertahankan diri mereka di "5". Mereka yang tinggal belum mendapat perlindungan, tetapi saya fikir semuanya akan baik untuk mereka juga.

ΠŸΠ»Π°Π½Ρ‹ Π½Π° Π±ΡƒΠ΄ΡƒΡ‰Π΅Π΅

Apakah rancangan yang kita ada untuk masa depan?

Sekurang-kurangnya permintaan ciri yang telah kami dengar daripada pengguna dan mahu lakukan. ini:

  • Memantau ketepatan penjejakan garis masa dalam arkib sandaran gugusan HA. Anda boleh melakukan ini dengan WAL-G. Dan saya fikir kita akan mempunyai pelajar yang akan mengambil perkara ini.
  • Kami sudah mempunyai orang yang bertanggungjawab untuk memindahkan sandaran dan WAL antara awan.
  • Dan baru-baru ini kami menerbitkan idea bahawa kami boleh mempercepatkan WAL-G dengan lebih banyak lagi dengan membongkar sandaran tambahan tanpa menulis semula halaman dan mengoptimumkan arkib yang kami hantar ke sana.

Anda boleh berkongsi di sini

Untuk apa laporan ini? Lebih-lebih lagi, kini, sebagai tambahan kepada 4 orang yang menyokong projek ini, kami mempunyai tangan tambahan, yang jumlahnya agak banyak. Terutama jika anda menulis kepada mereka dalam mesej peribadi. Dan jika anda menyandarkan data anda dan melakukannya menggunakan WAL-G atau ingin beralih ke WAL-G, maka kami boleh dengan mudah memenuhi kehendak anda.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

Ini adalah kod QR dan pautan. Anda boleh melaluinya dan tulis semua keinginan anda. Sebagai contoh, kami tidak membetulkan beberapa pepijat. Atau anda benar-benar mahukan beberapa ciri, tetapi atas sebab tertentu ia belum lagi berada dalam mana-mana sandaran, termasuk kami. Pastikan anda menulis tentang ini.

WAL-G: ciri baharu dan pengembangan komuniti. Georgy Rylov

soalan

helo! Terima kasih atas laporan itu! Soalan tentang WAL-G, tetapi bukan tentang Postgres. WAL-G menyandarkan MySQL dan memanggil sandaran tambahan. Jika kami mengambil pemasangan moden pada CentOS dan jika anda memasang MySQL, MariDB akan dipasang. Daripada versi 10.3 sandaran tambahan tidak disokong, sandaran MariDB disokong. Apa khabar anda dengan ini?

Pada masa ini kami belum cuba membuat sandaran MariDB. Kami mempunyai permintaan untuk sokongan FoundationDB, tetapi secara umum, jika terdapat permintaan sedemikian, maka kami boleh mencari orang yang akan melakukannya. Ia tidaklah lama atau sesusah yang saya fikirkan.

Selamat petang Terima kasih atas laporan itu! Soalan tentang potensi ciri baharu. Adakah anda bersedia untuk membuat WAL-G berfungsi dengan pita supaya anda boleh membuat sandaran kepada pita?

Sandaran pada penyimpanan pita nampaknya bermakna?

Ya.

Terdapat Andrei Borodin, yang boleh menjawab soalan ini lebih baik daripada saya.

(Andrey) Ya, terima kasih atas soalan itu! Kami mempunyai permintaan untuk memindahkan sandaran ke pita daripada storan awan. Dan untuk ini menggergaji pemindahan antara awan. Kerana pemindahan awan ke awan ialah versi umum pemindahan pita. Di samping itu, kami mempunyai seni bina yang boleh diperluaskan dari segi Storan. By the way, banyak Storoges ditulis oleh pelajar. Dan jika anda menulis Storan untuk pita, maka ia, sudah tentu, akan disokong. Kami bersedia untuk mempertimbangkan permintaan tarik. Di sana anda perlu menulis fail, membaca fail. Jika anda melakukan perkara ini dalam Go, anda biasanya mendapat 50 baris kod. Dan kemudian pita akan disokong dalam WAL-G.

Terima kasih atas laporan itu! Proses pembangunan yang menarik. Sandaran ialah fungsi serius yang harus dilindungi dengan baik oleh ujian. Apabila anda melaksanakan fungsi untuk pangkalan data baharu, adakah pelajar juga menulis ujian, atau adakah anda menulis sendiri ujian dan kemudian memberikan pelaksanaannya kepada pelajar?

Pelajar juga menulis ujian. Tetapi pelajar menulis lebih banyak untuk ciri seperti pangkalan data baharu. Mereka menulis ujian integrasi. Dan mereka menulis ujian unit. Jika penyepaduan berlalu, iaitu, pada masa ini, ini adalah skrip yang anda laksanakan secara manual atau anda mempunyai cron melakukannya, sebagai contoh. Maksudnya, skrip di sana sangat jelas.

Pelajar tidak mempunyai banyak pengalaman. Adakah semakan mengambil banyak masa?

Ya, ulasan mengambil masa yang agak lama. Iaitu, biasanya, apabila beberapa pengkomit datang serentak dan mengatakan bahawa saya melakukan ini, saya melakukan itu, maka anda perlu berfikir dan memperuntukkan kira-kira setengah hari untuk mengetahui apa yang mereka tulis di sana. Kerana kod itu mesti dibaca dengan teliti. Mereka tidak mempunyai temuduga. Kami tidak begitu mengenali mereka, jadi ia mengambil masa yang agak lama.

Terima kasih atas laporan itu! Sebelum ini, Andrey Borodin menyatakan bahawa archive_command dalam WAL-G harus dipanggil terus. Tetapi dalam kes beberapa jenis kartrij kluster, kita memerlukan logik tambahan untuk menentukan nod dari mana untuk menghantar aci. Bagaimana anda menyelesaikan masalah ini sendiri?

Apa masalah anda di sini? Katakan anda mempunyai replika segerak yang anda gunakan untuk membuat sandaran? Atau apa?

(Andrey) Hakikatnya sememangnya WAL-G bertujuan untuk digunakan tanpa skrip shell. Jika ada sesuatu yang hilang, maka mari kita tambah logik yang sepatutnya di dalam WAL-G. Mengenai dari mana pengarkiban harus berasal, kami percaya bahawa pengarkiban hendaklah daripada induk semasa dalam kelompok. Mengarkib daripada replika adalah idea yang tidak baik. Terdapat pelbagai kemungkinan senario dengan masalah. Khususnya, masalah dengan mengarkibkan garis masa dan sebarang maklumat tambahan. Terima kasih atas soalan!

(Penjelasan: Kami menyingkirkan skrip shell dalam isu ini)

Selamat petang! Terima kasih atas laporan itu! Saya berminat dengan ciri tangkapan yang anda bincangkan. Kami berhadapan dengan situasi di mana replika berada di belakang dan tidak dapat mengejar. Dan saya tidak menemui penerangan tentang ciri ini dalam dokumen WAL-G.

Catchup muncul secara literal pada 20 Januari 2020. Dokumentasi mungkin memerlukan lebih banyak kerja. Kami menulisnya sendiri dan kami tidak menulisnya dengan sangat baik. Dan mungkin kita harus mula meminta pelajar menulisnya.

Adakah ia sudah dikeluarkan?

Permintaan tarik sudah mati, iaitu saya menyemaknya. Saya mencuba ini pada kluster ujian. Setakat ini kami tidak mempunyai situasi di mana kami boleh menguji ini dalam contoh pertempuran.

Bila nak jangka?

saya tak tahu. Tunggu sebulan, kami akan semak untuk kepastian.

Sumber: www.habr.com

Tambah komen