HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Næsta HighLoad++ ráðstefna verður haldin 6. og 7. apríl 2020 í St. Pétursborg.
Upplýsingar og miðar по ссылке. HighLoad++ Siberia 2019. Salur "Krasnoyarsk". 25. júní, 12:00. Ritgerðir og kynningu.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Það kemur fyrir að hagnýtar kröfur stangast á við kenningar þar sem ekki er tekið tillit til þátta sem eru mikilvægir fyrir viðskiptavöru. Í þessu erindi er kynnt ferli til að velja og sameina mismunandi aðferðir við að búa til orsakasamræmisþætti sem byggja á fræðilegum rannsóknum sem byggja á kröfum viðskiptavöru. Hlustendur munu læra um núverandi fræðilegar aðferðir við rökrænar klukkur, ávanaeftirlit, kerfisöryggi, klukkusamstillingu og hvers vegna MongoDB settist á ákveðnar lausnir.

Mikhail Tyulenev (hér eftir nefndur MT): - Ég mun tala um orsakasamkvæmni - þetta er eiginleiki sem við unnum að í MongoDB. Ég vinn í hópi dreifðra kerfa, við gerðum það fyrir um tveimur árum síðan.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Í því ferli þurfti ég að kynna mér mikið af fræðilegum rannsóknum, því þessi eiginleiki hefur verið rannsakaður nokkuð vel. Í ljós kom að engin ein grein passar inn í það sem krafist er í framleiðslugagnagrunni vegna mjög sérstakra krafna sem líklega eru til staðar í hvaða framleiðsluforriti sem er.

Ég ætla að tala um hvernig við, sem neytendur akademískra rannsókna, útbúum eitthvað úr því sem við getum síðan kynnt notendum okkar sem tilbúinn rétt sem er þægilegur og öruggur í notkun.

Orsakasamkvæmni. Skilgreinum hugtökin

Til að byrja með vil ég segja almennt hvað orsakasamkvæmni er. Það eru tvær persónur - Leonard og Penny (sjónvarpsþáttaröðin "The Big Bang Theory"):

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Segjum að Penny sé í Evrópu og Leonard vilji halda henni óvænt veislu. Og hann getur ekki hugsað sér neitt betra en að henda henni af vinalistanum sínum og senda öllum vinum sínum uppfærslu á straumi: „Við skulum gleðja Penny! (hún er í Evrópu, meðan hún sefur, sér þetta ekki allt og getur ekki séð það, því hún er ekki þar). Á endanum eyðir hún þessari færslu, eyðir henni úr straumnum og endurheimtir aðgang þannig að hún tekur ekki eftir neinu og það er enginn hneyksli.
Þetta er allt gott og vel, en við skulum gera ráð fyrir að kerfið sé dreift og það hafi farið aðeins úrskeiðis. Það getur til dæmis gerst að aðgangstakmörkun Penny hafi átt sér stað eftir að þessi færsla birtist, ef atburðir tengjast ekki orsök og afleiðingu. Reyndar er þetta dæmi um þegar orsakasamkvæmni er nauðsynleg til að framkvæma viðskiptaaðgerð (í þessu tilviki).

Reyndar eru þetta frekar óléttir eiginleikar gagnagrunnsins - mjög fáir styðja þá. Við skulum halda áfram að módelunum.

Samræmislíkön

Hvað nákvæmlega er samræmislíkan í gagnagrunnum? Þetta eru nokkrar af þeim tryggingum sem dreift kerfi gefur um hvaða gögn viðskiptavinurinn getur fengið og í hvaða röð.

Í grundvallaratriðum snúast öll samkvæmnilíkön um hversu líkt dreifðu kerfi er kerfi sem keyrir til dæmis á einum hnút á fartölvu. Og þetta er hversu svipað kerfi sem keyrir á þúsundum landdreifðra „hnúta“ er fartölvu, þar sem allar þessar eiginleikar eru framkvæmdar sjálfkrafa í grundvallaratriðum.

Þess vegna eru samræmislíkön aðeins notuð á dreifð kerfi. Öll kerfi sem áður voru til og störfuðu í sömu lóðréttu mælikvarða áttu ekki við slík vandamál að stríða. Það var eitt Buffer Cache og alltaf var allt lesið úr því.

Fyrirmynd sterk

Reyndar er fyrsta módelið Strong (eða risagetulínan, eins og hún er oft kölluð). Þetta er samræmislíkan sem tryggir að allar breytingar, eftir að hafa verið staðfestar að þær hafi átt sér stað, sé sýnilegar öllum notendum kerfisins.

Þetta skapar alþjóðlega röð allra atburða í gagnagrunninum. Þetta er mjög sterk samkvæmni og er almennt mjög dýr. Hins vegar er það mjög vel studd. Það er bara mjög dýrt og hægt - það er bara sjaldan notað. Þetta er kallað hækkunargeta.

Það er önnur, sterkari eign sem er studd í Spanner - sem kallast Ytri samræmi. Við tölum um það aðeins síðar.

Orsök

Næsta er Causal, sem er einmitt það sem ég var að tala um. Það eru nokkrir fleiri undirstig á milli Sterks og orsakasambands sem ég ætla ekki að tala um, en þau koma öll niður í orsakasamband. Þetta er mikilvægt líkan vegna þess að það er sterkasta af öllum gerðum, sterkasta samkvæmið í viðurvist nets eða skiptinga.

Orsakasambönd eru í raun aðstæður þar sem atburðir tengjast orsök og afleiðingu. Mjög oft er litið á þá sem Lesa réttindi þín frá sjónarhóli viðskiptavinarins. Ef viðskiptavinurinn hefur fylgst með einhverjum gildum getur hann ekki séð gildi sem voru í fortíðinni. Hann er þegar farinn að sjá forskeyti. Þetta kemur allt út á það sama.
Orsakasambönd sem samræmislíkan er að hluta röðun atburða á þjóninum, þar sem atburðir frá öllum viðskiptavinum eru skoðaðir í sömu röð. Í þessu tilfelli, Leonard og Penny.

