Sejarah Arsitektur Dodo IS: Path Back Office

Habr ngganti donya. Kita wis blogging luwih saka setahun saiki. Udakara nem wulan kepungkur, kita nampa umpan balik sing logis saka Khabrovites: "Dodo, sampeyan ngomong ing endi wae yen sampeyan duwe sistem dhewe. Lan apa sistem iki? Lan kenapa chain pizza mbutuhake?

Kita lungguh, mikir lan ngerti yen sampeyan bener. Kita nyoba kanggo nerangake kabeh ing driji, nanging metu ing bêsik ambruk lan ora ana katrangan lengkap sistem. Mangkono wiwit perjalanan dawa kanggo ngumpulake informasi, nggoleki penulis lan nulis seri artikel babagan Dodo IS. Ayo budal!

Matur nuwun: Matur nuwun kanggo nuduhake umpan balik karo kita. Thanks kanggo dheweke, pungkasane kita nerangake sistem kasebut, nyusun radar teknis lan bakal cepet ngluncurake katrangan babagan proses kita. Tanpa sampeyan, kita bakal lungguh ing kono nganti 5 taun maneh.

Sejarah Arsitektur Dodo IS: Path Back Office

Seri artikel "Apa Dodo IS?" nyritakake babagan:

  1. Monolith awal ing Dodo IS (2011-2015). (Ing proses ...)
  2. Path kantor mburi: pangkalan lan bis sing kapisah. (kowe neng kene)
  3. Path sisih klien: fasad liwat pangkalan (2016-2017). (Ing proses…)
  4. Sajarah microservices bener. (2018-2019). (Ing proses ...)
  5. Rampung sawing saka monolith lan stabil saka arsitektur. (Ing proses ...)

Yen sampeyan kepengin sinau babagan liya, tulis ing komentar.

Panemu babagan deskripsi kronologis saka penulis
Aku rutin nganakake rapat kanggo karyawan anyar babagan topik "Arsitektur Sistem". Kita nyebat "Intro to Dodo IS Architecture" lan minangka bagean saka proses onboarding kanggo pangembang anyar. Ngomong ing salah siji wangun utawa liyane babagan arsitektur kita, babagan fitur, aku wis lair pendekatan sajarah tartamtu kanggo gambaran.

Cara tradisional, kita ndeleng sistem minangka sakumpulan komponen (tingkat teknis utawa luwih dhuwur), modul bisnis sing sesambungan karo saben liyane kanggo entuk sawetara tujuan. Lan yen tampilan kuwi sabdho kanggo desain, iku ora cukup cocok kanggo gambaran lan pangerten. Ana sawetara alasan ing kene:

  • Kasunyatan beda karo sing ana ing kertas. Ora kabeh bisa mlaku kaya sing direncanakake. Lan kita kasengsem ing carane iku bener nguripake metu lan dianggo.
  • Presentasi informasi sing konsisten. Nyatane, sampeyan bisa pindhah kanthi kronologis saka wiwitan nganti kahanan saiki.
  • Saka prasaja nganti kompleks. Ora universal, nanging ing kasus kita. Arsitèktur dipindhah saka pendekatan sing luwih prasaja menyang sing luwih rumit. Asring, masalah kacepetan lan stabilitas implementasine ditanggulangi kanthi komplikasi, uga puluhan properti liyane saka dhaptar syarat non-fungsi (kene uga ngandika babagan kontras kerumitan karo syarat liyane).

Ing 2011, arsitektur Dodo IS katon kaya iki:

Sejarah Arsitektur Dodo IS: Path Back Office

Ing taun 2020, wis dadi luwih rumit lan dadi kaya iki:

Sejarah Arsitektur Dodo IS: Path Back Office

Kepiye evolusi iki kedadeyan? Apa beda bagean sistem dibutuhake? Apa keputusan arsitektur sing digawe lan kenapa? Ayo goleki ing seri artikel iki.

Masalah pisanan 2016: kenapa layanan kudu ninggalake monolith

Artikel pisanan saka siklus bakal babagan layanan sing pisanan dipisahake saka monolit. Kanggo nggawe sampeyan ing konteks, Aku bakal pitutur marang kowe apa masalah kita wis ing sistem ing awal 2016, sing kita kudu menehi hasil karo misahake layanan.