Að lokum

Þriðja líkanið er Eventual Consistency. Þetta er það sem algerlega öll dreifð kerfi styðja, lágmarkslíkanið sem er skynsamlegt. Það þýðir eftirfarandi: þegar við höfum einhverjar breytingar á gögnunum verða þær á einhverjum tímapunkti í samræmi.

Á slíkri stundu segir hún ekki neitt, annars myndi hún breytast í Ytri samræmi - það væri allt önnur saga. Engu að síður er þetta mjög vinsælt líkan, það algengasta. Sjálfgefið er að allir notendur dreifðra kerfa nota Eventual Consistency.

Ég vil nefna nokkur samanburðardæmi:

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Hvað þýða þessar örvar?

  • Seinkun. Eftir því sem samræmisstyrkurinn eykst verður hann stærri af augljósum ástæðum: þú þarft að gera fleiri skrár, fá staðfestingu frá öllum vélum og hnútum sem taka þátt í klasanum að gögnin séu þegar til staðar. Í samræmi við það hefur Eventual Consistency hraðasta svarið, því þar er að jafnaði hægt að binda það í minni og þetta mun í grundvallaratriðum vera nóg.
  • Framboð. Ef við skiljum þetta sem getu kerfisins til að bregðast við þegar netkerfisbrot, skipting eða einhvers konar bilun eru til staðar, þá eykst bilanaþol eftir því sem samræmislíkanið minnkar, þar sem það er nóg fyrir okkur að einn gestgjafi lifi og á sama tíma tíminn framleiðir nokkur gögn. Endanleg samkvæmni ábyrgist alls ekki neitt um gögnin - það getur verið hvað sem er.
  • Frávik. Á sama tíma fjölgar auðvitað frávikum. Í Strong Consistency ættu þeir næstum alls ekki að vera til, en í Eventual Consistency geta þeir verið hvað sem er. Spurningin vaknar: hvers vegna velur fólk Eventual Consistency ef það inniheldur frávik? Svarið er að Eventual Consistency líkön eiga við og frávik eru til staðar, til dæmis á stuttum tíma; það er hægt að nota töframanninn til að lesa og meira eða minna lesa samræmd gögn; Oft er hægt að nota sterk samræmislíkön. Í reynd virkar þetta og oft er fjöldi frávika takmarkaður í tíma.

CAP setning

Þegar þú sérð orðin samkvæmni, framboð – hvað dettur þér í hug? Það er rétt - CAP setning! Nú vil ég eyða goðsögninni... Það er ekki ég - það er Martin Kleppmann, sem skrifaði frábæra grein, frábæra bók.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

CAP setningin er meginregla sem mótuð var á 2000. áratugnum að samræmi, framboð, skipting: taktu hvaða tvo sem er og þú getur ekki valið þrjár. Það var ákveðin meginregla. Það var sannað sem setning nokkrum árum síðar af Gilbert og Lynch. Svo fór að nota þetta sem þula - kerfi fóru að skipta í CA, CP, AP og svo framvegis.

Þessi setning var reyndar sönnuð fyrir eftirfarandi tilvik... Í fyrsta lagi var framboð ekki talið vera samfellt gildi frá núlli til hundruða (0 - kerfið er "dautt", 100 - svarar fljótt; við erum vön að líta á það þannig) , en sem eiginleiki reikniritsins , sem tryggir að fyrir allar framkvæmdir þess skilar það gögnum.

Það er alls ekki orð um viðbragðstíma! Það er til reiknirit sem skilar gögnum eftir 100 ár - alveg frábært tiltækt reiknirit, sem er hluti af CAP setningunni.
Í öðru lagi: setningin var sönnuð fyrir breytingar á gildum sama lykils, þrátt fyrir að þessar breytingar séu breytanlegar. Þetta þýðir að í raun og veru eru þau nánast ekki notuð, vegna þess að líkönin eru mismunandi Endanleg samræmi, sterk samræmi (kannski).

Til hvers er þetta allt? Þar að auki, CAP setningin í nákvæmlega því formi sem hún var sönnuð á nánast ekki við og er sjaldan notuð. Í fræðilegu formi takmarkar það einhvern veginn allt. Það kemur í ljós ákveðin meginregla sem er innsæi rétt, en hefur almennt ekki verið sönnuð.

Orsakasamkvæmni er sterkasta líkanið

Það sem er að gerast núna er að þú getur fengið allt þrennt: Samræmi, framboð með því að nota skipting. Sérstaklega er orsakasamkvæmni sterkasta samræmislíkanið, sem virkar enn í viðurvist skiptinga (brot á netinu). Þess vegna er það svo mikill áhugi og þess vegna tókum við það upp.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Í fyrsta lagi einfaldar það vinnu forritara. Sérstaklega nærvera mikils stuðnings frá þjóninum: þegar tryggt er að allar skrár sem eiga sér stað inni í einum viðskiptavini berast í sömu röð á öðrum viðskiptavini. Í öðru lagi þolir það skipting.

MongoDB innra eldhús

Við minnumst þess að það er hádegismatur og flytjum okkur fram í eldhús. Ég mun segja þér frá kerfislíkaninu, nefnilega hvað MongoDB er fyrir þá sem eru að heyra um slíkan gagnagrunn í fyrsta skipti.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

MongoDB (hér eftir nefnt „MongoDB“) er dreift kerfi sem styður lárétta mælikvarða, það er sharding; og innan hvers brots styður það einnig gagnaofframboð, það er að segja afritun.

Sharding í MongoDB (ekki tengslagagnagrunnur) framkvæmir sjálfvirka jafnvægisstillingu, það er að hverju safni skjala (eða „tafla“ hvað varðar tengslagögn) er skipt í hluta og þjónninn færir þau sjálfkrafa á milli brota.

Query Router, sem dreifir beiðnum, fyrir viðskiptavin er einhver viðskiptavinur sem hann vinnur í gegnum. Það veit nú þegar hvar og hvaða gögn eru staðsett og beinir öllum beiðnum á réttan brot.

Annað mikilvægt atriði: MongoDB er einn meistari. Það er eitt aðal - það getur tekið færslur sem styðja lyklana sem það inniheldur. Þú getur ekki skrifað Multi-master.

Við gerðum útgáfu 4.2 - þar birtust nýir áhugaverðir hlutir. Sérstaklega settu þeir Lucene - leit - nefnilega executable java beint inn í Mongo og þar varð hægt að framkvæma leit í gegnum Lucene, það sama og í Elastica.

Og þeir bjuggu til nýja vöru - Charts, hún er líka fáanleg á Atlas (eigin Cloud Mongo). Þeir eru með ókeypis stig - þú getur leikið þér með það. Mér líkaði mjög vel við töflur - gagnasýn, mjög leiðandi.

Innihald Orsakasamkvæmni

Ég taldi um 230 greinar sem hafa verið birtar um þetta efni - frá Leslie Lampert. Nú mun ég frá minni mínu flytja þér nokkra hluta af þessum efnum.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Þetta byrjaði allt með grein eftir Leslie Lampert, sem var skrifuð á áttunda áratugnum. Eins og þú sérð eru nokkrar rannsóknir á þessu efni enn í gangi. Nú er orsakasamkvæmni að upplifa áhuga í tengslum við þróun dreifðra kerfa.

Takmarkanir

Hvaða takmarkanir eru til staðar? Þetta er í rauninni eitt aðalatriðið, því þær hömlur sem framleiðslukerfi setur eru allt aðrar en þær hömlur sem eru í fræðigreinum. Þau eru oft frekar tilgerðarleg.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

  • Í fyrsta lagi er „MongoDB“ einn meistari, eins og ég sagði þegar (þetta einfaldar mjög).
  • Við teljum að kerfið eigi að standa undir um 10 þús. Við getum ekki tekið neinar byggingarfræðilegar ákvarðanir sem munu beinlínis takmarka þetta gildi.
  • Við erum með ský, en við gerum ráð fyrir að einstaklingur ætti samt að hafa tækifæri þegar hann hleður niður binary, keyrir það á fartölvunni sinni og allt virkar frábærlega.
  • Við gerum ráð fyrir einhverju sem Rannsóknir gera sjaldan ráð fyrir: ytri viðskiptavinir geta gert hvað sem þeir vilja. MongoDB er opinn uppspretta. Í samræmi við það geta viðskiptavinir verið svo klárir og reiðir - þeir geta viljað brjóta allt. Við veltum því fyrir okkur að býsanskir ​​feilors gætu átt uppruna sinn.
  • Fyrir utanaðkomandi viðskiptavini sem eru utan jaðarsins er mikilvæg takmörkun: ef þessi eiginleiki er óvirkur, þá ætti ekki að fylgjast með afköstum.
  • Annað atriði er almennt and-akademískt: samhæfni fyrri útgáfur og framtíðar. Gamlir reklar verða að styðja nýjar uppfærslur og gagnagrunnurinn verður að styðja gamla rekla.

Almennt séð setur allt þetta takmarkanir.

Orsakasamkvæmniþættir

Ég mun nú tala um suma þættina. Ef við lítum á orsakasamkvæmni almennt getum við valið blokkir. Við völdum úr verkum sem tilheyra ákveðinni blokk: Dependency Tracking, val á klukkum, hvernig hægt er að samstilla þessar klukkur hver við aðra og hvernig við tryggjum öryggi - þetta er í grófum dráttum af því sem ég mun tala um:

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Fullt ávanaeftirlit

Af hverju er þess þörf? Þannig að þegar gögn eru afrituð inniheldur hver skrá, hver gagnabreyting upplýsingar um hvaða breytingar þær eru háðar. Fyrsta og barnalega breytingin er þegar hvert skeyti sem inniheldur færslu inniheldur upplýsingar um fyrri skilaboð:

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Í þessu dæmi er talan í krulluðum sviga metnúmerin. Stundum eru þessar færslur með gildum jafnvel fluttar í heild sinni, stundum eru sumar útgáfur fluttar. Niðurstaðan er sú að hver breyting inniheldur upplýsingar um þá fyrri (ber augljóslega allt þetta innra með sér).

Hvers vegna ákváðum við að nota ekki þessa nálgun (fullt mælingar)? Augljóslega, vegna þess að þessi nálgun er óframkvæmanleg: allar breytingar á félagslegu neti eru háðar öllum fyrri breytingum á því félagslega neti, td flytja Facebook eða VKontakte í hverri uppfærslu. Engu að síður er mikið af rannsóknum um mælingar á fullri ósjálfstæði – þetta eru forsamfélagsnet; fyrir sumar aðstæður virkar það virkilega.

Skýr eftirlitsmæling

Næsta er takmarkaðra. Hér er einnig litið til upplýsingamiðlunar, en aðeins það sem augljóslega er háð. Hvað fer eftir hverju, að jafnaði, ræðst af umsókninni. Þegar gögn eru afrituð skilar fyrirspurnin aðeins svörum þegar fyrri ósjálfstæði hefur verið fullnægt, það er að segja sýnt. Þetta er kjarninn í því hvernig orsakasamkvæmni virkar.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Hún sér að skrá 5 er háð færslum 1, 2, 3, 4 - í samræmi við það bíður hún þar til viðskiptavinurinn hefur aðgang að breytingunum sem gerðar voru með aðgangsákvörðun Penny, þegar allar fyrri breytingar hafa þegar farið í gegnum gagnagrunninn.

Þetta hentar okkur ekki heldur, því það er enn of mikið af upplýsingum og það mun hægja á hlutunum. Það er önnur nálgun ...

Lamport klukka

Þeir eru mjög gamlir. Lamport klukka þýðir að þessar ósjálfstæðir eru brotnar saman í mælikvarða, sem kallast Lamport klukka.