Basis data MySql siji, ing ngendi kabeh aplikasi sing ana ing Dodo IS nulis cathetane. Akibate yaiku:

  • Beban abot (kanthi 85% panjaluk sing diwaca).
  • Dasar wis tuwuh. Amarga iki, biaya lan dhukungan dadi masalah.
  • Titik kegagalan tunggal. Yen salah siji aplikasi nulis ing database dumadakan wiwit nindakaken luwih aktif, banjur aplikasi liyane felt ing dhewe.
  • Inefficiency ing panyimpenan lan pitakon. Asring data disimpen ing sawetara struktur sing trep kanggo sawetara skenario nanging ora cocok kanggo liyane. Indeks nyepetake sawetara operasi, nanging bisa nyuda liyane.
  • Sawetara masalah dibusak dening caches digawe cepet-cepet lan replika diwaca kanggo dhasar (iki bakal dadi artikel kapisah), nanging padha mung ngidini kanggo gain wektu lan ora dhasar ngatasi masalah.

Masalah ana ing ngarsane monolith dhewe. Akibate yaiku:

  • Rilis tunggal lan langka.
  • Kesulitan ing pangembangan gabungan saka akeh wong.
  • Ora bisa nggawa teknologi anyar, kerangka kerja lan perpustakaan anyar.

Masalah karo basa lan monolit wis diterangake kaping pirang-pirang, contone, ing konteks kacilakan ing awal 2018 (Dadi kaya Munch, utawa sawetara tembung babagan utang teknis, Dina Dodo IS mandheg. Skrip Asynchronous и Crita manuk Dodo saka kulawarga Phoenix. Gugur Agung Dodo IS), dadi aku ora bakal manggon akeh. Ayo kula ujar manawa kita pengin menehi keluwesan luwih akeh nalika ngembangake layanan. Kaping pisanan, iki gegayutan karo sing paling akeh dimuat lan oyod ing kabeh sistem - Auth lan Tracker.

The Back Office Path: Bases kapisah lan Bus

Bab Navigasi

  1. Skema Monolith 2016
  2. Miwiti Mbongkar Monolith: Pemisahan Auth lan Tracker
  3. Apa sing ditindakake Auth?
  4. Saking pundi muatanipun?
  5. Unloading Auth
  6. Apa sing ditindakake Tracker?
  7. Saking pundi muatanipun?
  8. Unloading Tracker

Skema Monolith 2016

Punika pamblokiran utama Dodo IS 2016 monolith, lan ing ngisor iki transcript saka tugas utama.
Sejarah Arsitektur Dodo IS: Path Back Office
Pangiriman Kasir. Akuntansi kanggo kurir, nerbitake pesenan kanggo kurir.
Pusat kontak. Penerimaan pesenan liwat operator.
situs. Situs web kita (dodopizza.ru, dodopizza.co.uk, dodopizza.by, lsp).
Panganggoan berkas. Layanan wewenang lan otentikasi kanggo kantor mburi.
tracker. Tracker pesenan ing pawon. Layanan kanggo menehi tandha status kesiapan nalika nyiapake pesenan.
Meja kas Restoran. Njupuk pesenan ing restoran, antarmuka kasir.
kaca. Ngunggah laporan ing 1C kanggo akuntansi.
Kabar lan invoice. Printah swara ing pawon (contone, "Pizza anyar teka") + printing invoice kanggo kurir.
Manajer Shift. Antarmuka kanggo karya manajer shift: dhaptar pesenan, grafik kinerja, transfer karyawan menyang shift.
Kantor manager. Antarmuka kanggo karya franchisee lan manager: reception saka karyawan, laporan ing karya saka pizzeria.
Papan skor restoran. Tampilan menu ing TV ing pizzerias.
admin. Setelan ing pizzeria tartamtu: menu, prices, accounting, kode promo, promosi, spanduk situs web, etc.
Akun Pribadi Karyawan. Jadwal kerja karyawan, informasi babagan karyawan.
Papan Motivasi Pawon. Layar kapisah sing macet ing pawon lan nampilake kacepetan pembuat pizza.
Komunikasi. Ngirim sms lan email.
FileStorage. Layanan dhewe kanggo nampa lan nerbitake file statis.

Upaya pisanan kanggo ngatasi masalah mbantu kita, nanging mung istirahat sementara. Dheweke ora dadi solusi sistem, mula jelas yen ana sing kudu ditindakake kanthi dhasar. Contone, dibagi database umum dadi sawetara liyane khusus.