Stöðvarfall er einhver óhlutbundin tala. Það er oft kallað rökréttur tími. Með hverjum atburði hækkar þessi teljari. Teljari, sem nú er þekktur fyrir ferlið, sendir hvert skeyti. Það er ljóst að ferli geta verið afsamstillt, þeir geta haft allt aðra tíma. Engu að síður jafnar kerfið klukkuna á einhvern hátt með slíkum skilaboðum. Hvað gerist í þessu tilfelli?

Ég skipti þessum stóra broti í tvennt til að gera það ljóst: Vinir geta búið í einum hnút, sem inniheldur hluta af safninu, og Feed getur búið í öðrum hnút, sem inniheldur hluta af þessu safni. Er ljóst hvernig þeir geta farið út úr röðinni? Fyrsta straumurinn mun segja: „Replicated“ og síðan Friends. Ef kerfið veitir ekki einhvers konar tryggingu fyrir því að straumurinn verði ekki sýndur fyrr en Friends-fíknin í Friends safninu eru líka afhent, þá munum við hafa nákvæmlega ástandið sem ég nefndi.

Þú sérð hvernig teljaratími á straumi eykst rökrétt:

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Þannig að aðaleiginleiki þessarar Lamport klukku og orsakasamkvæmni (útskýrð með Lamport klukku) er þessi: ef við höfum atburði A og B, og atburður B fer eftir atburði A*, þá leiðir það af því að röklegur tími atburðar A er minni en en LogicalTime frá atburði B.

* Stundum segja þeir líka að A hafi gerst á undan B, það er að A hafi gerst á undan B - þetta er ákveðið samband sem að hluta til skipar öllu mengi atburða sem gerðust almennt.

Hið gagnstæða er rangt. Þetta er í raun einn helsti ókosturinn við Lamport Clock - hlutaröð. Það er til hugtak um samtímis atburði, það er atburðir þar sem hvorki (A gerðist á undan B) né (A gerðist á undan B). Dæmi væri samhliða viðbót Leonards við einhvern annan sem vin (ekki einu sinni Leonard, heldur Sheldon, til dæmis).
Þetta er eiginleiki sem er oft notaður þegar unnið er með Lamport klukkur: þær skoða sérstaklega virknina og af þessu draga þær þá ályktun að ef til vill séu þessir atburðir háðir. Vegna þess að ein leið er sönn: ef LogicalTime A er minna en LogicalTime B, þá getur B ekki gerst á undan A; og ef meira, þá kannski.

Vektor klukka

Rökrétt þróun Lamport klukkunnar er Vector Clock. Þeir eru ólíkir að því leyti að hver hnút sem er hér inniheldur sína eigin klukku og þeir eru sendir sem vektor.
Í þessu tilviki sérðu að núllvísitalan á vigri er ábyrg fyrir straumi og fyrsta vísitalan á vigri er fyrir Friends (hver þessara hnúta). Og nú munu þeir hækka: núllvísitalan á „straumi“ hækkar þegar þú skrifar - 1, 2, 3:

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Af hverju er Vector Clock betri? Vegna þess að þeir leyfa þér að reikna út hvaða atburðir eru samtímis og hvenær þeir eiga sér stað á mismunandi hnútum. Þetta er mjög mikilvægt fyrir klippikerfi eins og MongoDB. Hins vegar völdum við þetta ekki, þó þetta sé dásamlegur hlutur, og það virkar frábærlega, og það myndi líklega henta okkur...

Ef við erum með 10 þúsund shards getum við ekki flutt 10 þúsund íhluti, jafnvel þótt við þjöppum þeim saman eða komum með eitthvað annað - farmurinn verður samt margfalt minni en rúmmál alls þessa vektor. Þess vegna, með því að gnísta hjörtu okkar og tennur, hættum við þessari nálgun og fórum yfir í aðra.

Spanner TrueTime. Atómklukka

Ég sagði að það yrði saga um Spanner. Þetta er töff hlutur, beint úr XNUMX. öld: atómklukkur, GPS samstilling.

Hver er hugmyndin? „Spanner“ er Google kerfi sem nýlega varð jafnvel aðgengilegt fólki (þeir bættu SQL við það). Hver viðskipti þar hafa einhvern tímastimpil. Þar sem tíminn er samstilltur* er hægt að úthluta hverjum atburði ákveðnum tíma - atómklukkur hafa biðtíma, eftir það er tryggt að annar tími "gerist".

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Þannig, með því einfaldlega að skrifa í gagnagrunninn og bíða í einhvern tíma, er raðhæfni viðburðarins sjálfkrafa tryggð. Þeir hafa sterkasta samræmislíkanið sem hægt er að ímynda sér í grundvallaratriðum - það er ytri samræmi.

* Þetta er helsta vandamálið með Lampart klukkur - þær eru aldrei samstilltar á dreifðum kerfum. Þeir geta verið ólíkir; jafnvel með NTP virka þeir samt ekki mjög vel. „Spanner“ er með atómklukku og samstilling, að því er virðist, er míkrósekúndur.

Af hverju völdum við ekki? Við gerum ekki ráð fyrir að notendur okkar séu með innbyggða atómklukku. Þegar þær birtast, innbyggðar í hverja fartölvu, verður einhver ofboðslega flott GPS samstilling - þá já... En í augnablikinu er það besta sem er mögulegt er Amazon, Base Stations - fyrir ofstækismenn... Svo notuðum við önnur úr .

Hybrid klukka

Þetta er í raun það sem merkir í MongoDB þegar tryggt er orsakasamkvæmni. Hvernig eru þeir blendingar? Hybrid er stigstærðgildi, en það hefur tvo þætti:

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

  • Hið fyrra er Unix-tímabilið (hversu margar sekúndur eru liðnar frá „upphafi tölvuheimsins“).
  • Annað er einhver aukning, einnig 32-bita óundirritaður int.

Það er allt, reyndar. Það er þessi nálgun: sá hluti sem ber ábyrgð á tímanum er samstilltur við klukkuna allan tímann; í hvert skipti sem uppfærsla á sér stað er þessi hluti samstilltur við klukkuna og þá kemur í ljós að tíminn er alltaf nokkurn veginn réttur og aukning gerir þér kleift að greina á milli atburða sem áttu sér stað á sama tímapunkti.

Af hverju er þetta mikilvægt fyrir MongoDB? Vegna þess að það gerir þér kleift að búa til einhvers konar varaveitingahús á ákveðnum tímapunkti, það er að viðburðurinn er verðtryggður eftir tíma. Þetta er mikilvægt þegar þörf er á ákveðnum viðburðum; Fyrir gagnagrunn eru atburðir breytingar á gagnagrunninum sem áttu sér stað með ákveðnu millibili í tíma.

Ég mun segja þér mikilvægustu ástæðuna aðeins fyrir þér (vinsamlegast, ekki segja neinum)! Við gerðum þetta vegna þess að þetta er hvernig skipulögð, verðtryggð gögn líta út í MongoDB OpLog. OpLog er gagnaskipulag sem inniheldur nákvæmlega allar breytingar á gagnagrunninum: þær fara fyrst í OpLog og síðan er þeim beitt á geymsluna sjálft ef um er að ræða endurtekna dagsetningu eða brot.

Þetta var aðalástæðan. Samt eru líka hagnýtar kröfur til að þróa gagnagrunn, sem þýðir að hann ætti að vera einfaldur - lítill kóða, eins fáir brotnir hlutir og mögulegt er sem þarf að endurskrifa og prófa. Sú staðreynd að oplogarnir okkar voru verðtryggðir með blendingsklukkum hjálpaði mikið og gerði okkur kleift að velja rétt. Það borgaði sig virkilega og virkaði einhvern veginn á töfrandi hátt í fyrstu frumgerðinni. Það var mjög flott!

Samstilling klukku

Það eru nokkrar samstillingaraðferðir sem lýst er í vísindaritum. Ég er að tala um samstillingu þegar við erum með tvær mismunandi shards. Ef það er eitt eftirlíkingarsett er engin þörf á neinni samstillingu: þetta er „einn meistari“; við erum með OpLog, sem allar breytingar falla í - í þessu tilfelli er allt þegar raðað í röð í „Oplog“ sjálfum. En ef við erum með tvö mismunandi brot er tímasamstilling mikilvæg hér. Þetta er þar sem vektorklukkan hjálpaði meira! En við höfum þá ekki.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Annað er hentugur - þetta er "Hjartsláttur". Það er hægt að skiptast á nokkrum merkjum sem eiga sér stað á hverri tímaeiningu. En hjartsláttur er of hægur, við getum ekki veitt viðskiptavinum okkar biðtíma.

Sannur tími er auðvitað dásamlegur hlutur. En aftur, þetta er líklega framtíðin... Þó að það sé nú þegar hægt að gera það í Atlas, þá eru nú þegar til hröð „Amazon“ tímasamstillingartæki. En það mun ekki vera í boði fyrir alla.

Slúður er þegar öll skilaboð innihalda tíma. Þetta er um það bil það sem við notum. Sérhver skilaboð á milli hnúta, ökumanns, gagnahnútbeins, nákvæmlega allt fyrir MongoDB er einhvers konar þáttur, gagnagrunnsþáttur sem inniheldur klukku sem keyrir. Þeir hafa merkingu blendingstíma alls staðar, hann er sendur út. 64 bita? Þetta leyfir, þetta er hægt.

Hvernig virkar þetta allt saman?

Hér er ég að skoða eitt eftirlíkingarsett til að gera það aðeins auðveldara. Það eru grunnskólar og framhaldsskólar. Secondary gerir afritun og er ekki alltaf alveg samstillt við Primary.

Innsetning á sér stað í „Primery“ með ákveðnu tímagildi. Þessi innskot eykur innri fjöldann um 11, ef þetta er hámarkið. Eða það mun athuga klukkugildin og samstilla við klukkuna ef klukkugildin eru hærri. Þetta gerir þér kleift að skipuleggja eftir tíma.

Eftir að hann gerir upptökuna gerist mikilvægt augnablik. Klukkan er í „MongoDB“ og er aðeins hækkuð ef skrifað er í „Oplog“. Þetta er atburðurinn sem breytir stöðu kerfisins. Í nákvæmlega öllum klassískum greinum er atburður talinn vera þegar skilaboð lenda á hnút: skilaboðin hafa borist, sem þýðir að kerfið hefur breytt stöðu sinni.

Þetta er vegna þess að við rannsóknir er ekki alveg ljóst hvernig þessi skilaboð verða túlkuð. Við vitum fyrir víst að ef það endurspeglast ekki í „Oplog“ þá verður það ekki túlkað á nokkurn hátt og breyting á stöðu kerfisins er aðeins færsla í „Oplog“. Þetta einfaldar allt fyrir okkur: líkanið einfaldar það og gerir okkur kleift að skipuleggja það innan eins eftirmyndarsetts og margt annað gagnlegt.

Gildinu sem þegar er skrifað í "Oplog" er skilað - við vitum að "Oplog" inniheldur nú þegar þetta gildi og tími þess er 12. Nú, segjum, byrjar lestur frá öðrum hnút (Secondary), og það sendir afterClusterTime í Skilaboðið. Hann segir: "Ég þarf allt sem gerðist að minnsta kosti eftir 12 eða á tólf" (sjá mynd að ofan).

Þetta er það sem kallast Causal a consistent (CAT). Það er þannig hugtak í orði að þetta er einhver sneið af tíma, sem er í sjálfu sér samræmi. Í þessu tilviki getum við sagt að þetta sé ástand kerfisins sem sást á tíma 12.

Nú er ekkert hér ennþá, því þetta líkir eftir aðstæðum þegar þú þarft Secondary til að endurtaka gögn frá Primary. Hann bíður ... Og nú eru gögnin komin - hann skilar þessum gildum til baka.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Það er nokkurn veginn hvernig þetta virkar allt. Næstum.