Miwiti Mbongkar Monolith: Pemisahan Auth lan Tracker

Layanan utama sing banjur nulis lan maca saka database luwih akeh tinimbang liyane:

  1. Auth. Layanan wewenang lan otentikasi kanggo kantor mburi.
  2. Tracker. Tracker pesenan ing pawon. Layanan kanggo menehi tandha status kesiapan nalika nyiapake pesenan.

Apa sing ditindakake Auth?

Auth minangka layanan sing pangguna mlebu menyang kantor mburi (ana lawang independen sing kapisah ing sisih klien). Iki uga diarani ing panyuwunan kanggo mesthekake yen hak akses sing dibutuhake ana lan hak kasebut ora diganti wiwit mlebu pungkasan. Liwat iku, piranti mlebu pizzeria.

Contone, kita pengin mbukak tampilan kanthi status pesenan rampung ing TV sing digantung ing aula. Banjur kita mbukak auth.dodopizza.ru, pilih "Login minangka piranti", kode katon sing bisa dilebokake ing kaca khusus ing komputer manager shift, nuduhake jinis piranti (piranti). TV dhewe bakal ngalih menyang antarmuka sing dikarepake saka pizzeria lan wiwit nampilake jeneng pelanggan sing pesenan wis siap.

Sejarah Arsitektur Dodo IS: Path Back Office

Saking pundi muatanipun?

Saben pangguna ing kantor mburi mlebu menyang database, menyang tabel pangguna kanggo saben panyuwunan, narik pangguna liwat query sql lan mriksa yen dheweke duwe akses lan hak sing dibutuhake kanggo kaca iki.

Saben piranti nindakake perkara sing padha mung karo tabel piranti, mriksa peran lan aksese. A nomer akeh panjalukan kanggo database master ndadékaké kanggo loading lan sampah sumber daya saka database umum kanggo operasi iki.

Unloading Auth

Auth duwe domain terisolasi, yaiku, data babagan pangguna, login utawa piranti mlebu layanan (saiki) lan tetep ana. Yen ana sing butuh, dheweke bakal pindhah menyang layanan iki kanggo data.

WAS. Skema karya asli kaya ing ngisor iki:

Sejarah Arsitektur Dodo IS: Path Back Office

Aku pengin nerangake sethithik cara kerjane:

  1. A request saka njaba teka backend (ana Asp.Net MVC), ndadekke karo cookie sesi, kang digunakake kanggo njupuk data sesi saka Redis (1). Iku salah siji ngemot informasi bab akses, lan banjur akses menyang controller mbukak (3,4), utawa ora.
  2. Yen ora ana akses, sampeyan kudu ngliwati prosedur wewenang. Ing kene, kanggo kesederhanaan, ditampilake minangka bagéan saka path ing atribut sing padha, sanajan iku transisi menyang kaca login. Ing kasus skenario positif, kita bakal entuk sesi sing wis rampung kanthi bener lan pindhah menyang Backoffice Controller.
  3. Yen ana data, sampeyan kudu mriksa relevansi ing basis pangguna. Apa perane wis diganti, apa dheweke ora diidini ing kaca saiki? Ing kasus iki, sawise nampa sesi (1), sampeyan kudu langsung menyang database lan mriksa akses pangguna nggunakake lapisan logika otentikasi (2). Sabanjure, menyang kaca login, utawa menyang controller. Sistem sing prasaja, nanging ora cukup standar.
  4. Yen kabeh prosedur liwati, banjur kita skip luwih ing logika ing pengontrol lan cara.

Data pangguna dipisahake saka kabeh data liyane, disimpen ing tabel anggota sing kapisah, fungsi saka lapisan logika AuthService bisa uga dadi metode api. Wates domain ditetepake kanthi cetha: pangguna, peran, akses data, menehi lan mbatalake akses. Kabeh katon supaya bisa dijupuk metu ing layanan kapisah.

DADI. Dadi padha nindakake:

Sejarah Arsitektur Dodo IS: Path Back Office

Pendekatan iki duwe sawetara masalah. Contone, nelpon cara ing proses ora padha nelpon layanan njaba liwat http. Latency, linuwih, maintainability, transparan operasi temen beda. Andrey Morevskiy ngandika ing liyane rinci babagan masalah kuwi ing laporan. "50 Shades of Microservices".