Hvað þýðir "næstum"? Gefum okkur að það sé einhver aðili sem hefur lesið og skilið hvernig þetta allt virkar. Ég áttaði mig á því að í hvert sinn sem ClusterTime kemur upp, uppfærir það innri rökréttu klukkuna og þá hækkar næsta færsla um eina. Þessi aðgerð tekur 20 línur. Segjum að þessi manneskja sendi stærstu 64 bita töluna, mínus einn.

Af hverju "mínus einn"? Vegna þess að innri klukkunni verður skipt út í þetta gildi (augljóslega, þetta er stærsti mögulegi og hærri en núverandi tími), þá mun færsla eiga sér stað í „Oplog“ og klukkunni verður aukið um aðra einingu - og það mun þegar vera hámarksgildi (það eru einfaldlega allar einingar, það er hvergi annars staðar að fara), unsaint ints).

Ljóst er að eftir þetta verður kerfið gjörsamlega óaðgengilegt fyrir neitt. Það er aðeins hægt að afferma og þrífa - mikil handavinna. Fullt framboð:

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Þar að auki, ef þetta er endurtekið einhvers staðar annars staðar, þá dettur allur þyrpingin einfaldlega niður. Algjörlega óviðunandi aðstæður sem allir geta skipulagt mjög fljótt og auðveldlega! Þess vegna töldum við þessa stund sem eina mikilvægustu. Hvernig á að koma í veg fyrir það?

Leið okkar er að undirrita clusterTime

Þannig er það sent í skilaboðunum (á undan bláa textanum). En við byrjuðum líka að búa til undirskrift (blár texti):

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Undirskriftin er búin til af lykli sem er geymdur inni í gagnagrunninum, innan öruggs jaðar; sjálft er búið til og uppfært (notendur sjá ekkert um það). Hash er búið til og hvert skeyti er undirritað þegar þau eru búin til og staðfest þegar þau eru móttekin.
Spurningin vaknar líklega í huga fólks: "Hversu hægir þetta á hlutunum?" Ég sagði þér að það ætti að virka hratt, sérstaklega ef þessi eiginleiki er ekki til.

Hvað þýðir það að nota orsakasamkvæmni í þessu tilviki? Þetta er til að sýna afterClusterTime færibreytuna. Án þessa mun það einfaldlega standast gildi samt. Slúður, frá útgáfu 3.6, virkar alltaf.

Ef við sleppum stöðugri undirskriftamyndun mun það hægja á kerfinu jafnvel ef ekki er til staðar eiginleiki sem uppfyllir ekki aðferðir okkar og kröfur. Svo hvað gerðum við?

Gerðu það fljótt!

Þetta er frekar einfalt hlutur, en bragðið er áhugavert - ég mun deila því, kannski hefur einhver áhuga.
Við erum með kjötkássa sem geymir undirrituð gögn. Öll gögn fara í gegnum skyndiminni. Skyndiminnið táknar ekki tiltekinn tíma, heldur Range. Þegar eitthvað gildi kemur, búum við til Range, hyljum síðustu 16 bitana og við skrifum undir þetta gildi:

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Með því að fá slíka undirskrift flýtum við kerfinu (tiltölulega) 65 þúsund sinnum. Það virkar frábærlega: þegar við gerðum tilraunir minnkaði tíminn í raun um 10 þúsund sinnum þegar við fengum uppfærslu í röð. Það er ljóst að þegar þeir eru á skjön, þá gengur þetta ekki. En í flestum hagnýtum tilfellum virkar það. Samsetning Range undirskriftarinnar ásamt undirskriftinni leysti öryggisvandamálið.

Hvað höfum við lært?

Lærdómur sem við lærðum af þessu:

  • Við þurfum að lesa efni, sögur, greinar, því við höfum margt áhugavert. Þegar við vinnum að einhverjum eiginleikum (sérstaklega núna, þegar við gerðum viðskipti o.s.frv.), þurfum við að lesa og skilja. Það tekur tíma, en það er í raun mjög gagnlegt því það gerir það ljóst hvar við erum. Við virtumst ekki hafa komið með neitt nýtt - við tókum bara hráefnið.

    Almennt séð er ákveðinn munur á hugsun þegar það er fræðileg ráðstefna (Sigmon t.d.) - allir einblína á nýjar hugmyndir. Hvað er nýtt við reikniritið okkar? Hér er ekkert sérstaklega nýtt. Nýjungin felst frekar í því hvernig við blanduðum núverandi nálgunum saman. Þess vegna er það fyrsta að lesa klassíkina, byrja á Lampart.

  • Í framleiðslu eru kröfurnar allt aðrar. Ég er viss um að mörg ykkar standa ekki frammi fyrir „kúlulaga“ gagnagrunnum í óhlutbundnu tómarúmi, heldur venjulegum, raunverulegum hlutum sem eiga í vandræðum með aðgengi, leynd og bilanaþol.
  • Það síðasta er að við þurftum að skoða mismunandi hugmyndir og sameina nokkrar gjörólíkar greinar í eina nálgun, saman. Hugmyndin um að undirrita, til dæmis, kom almennt frá grein sem taldi Paxos siðareglur, sem fyrir non-Byzantine Failors er innan heimildar siðareglur, fyrir Byzantine - utan heimildar siðareglur... Almennt séð er þetta nákvæmlega það sem við endaði með því að gera.

    Það er nákvæmlega ekkert nýtt hér! En um leið og við blönduðum þessu öllu saman... Það er það sama og að segja að Olivier salatuppskriftin sé bull, því egg, majónes og gúrkur hafa þegar verið fundin upp... Þetta er um sömu sögu.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Ég ætla að klára þetta. Þakka þér fyrir!

spurningar