Layanan otentikasi lan, kanthi, layanan piranti digunakake kanggo kantor mburi, yaiku, kanggo layanan lan antarmuka sing digunakake ing produksi. Otentikasi kanggo layanan klien (kaya situs web utawa aplikasi seluler) ditindakake kanthi kapisah tanpa nggunakake Auth. Pemisahan njupuk kira-kira setahun, lan saiki kita lagi nangani topik iki, nransfer sistem menyang layanan otentikasi anyar (karo protokol standar).

Yagene perpisahan kuwi suwe banget?
Ana akeh masalah ing dalan sing nyebabake kita mudhun:

  1. Kita pengin nransfer data pangguna, piranti, lan otentikasi saka database khusus negara menyang siji. Kanggo nindakake iki, kita kudu nerjemahake kabeh tabel lan panggunaan saka pengenal int menyang pengenal UUId global (mung gawe maneh kode iki. Roman Bukin "Uuid - crita gedhe saka struktur cilik" lan proyek open source Primitif). Nyimpen data pangguna (amarga informasi pribadhi) duwe watesan lan kanggo sawetara negara kudu disimpen kanthi kapisah. Nanging id global pangguna kudu.
  2. Akeh tabel ing database duwe informasi audit babagan pangguna sing nindakake operasi kasebut. Iki mbutuhake mekanisme tambahan kanggo konsistensi.
  3. Sawise nggawe layanan API, ana wektu transfer sing dawa lan bertahap menyang sistem liyane. Ngalih kudu kelakon seamlessly kanggo pangguna lan mbutuhake karya manual.

Skema kanggo ndhaptar piranti ing pizzeria:

Sejarah Arsitektur Dodo IS: Path Back Office

Arsitektur umum sawise ekstraksi layanan Auth and Devices:

Sejarah Arsitektur Dodo IS: Path Back Office

komentar. Kanggo 2020, kita nggarap versi anyar Auth, sing adhedhasar standar wewenang OAuth 2.0. Standar iki cukup rumit, nanging migunani kanggo ngembangake layanan otentikasi pass-through. Ing artikel "Subtleties wewenang: ringkesan teknologi OAuth 2.0»we Alexey Chernyaev nyoba ngomong babagan standar kanthi gampang lan jelas supaya sampeyan bisa ngirit wektu kanggo sinau.

Apa sing ditindakake Tracker?

Saiki babagan nomer loro saka layanan sing dimuat. Tracker nindakake peran ganda:

  • Ing tangan siji, tugase nuduhake karyawan ing pawon apa pesenan sing saiki diproses, produk apa sing kudu dimasak saiki.
  • Ing tangan liyane, kanggo digitalisasi kabeh pangolahan ing pawon.

Sejarah Arsitektur Dodo IS: Path Back Office

Nalika produk anyar katon ing pesenan (Contone, pizza), dadi kanggo Rolling metu stasiun tracker. Ing stasiun iki, ana pembuat pizza sing njupuk roti ukuran sing dibutuhake lan gulung metu, lan banjur nyathet ing tablet tracker yen dheweke wis ngrampungake tugas lan nransfer basis adonan sing digulung menyang stasiun sabanjure - "Inisiasi" .

Ing kana, pembuat pizza sabanjure ngisi pizza, banjur nyathet ing tablet yen dheweke wis ngrampungake tugase lan nyelehake pizza ing oven (iki uga minangka stasiun kapisah sing kudu dicathet ing tablet). Sistem sing kaya mengkono iku wiwit wiwitan ing Dodo lan wiwit wiwitan ana Dodo IS. Iki ngidini sampeyan nglacak lan digitalisasi kabeh transaksi kanthi lengkap. Kajaba iku, tracker nyaranake carane masak produk tartamtu, nuntun saben jinis produk miturut skema manufaktur, nyimpen wektu masak sing optimal kanggo produk kasebut, lan nglacak kabeh operasi ing produk kasebut.

Sejarah Arsitektur Dodo IS: Path Back OfficeIki minangka tampilan layar tablet ing stasiun tracker Raskatka.

Saking pundi muatanipun?

Saben pizzeria duwe limang tablet kanthi tracker. Ing 2016, kita duwe luwih saka 100 pizzeria (lan saiki luwih saka 600). Saben tablet nggawe panjalukan menyang backend sapisan saben 10 detik lan scrape data saka tabel pesenan (sambungan karo klien lan alamat), komposisi pesenan (sambungan karo produk lan indikasi jumlahe), tabel akuntansi motivasi (ing wektu mencet dilacak ing). Nalika pembuat pizza ngeklik produk ing tracker, entri ing kabeh tabel iki dianyari. Tabel pesenan umum, uga ngemot sisipan nalika nampa pesenan, nganyari saka bagean liya saka sistem lan akeh maca, contone, ing TV sing macet ing pizzeria lan nuduhake pesenan sing wis rampung menyang pelanggan.

Sajrone periode perjuangan karo beban, nalika kabeh lan kabeh wis di-cache lan ditransfer menyang replika asynchronous saka basa, operasi iki karo tracker terus menyang basis master. Ora ana lag, data kudu up-to-date, ora sinkron ora bisa ditampa.

Kajaba iku, kekurangan tabel lan indeks dhewe ora ngidini nulis pitakon sing luwih spesifik sing cocog kanggo panggunaane. Contone, bisa uga efisien kanggo tracker duwe indeks kanggo pizzeria ing meja pesenan. Kita tansah mbusak pesenan pizzeria saka database tracker. Ing wektu sing padha, kanggo nampa pesenan, iku ora dadi penting kang pizzeria tumiba menyang, iku luwih penting kang klien digawe pesenan iki. Lan tegese ana indeks ing klien perlu. Sampeyan uga ora perlu kanggo tracker kanggo nyimpen id saka panrimo dicithak utawa promosi bonus gadhah pesenan ing meja pesenan. Informasi iki ora menarik kanggo layanan tracker kita. Ing basis data monolitik umum, tabel mung bisa dadi kompromi antarane kabeh pangguna. Iki minangka salah sawijining masalah asli.

WAS. Arsitektur asli yaiku:

Sejarah Arsitektur Dodo IS: Path Back Office

Sanajan sawise dipisahake dadi proses sing kapisah, umume basis kode tetep umum ing macem-macem layanan. Kabeh ing sangisore pengontrol digabungake lan manggon ing siji gudang. Cara umum layanan, repositori, lan basis data umum sing ngemot tabel umum digunakake.

Unloading Tracker

Masalah utama karo tracker yaiku data kudu disinkronake ing antarane database sing beda. Iki uga minangka prabédan utama saka pamisahan layanan Auth, urutan lan statuse bisa diganti lan kudu ditampilake ing layanan sing beda-beda.

Kita nampa pesenan ing Restaurant kang Checkout (iki layanan), disimpen ing database ing status "Ditampa". Sawisé iku, iku kudu njaluk menyang tracker, kang bakal ngganti status kaping pirang-pirang: saka "Pawon" kanggo "Dikempalken". Ing wektu sing padha, sawetara pengaruh eksternal saka Kasir utawa antarmuka Shift Manager bisa kedadeyan karo pesenan kasebut. Aku bakal menehi status pesenan kanthi katrangan ing tabel:

Sejarah Arsitektur Dodo IS: Path Back Office
Skema kanggo ngganti status pesenan katon kaya iki:

Sejarah Arsitektur Dodo IS: Path Back Office

Status ganti antarane sistem sing beda. Lan ing kene tracker dudu sistem pungkasan sing data ditutup. Kita wis ndeleng sawetara pendekatan sing bisa dipisahake ing kasus kasebut:

  1. We musataken kabeh tumindak supaya ing siji layanan. Ing kasus kita, pilihan iki mbutuhake akeh banget layanan kanggo nggarap pesenan. Yen kita mandheg ing, kita bakal entuk monolit kapindho. Kita ora bakal ngatasi masalah kasebut.
  2. Siji sistem nelpon menyang liyane. Opsi kapindho wis luwih menarik. Nanging karo iku, chain saka telpon bisa (gagal cascading), konektivitas komponen luwih dhuwur, luwih angel diatur.
  3. We ngatur acara, lan saben layanan komunikasi karo liyane liwat acara iki. Akibaté, iku pilihan katelu sing dipilih, miturut kang kabeh layanan wiwiti ijol-ijolan acara karo saben liyane.