Spurning úr sal (hér eftir nefnd B): – Þakka þér, Mikhail, fyrir skýrsluna! Umræðuefnið um tíma er áhugavert. Þú ert að nota Gossiping. Þeir sögðu að hver og einn hefði sinn tíma, allir þekktu sinn staðbundna tíma. Eins og ég skil það þá erum við með bílstjóri - það geta verið margir viðskiptavinir með rekla, fyrirspurnaskipuleggjendur líka, brot líka... Og hvað kemur kerfið niður á ef allt í einu kemur upp misræmi: einhver ákveður að það sé fyrir a. mínútu á undan, einhver mínútu á eftir? Hvar munum við enda?

MT: — Mjög góð spurning! Mig langaði bara að tala um brot. Ef ég skil spurninguna rétt erum við með eftirfarandi aðstæður: það er brot 1 og brot 2, lestur á sér stað úr þessum tveimur brotum - þeir hafa misræmi, þeir hafa ekki samskipti sín á milli, vegna þess að tíminn sem þeir vita er mismunandi, sérstaklega tíminn sem þeir eru til í oplogum.
Segjum að shard 1 hafi gert milljón færslur, shard 2 hafi alls ekki gert neitt og beiðnin hafi verið tvö. Og sá fyrsti er með eftirþyrpingatíma sem er yfir milljón. Í slíkum aðstæðum, eins og ég útskýrði, mun shard 2 aldrei bregðast við.

IN: – Mig langaði að vita hvernig þeir samstilla og velja einn rökréttan tíma?

MT: - Mjög auðvelt að samstilla. Shard, þegar afterClusterTime kemur til hans og hann finnur ekki tíma í "Oplog", byrjar ekkert samþykkt. Það er að segja, hann hækkar tíma sinn með höndunum í þetta gildi. Þetta þýðir að það hefur enga atburði sem passa við þessa beiðni. Hann skapar þennan atburð með tilbúnum hætti og verður þannig Causal Consistent.

IN: – Hvað ef eftir þetta koma einhverjir aðrir atburðir til hans sem týndust einhvers staðar í netinu?

MT: – Shard er hannað á þann hátt að þeir koma ekki aftur, þar sem það er einn meistari. Ef hann er búinn að skrá sig þá koma þeir ekki heldur koma seinna. Það getur ekki gerst að eitthvað festist einhvers staðar, þá skrifar hann ekkert og þá koma þessir atburðir - og orsakasamkvæmni er rofin. Þegar hann skrifar ekkert, ættu þeir allir að koma næst (hann mun bíða eftir þeim).

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

IN: – Ég hef nokkrar spurningar varðandi biðraðir. Orsakasamkvæmni gerir ráð fyrir að það sé ákveðin biðröð aðgerða sem þarf að framkvæma. Hvað gerist ef einn pakkinn okkar hverfur? Hér kemur 10., 11.... sá 12. er horfinn og allir hinir bíða eftir að hann rætist. Og skyndilega dó bíllinn okkar, við getum ekki gert neitt. Er hámarkslengd á biðröðinni sem safnast upp áður en hún er keyrð? Hvaða banvæna bilun á sér stað þegar eitthvert ríki er glatað? Þar að auki, ef við skrifum niður að það sé eitthvert fyrra ástand, ættum við einhvern veginn að byrja á því? En þeir ýttu honum ekki í burtu!

MT: - Einnig frábær spurning! Hvað erum við að gera? MongoDB hefur hugtakið sveitarskrif, sveit les. Í hvaða tilvikum geta skilaboð tapast? Þegar rit er ekki ályktunarhæft eða þegar lesið er ekki ályktunarhæft (einhvers konar sorp getur líka fest sig).
Varðandi orsakasamkvæmni var gerð stór tilraunapróf sem niðurstaðan var sú að þegar skrif og lestur eru ekki ályktunarhæfur, eiga sér stað brot á orsakasamræmi. Nákvæmlega það sem þú segir!

Ráð okkar: notaðu að minnsta kosti sveitarlestur þegar þú notar orsakasamkvæmni. Í þessu tilviki mun ekkert glatast, jafnvel þótt sveitarskráin glatist... Þetta er hornrétt ástand: ef notandinn vill ekki að gögn tapist þarf hann að nota sveitarskrá. Orsakasamkvæmni tryggir ekki endingu. Ending er tryggð með afritun og vélum sem tengjast afritun.

IN: – Þegar við búum til tilvik sem framkvæmir klippingu fyrir okkur (ekki meistara, heldur þræl), treystir það á Unix tíma eigin vélar eða tíma „meistarans“; Samstillingar það í fyrsta skipti eða reglulega?

MT: — Ég skal skýra það núna. Shard (þ.e. lárétt skipting) – það er alltaf Primary þar. Og brot getur haft „meistara“ og það geta verið eftirlíkingar. En brotið styður alltaf upptöku, því það verður að styðja eitthvað lén (brotið hefur Primary).

IN: – Þannig að allt veltur eingöngu á „meistaranum“? Er meistaratími alltaf notaður?

MT: - Já. Þú getur sagt í óeiginlegri merkingu: klukkan tifkar þegar farið er inn í „meistara“, í „Oplog“.

IN: - Við erum með viðskiptavin sem tengist og þarf ekki að vita neitt um tímann?

MT: — Þú þarft alls ekki að vita neitt! Ef við tölum um hvernig það virkar á viðskiptavininn: þegar viðskiptavinurinn vill nota orsakasamkvæmni þarf hann að opna lotu. Nú er allt til staðar: viðskipti í lotunni, og sækja réttindi... Fundur er röðun á rökréttum atburðum sem eiga sér stað hjá viðskiptavininum.

Ef hann opnar þessa lotu og segir þar að hann vilji orsakasamkvæmni (ef lotan styður sjálfgefið orsakasamkvæmni) virkar allt sjálfkrafa. Ökumaðurinn man eftir þessum tíma og eykur hann þegar hann fær ný skilaboð. Það man hvaða svar það fyrra skilaði frá þjóninum sem skilaði gögnunum. Næsta beiðni mun innihalda afterCluster ("tími lengri en þetta").