Kasunyatan bilih kita milih pilihan katelu temenan tracker bakal duwe database dhewe, lan kanggo saben owah-owahan ing urutan, iku bakal ngirim acara bab iki, kanggo layanan liyane langganan lan uga tiba ing database master. Kanggo nindakake iki, kita butuh sawetara layanan sing bakal njamin pangiriman pesen ing antarane layanan.

Ing wektu iku, kita wis duwe RabbitMQ ing tumpukan, mula keputusan pungkasan kanggo nggunakake minangka makelar pesen. Diagram nuduhake transisi pesenan saka Restaurant Cashier liwat Tracker, ngendi ngganti status lan tampilan ing antarmuka Pesenan Manager. DADI:

Sejarah Arsitektur Dodo IS: Path Back Office

Urutan langkah demi langkah
Path pesenan diwiwiti ing salah sawijining layanan sumber pesenan. Punika kasir restoran:

  1. Ing checkout, pesenan rampung siap, lan wektune ngirim menyang tracker. Acara sing langganan tracker dibuwang.
  2. Tracker, nampa pesenan kanggo dhewe, nyimpen menyang database dhewe, nggawe acara "Pesenan Ditampa dening Tracker" lan dikirim menyang RMQ.
  3. Sawetara pawang wis langganan bis acara khusus. Kanggo kita, sing nyinkronake karo database monolitik iku penting.
  4. Handler nampa acara, milih saka data sing penting: ing kasus kita, iki status pesenan "Ditampa dening Tracker" lan nganyari entitas pesenan ing database utama.

Yen wong mbutuhake pesenan saka pesenan meja monolitik, sampeyan uga bisa maca saka kono. Contone, antarmuka Pesenan ing Shift Manager mbutuhake iki:

Sejarah Arsitektur Dodo IS: Path Back Office

Kabeh layanan liyane uga bisa langganan pesenan acara saka tracker kanggo digunakake kanggo awake dhewe.

Yen sawise sawetara wektu, pesenan kasebut ditindakake, mula statuse diganti pisanan ing basis data (database Tracker), banjur acara "OrderIn Progress" langsung digawe. Iki uga mlebu RMQ, saka ngendi disinkronake ing basis data monolitik lan dikirim menyang layanan liyane. Bisa uga ana macem-macem masalah ing dalan, rincian liyane babagan iki bisa ditemokake ing laporan Zhenya Peshkov babagan rincian implementasine Konsistensi Akhire ing Tracker.

Arsitektur final sawise owah-owahan ing Auth lan Tracker

Sejarah Arsitektur Dodo IS: Path Back Office

Summing up hasil intermediate: Kaping pisanan, aku duwe ide kanggo ngemas sejarah sangang taun sistem Dodo IS dadi siji artikel. Aku pengin cepet lan mung ngomong babagan tahapan evolusi. Nanging, sawise lungguh kanggo sinau materi, aku nyadari yen kabeh luwih rumit lan menarik tinimbang sing katon.

Nggambarake babagan keuntungan (utawa kekurangane) saka materi kasebut, aku nyimpulake manawa pangembangan terus-terusan ora mungkin tanpa riwayat acara sing lengkap, retrospektif rinci lan analisa keputusanku sing kepungkur.

Muga-muga bisa migunani lan menarik kanggo sampeyan sinau babagan dalan kita. Saiki aku ngadhepi pilihan sing bagean saka sistem Dodo IS kanggo njlèntrèhaké ing artikel sabanjuré: nulis ing komentar utawa milih.

Mung pangguna pangguna sing bisa melu survey. mlebunggih.

Apa bagean Dodo IS sing pengin sampeyan ngerti ing artikel sabanjure?

  • 24,1%Monolit awal ing Dodo IS (2011-2015)14

  • 24,1%Masalah pisanan lan solusi (2015-2016)14

  • 20,7%Path sisih klien: fasad liwat basis (2016-2017)12

  • 36,2%Sajarah layanan mikro nyata (2018-2019)21

  • 44,8%Sawing lengkap saka monolit lan stabil saka arsitektur26

  • 29,3%Babagan rencana luwih lanjut kanggo pangembangan sistem17

  • 19,0%Aku ora pengin ngerti apa-apa bab Dodo IS11

58 pangguna milih. 6 kedhaftar abstained.

Source: www.habr.com

Add a comment