Viðskiptavinurinn þarf ekki að vita nákvæmlega neitt! Þetta er algjörlega ógegnsætt fyrir honum. Ef fólk notar þessa eiginleika, hvað getur það gert? Í fyrsta lagi geturðu lesið aukaefni á öruggan hátt: þú getur skrifað í grunnskóla og lesið úr landfræðilega endurteknum framhaldsþáttum og verið viss um að það virki. Á sama tíma er jafnvel hægt að flytja lotur sem voru teknar upp á Primary yfir á Secondary, þ.e.a.s. þú getur ekki notað eina lotu, heldur nokkrar.

IN: – Nýtt lag af tölvufræði – CRDT (Conflict-free Replicated Data Types) gagnategundir – er mjög tengt efninu Endanleg samkvæmni. Hefurðu íhugað að samþætta þessar tegundir gagna í gagnagrunninn og hvað getur þú sagt um það?

MT: - Góð spurning! CRDT er skynsamlegt fyrir skrifátök: í MongoDB, einn meistari.

IN: — Ég er með spurningu frá devops. Í hinum raunverulega heimi eru slíkar Jesuitískar aðstæður þegar Býsanska bilunin á sér stað og illt fólk innan verndar jaðarsins byrjar að pota inn í siðareglur, senda handverkspakka á sérstakan hátt?

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

MT: – Illt fólk innan jaðarsins er eins og trójuhestur! Illt fólk innan jaðarsins getur gert margt slæmt.

IN: – Það er ljóst að skilja eftir, í grófum dráttum, gat á netþjóninum þar sem hægt er að troða dýragarði af fílum í gegnum og fella allan þyrpinguna að eilífu... Það mun taka tíma fyrir handvirkan bata... Þetta, vægast sagt, er rangt. Á hinn bóginn er þetta áhugavert: í raunveruleikanum, í reynd, eru aðstæður þar sem náttúrulega svipaðar innri árásir eiga sér stað?

MT: - Þar sem ég lendi sjaldan í öryggisbrestum í raunveruleikanum get ég ekki sagt til um hvort þau gerast. En ef við tölum um þróunarheimspeki þá hugsum við svona: við erum með jaðar sem veitir strákunum sem sinna öryggi - þetta er kastali, veggur; og inni í jaðrinum geturðu gert hvað sem þú vilt. Það er ljóst að það eru notendur sem geta aðeins skoðað og það eru notendur sem geta eytt möppunni.

Það fer eftir réttindum, skaðinn sem notendur geta valdið getur verið mús, eða það getur verið fíll. Það er ljóst að notandi með full réttindi getur gert hvað sem er. Notandi með takmörkuð réttindi getur valdið mun minni skaða. Einkum getur það ekki brotið kerfið.

IN: – Í vernduðu jaðrinum reyndi einhver að búa til óvæntar samskiptareglur fyrir þjóninn til að eyðileggja þjóninn algjörlega, og ef þú ert heppinn, allur þyrpingin... Verður það einhvern tíma svona „gott“?

MT: „Ég hef aldrei heyrt um slíkt“. Sú staðreynd að þú getur hrundið netþjóni á þennan hátt er ekkert leyndarmál. Mistök inni, vera frá samskiptareglunum, vera viðurkenndur notandi sem getur skrifað eitthvað svona í skilaboðin... Reyndar er það ómögulegt, því það verður samt staðfest. Það er hægt að slökkva á þessari auðkenningu fyrir notendur sem vilja hana ekki - þá er það þeirra vandamál; þeir, í grófum dráttum, eyðilögðu sjálfa veggina og þú getur ýtt þar inn fíl sem mun troða... En almennt séð geturðu klætt þig upp sem viðgerðarmann, komið og dregið hann út!

IN: — Þakka þér fyrir skýrsluna. Sergey (Yandex). Það er fasti í Mong sem takmarkar fjölda atkvæðisbærra meðlima í eftirmyndasettinu og þessi fasti er 7 (sjö). Af hverju er þetta fasti? Af hverju er þetta ekki einhvers konar breytu?

MT: - Við erum með eftirlíkingarsett með 40 hnútum. Það er alltaf meirihluti. Ég veit ekki hvaða útgáfu...

IN: – Í Replica Set geturðu keyrt meðlimi án atkvæðisréttar, en það eru að hámarki 7 atkvæðisbærir meðlimir. Hvernig getum við lifað lokunina af í þessu tilfelli ef eftirlíkingarsettið okkar er dreift á 3 gagnaver? Ein gagnaver getur auðveldlega slökkt og önnur vél getur dottið út.

MT: – Þetta er nú þegar aðeins fyrir utan gildissvið skýrslunnar. Þetta er almenn spurning. Kannski get ég sagt þér frá því seinna.

HighLoad++, Mikhail Tyulenev (MongoDB): Orsakasamkvæmni: frá kenningu til framkvæmda

Nokkrar auglýsingar 🙂

Þakka þér fyrir að vera hjá okkur. Líkar þér við greinarnar okkar? Viltu sjá meira áhugavert efni? Styðjið okkur með því að leggja inn pöntun eða mæla með því við vini, cloud VPS fyrir forritara frá $4.99, einstök hliðstæða upphafsþjóna, sem var fundið upp af okkur fyrir þig: Allur sannleikurinn um VPS (KVM) E5-2697 v3 (6 kjarna) 10GB DDR4 480GB SSD 1Gbps frá $19 eða hvernig á að deila netþjóni? (fáanlegt með RAID1 og RAID10, allt að 24 kjarna og allt að 40GB DDR4).

Dell R730xd 2x ódýrari í Equinix Tier IV gagnaveri í Amsterdam? Aðeins hér 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 sjónvarp frá $199 í Hollandi! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - frá $99! Lestu um Hvernig á að byggja upp infrastructure Corp. flokki með notkun Dell R730xd E5-2650 v4 netþjóna að verðmæti 9000 evrur fyrir eyri?

Heimild: www.habr.com

Bæta við athugasemd