Следващата конференция HighLoad++ ще се проведе на 6 и 7 април 2020 година в Санкт Петербург.
Подробности и билети . HighLoad++ Сибир 2019. Зала «Красноярск». 25 юни, 12:00. Тезиси и .

Понякога практическите изисквания конфликтуват с теорията, в която не са отчетени важни аспекти за търговския продукт. В този доклад е представен процесът на избор и комбиниране на различни подходи за създаване на компоненти на причинната съгласуваност, основани на академични изследвания, в зависимост от изискванията на търговския продукт. Слушателите ще научат за съществуващите теоретични подходи към логични часовници, проследяване на зависимости, системна сигурност, синхронизация на часовници и защо MongoDB се е спряла на определени решения.
Михаил Тюленев (по-нататък – МТ): – Ще говоря за причинна съгласуваност – това е функция, над която работихме в MongoDB. Работя в група за разпределени системи, създадохме я преди около две години.

В процеса се наложи да се запозная с голямо количество академични изследвания, защото тази функция е достатъчно добре проучена. Оказа се, че нито една статия не отговаря на изискванията в продукцията, база от данни имайки предвид много специфични изисквания, които съществуват вероятно във всяко приложение за продукция.
Ще говоря за това как ние, като потребители на академични изследвания, подготвяме нещо такова, което можем да предложим на нашите потребители под формата на готово ястие, което е удобно и безопасно за ползване.
Причинна съгласуваност (Causal consistency). Да уточним понятията
Първо искам да кажа в общи черти какво е причинна съгласуваност. Има двама персонажа – Леонард и Пенни (сериала «Теория на големия взрив»):

Да предположим, че Пенни е в Европа, а Леонард иска да й направи някакъв изненада, парти. И той не може да измисли нищо по-добро от това да я изтрие от списъка с приятели, да изпрати на всички приятели ъпдейт на feed: «Нека зарадваме Пенни!» (тя е в Европа, докато спи, не вижда всичко това и не може да го види, защото не е там). В крайния момент той изтрива този пост, премахва от «Фида» и възстановява достъпа, за да не забележи нищо и да няма скандал.
Всичко е прекрасно, но нека предположим, че системата е разпределена и нещата не преминават точно така. Може например да се случи, че ограничението за достъп до Пенни е възникнало след появата на този пост, ако събитията не са причинно свързани помежду си. Всъщност, това е пример за необходимостта от Causal consistency, за да се изпълни бизнес функция (в този случай).
Всъщност, това са доста нетривиални характеристики на базите данни – много малко от тях ги поддържат. Нека преминем към моделите.
Модели на консистентност (Consistency Models)
Какво е модел на консистентност в базите данни? Това са определени гаранции, които разпределената система предоставя относно това какви данни и в каква последователност клиентът може да получи.
Всъщност всички модели на консистентност се свеждат до това, колко разпределената система прилича на система, която работи, например, на един възел на лаптоп. И на колко система, която работи на хиляди геораспределени 'Нодов', прилича на лаптоп, в който всички тези свойства се изпълняват практически автоматично.
Затова моделите на консистентност се прилагат само за разпределени системи. Всички системи, които преди това съществуваха и работеха на принципа на вертикалното мащабируемост, не срещаха такива проблеми. Имаше един Buffer Cache, и от него всичко винаги се четеше.
Модел Strong
По същество, първият модел е Strong (или rise ability, както често се нарича). Това е модел на консистентност, който гарантира, че всяко изменение, веднага щом получи потвърждение, че е извършено, става видимо за всички потребители на системата.
Това създава глобален ред на всички събития в базата данни. Това е много силно свойство на консистентността и е изключително скъпо. Въпреки това, то се поддържа много добре. Просто е много скъпо и бавно – рядко се използва. Това се нарича rise ability.
Има още едно, по-силно свойство, което се поддържа в 'Спаннер' – нарича се External Consistency. Ще говорим за него малко по-късно.
Causal
Следващото е Causal, точно за това говорих. Между Strong и Causal има още няколко подуровня, за които няма да говоря, но те всички се свеждат до Causal. Това е важен модел, защото е най-силният от всички модели, с най-силна консистентност при наличие на мрежа или дялове.
Causals е всъщност ситуация, при която събитията са свързани причинно-следствено. Често ги възприемат като Read your on rights от гледна точка на клиента. Ако клиентът е наблюдавал определени стойности, той не може да види стойностите от миналото. Вече започва да вижда префиксни четения. Всичко това се свежда до едно и също.
Causals като модел на консистентност – частично подреждане на събития на сървъра, при което събитията от всички клиенти се наблюдават в една и съща последователност. В този случай – Леонард и Пенни.
Eventual
Третият модел е Eventual Consistency. Това е модел, който поддържат абсолютно всички разпределени системи, минимален модел, който изобщо има смисъл. Той означава следното: когато правим някои промени в данните, те в даден момент стават консистентни.
В такъв момент не казва нищо, иначе щеше да се превърне в External Consistency – щеше да бъде съвсем друга история. Въпреки това, това е много популярна и най-разпространена модел. По подразбиране всички потребители на разпределени системи използват именно Eventual Consistency.
Искам да дам някои сравнителни примери:

Какво означават тези стрелки?
- Забавяне. С увеличаване на силата на консистентността, тя става по-голяма по разбираеми причини: трябва да се направят повече записи, да се получи потвърждение от всички хостове и възли, които участват в клъстера, че данните вече са там. Следователно в Eventual Consistency най-бързият отговор, защото там обикновено може дори да се запише в паметта и това би било принципно достатъчно.
- Достъпност. Ако го разбираме като възможността на системата да отговаря при наличие на прекъсвания на мрежата, дялове или някакви повреди, отказоустойчивостта нараства при намаляване на модела на консистентност, тъй като ни стига, ако един хост работи и предоставя някакви данни. Eventual Consistency изобщо не гарантира нищо относно данните – това може да бъде всичко.
- Аномалии. При това, разбира се, се увеличава броят на аномалиите. При Strong Consistency те изобщо не трябва да съществуват, докато при Eventual Consistency могат да бъдат каквито си искате. Въпросът е: защо хората избират Eventual Consistency, ако тя съдържа аномалии? Отговорът е, че моделите на Eventual Consistency са приложими, а аномалиите съществуват, например, за кратък период от време; съществува възможност да се използва мастер за четене и да се четат относително последователни данни; често има възможност да се използват силни модели на последователност. Практически това работи и често броят на аномалиите е ограничен по време.
Теорема CAP
Когато видите думите consistency, availability – какво ви идва на ум? Правилно – теоремата CAP! Сега искам да развея мита… Това не съм аз – има Мартин Клеппман, който е написал прекрасна статия, чудесна книга.

Теоремата CAP е принцип, формулиран в началото на 2000-те години, който твърди, че Consistency, Availability, Partitions: вземете две, и не можете да изберете три. Това е бил един принцип. Той бе доказан като теорема няколко години по-късно, направиха го Джилберт и Линч. След това тя започна да се използва като мантра – системите бяха разделени на CA, CP, AP и така нататък.
Тази теорема наистина бе доказана за следните случаи… Първо, Availability не се разглеждаше като непрекъсната стойност от нула до сто (0 – системата е „мъртва“, 100 – отговаря бързо; така я разглеждахме), а като свойство на алгоритъм, което гарантира, че при всяко негово изпълнение той връща данни.
За времето за отговор там изобщо не става дума! Има алгоритъм, който връща данни след 100 години – напълно страхотен available-алгоритъм, който е част от теоремата CAP.
Второ: теоремата бе доказана за изменения в стойностите на един и същи ключ, докато тези изменения са линия resizable. Това означава, че те всъщност почти не се използват, защото други модели Eventual Consistency, Strong Consistency (може би) са налични.
Какво означава всичко това? Че теоремата CAP в онази форма, в която е доказана, практически не е приложима и рядко се използва. В теоретична форма тя по някакъв начин всичко ограничава. Получава се един принцип, който интуитивно е верен, но не е доказан.
Causal consistency – най-силният модел
Това, което се случва в момента – можете да получите всичките три неща: Консистентност, Наличност, които можете да постигнете чрез Партиции. В частност, Козуална консистентност – най-силният модел на консистентност, който при наличие на Партиции (разриви в мрежата) все пак работи. Затова тя представлява толкова голям интерес и затова се занимаваме с нея.

Първо, тя опростява работата на разработчиците на приложения. В частност, наличието на голяма поддръжка от страна на сървъра: когато всичките записи, които се случват вътре при един клиент, гарантирано ще пристигнат в същата последователност на друг клиент. На второ място, тя издържа на партиции.
Вътрешната кухня на MongoDB
Помняйки, че имаме обяд, се преместваме в кухнята. Ще ви разкажа за модела на системата, а именно – какво е MongoDB за тези, които за първи път чуват за такава база данни.


MongoDB (по-нататък – «МонгоБД») е разпределена система, която поддържа хоризонтално мащабиране, тоест шардинг; и вътре в всеки шард тя също така поддържа излишък от данни, тоест репликация.
Шардингът в «МонгоБД» (не релационна БД) изпълнява автоматично балансиране, тоест всяка колекция от документи (или «таблица» в термините на релационните данни) се разделя на парчета, и след това сървърът автоматично ги премества между шардовете.
Query Router, който разпределя запросите, за клиента е някакъв клиент, през който той работи. Той вече знае къде и какви данни се намират и насочва всичките запроси към правилния шард.
Още един важен момент: MongoDB е с един главен сървър. Има един Primary – той може да приема записи, поддържащи тези ключове, които той притежава. Невъзможно е да се направи Multi-master запис.
Пуснахме версия 4.2 – там се появиха нови интересни неща. В частност, вкарахме Lucene – търсене – именно изпълним Java право в «Монго», и там стана възможно да се извършва търсене чрез Lucene, подобно на «Еластик».
И направихме нов продукт – Charts, той също е наличен на «Атласа» (собствен Cloud на «Монго»). Те имат Free Tier – можете да поиграете с това. Charts ми хареса много – визуализация на данни, много интуитивна.
Ингредиенти на Козуалната консистентност
Броих около 230 статии, които са публикувани по тази тема – от Лесли Ламперта. Сега от паметта си ще ви предам някои части от тези материали.

Всичко започна с статията на Лесли Ламперта, написана през 70-те години на миналия век. Както можете да видите, все още продължават да се провеждат изследвания в тази област. В момента Causal consistency разполага с интерес, свързан с развитието на разпределените системи.
Ограничения
Какви ограничения съществуват? Това е наистина един от основните моменти, тъй като ограниченията, наложени от производствените системи, значително се различават от тези, които съществуват в академичните статии. Често те са доста изкуствени.

- На първо място, "МоноГDB" е single master, както вече казах (това значително опростява).
- Смятаме, че системата трябва да поддържа около 10 хиляди шардове. Не можем да приемем архитектурни решения, които явно ще ограничат това значение.
- Имаме облак, но считаме, че потребителят трябва да има възможност, когато изтегля binary, да го стартира на лаптопа си и всичко да работи отлично.
- Считаме, че в Research рядко се използва: външните клиенти могат да правят каквото си поискат. "МоноГDB" е опен сорс. Съответно, клиентите могат да бъдат умни, злонамерени и да искат да разрушат всичко. Предполагаме, че могат да възникнат византийски Фейлори.
- За външни клиенти, които са извън периметъра – важно ограничение: ако тази функция е изключена, не трябва да се наблюдават никакви снижавания на производителността.
- Още един момент – изцяло антиакадемичен: съвместимост на предишни и бъдещи версии. Стари драйвери трябва да поддържат нови актуализации, а базата данни трябва да поддържа стари драйвери.
Общо взето, всичко това налага ограничения.
Компоненти на Causal consistency
Сега ще говоря за някои компоненти. Ако разгледаме Causal consistency общо взето, можем да отделим блокове. Избирахме от работи, които се отнасят към определен блок: Следене на зависимости, избор на часовници, как тези часовници могат да се синхронизират помежду си и как осигуряваме безопасност – това е приблизителен план за темите, които ще обсъждам:

Пълно проследяване на зависимости (Full Dependency Tracking)
Защо е необходимо? За да може, когато данните се репликират – всяко записване, всяка промяна на данните да съдържа информация за това, от какви промени зависи. Най-първата и наивна промяна – е, че всяко съобщение, което съдържа запис, включва информация за предходните съобщения:

В този пример номерът в фигурните скобки – са номерата на записите. Понякога тези записи със стойности се предават дори изцяло, понякога се предават някакви версии. Същността е, че всяка промяна съдържа информация за предходното (явно носи всичко това в себе си).
Защо решихме да не използваме такъв подход (пълен мониторинг)? Очевидно, защото този подход не е практичен: всяка промяна в социалната мрежа зависи от всички предходни промени в тази социална мрежа, предавайки, да речем, "Фейсбук" или "Вконтакте" във всяко обновление. Все пак има много изследвания именно по Full Dependency Tracking – това са предсоциални мрежи, за някои ситуации това наистина работи.
Явно проследяване на зависимости (Explicit Dependency Tracking)
Следващият – по-ограничен. Тук също се разглежда предаването на информация, но само тази, която явно зависи. Какво от какво зависи, обикновено определя вече приложението. Когато данните се репликират, при заявка се предоставят само отговори, когато предходните зависимости са задоволени, т.е. показани. В това е същността на това как работи Causal consistency.

Тя вижда, че запис 5 зависи от записи 1, 2, 3, 4 – съответно, тя изчаква, преди клиентът да получи достъп до измененията, направени с разрешението на Пенни, когато всички предходни изменения вече са преминали в базата данни.
Това също не ни устройва, защото информацията е прекалено много и това ще забави процеса. Има и друг подход…
Часовник на Лампорт (Lamport Clock)
Те са много стари. Lamport Clock предполага, че тези зависимости се свиват в скаларна функция, която се нарича Lamport Clock.
Скалярната функция е някакво абстрактно число. Често я наричат логическо време. При всяко събитие този counter се увеличава. Counter, който в момента е известен на процеса, изпраща всяко съобщение. Ясно е, че процесите могат да бъдат рассинхронизирани, те могат да имат напълно различно време. Въпреки това, с такъв обмен на съобщения, системата по някакъв начин балансира часовниците. Какво се случва в този случай?
Разбих този голям шард на две части, за да бъде ясно: Friends могат да живеят в една нода, която съдържа част от колекцията, а Feed – изцяло в друга нода, в която се съдържа част от тази колекция. Ясно е как могат да не попаднат в ред? Първо Feed ще каже: „Реплицирано“, а след това – Friends. Ако системата не предоставя никакви гаранции, че Feed няма да бъде показан, преди зависимостите на Friends в колекцията Friends да бъдат доставени, тогава точно това ще доведе до ситуацията, за която споменах.
Виждате как логически времето на counter на Feed’а се увеличава:

Така основното свойство на този Lamport Clock и Causal consistency (обяснено чрез Lamport Clock) е следното: ако имаме събития A и B, и събитие B зависи от събитие A *, тогава следва, че LogicalTime на събитие A е по-малко от LogicalTime на събитие B.
* Понякога се казва, че A happened before B, т.е. A се е случило преди B – това е определено отношение, което частично подрежда цялото множество събития, които изобщо са се случили.
Обратната посока не е вярна. Всъщност, това е един от основните недостатъци на Lamport Clock – частичният ред. Там има понятие за съвременни събития, т.е. събития, при които нито (A happened before B), нито (A happened before B). Пример може да бъде паралелното добавяне на Леонард в приятели на някой друг (дори не от Леонард, а например от Шелдън).
Това е свойството, което често се използва при работа с Lamport часовници: точно се гледа на функцията и от това се прави заключение – може би тези събития са зависими. Защото в едната посока това е вярно: ако LogicalTime A е по-малко от LogicalTime B, то B не може да е happened before A; а ако е по-голямо, то може да бъде.
Векторни часовници (Vector Clock)
Логичното развитие на часовника на Лампорта е Векторните часовници. Те се различават по това, че всеки нод, който тук съществува, съдържа свои, отделни часовници, и те се предават като вектор.
В този случай виждате, че нулевият индекс на вектора отговаря за Feed, а първият индекс на вектора – за Friends (всеки от тези ноди). И те сега ще се увеличават: нулевият индекс на "Фида" се увеличава при запис – 1, 2, 3:

Какво прави Векторните часовници по-добри? Те дават възможност да се разбере кои събития се случват едновременно и кога те се случват на различни ноди. Това е много важно за системата за шардирование, като "MongoDB". Въпреки това, ние не го избрахме, макар че е страхотно нещо, работи чудесно и вероятно би ни подхождало...
Ако имаме 10 хиляди шардове, не можем да предаваме 10 хиляди компонента, дори ако ги компресираме или измисляме нещо друго – все пак полезният товар ще бъде многократно по-малък от обема на целия този вектор. Затова, с мъка на сърцето и зъбите, се отказахме от този подход и преминахме към друг.
Spanner TrueTime. Атомни часове
Казах, че ще стане дума за "Спаннер". Това е страхотно нещо, направо XXI век: атомни часове, GPS-синхронизация.
Каква е идеята? "Спаннер" е система на Google, която наскоро стана достъпна за хората (те добавиха SQL към нея). Всяка транзакция там има определен времеви маркер. Понеже времето е синхронизирано*, на всяко събитие може да се назначи определен времеви маркер – атомните часове имат време на очакване, след което гарантирано "процесира" друго време.

Така, просто записвайки в базата данни и очаквайки някакъв период от време, автоматично се гарантира сериализируемост на събитието. Те имат най-силната модел на консистентност, който по принцип може да се представи – това е външна консистентност.
* Това е основният проблем на часовниците на Лемпарт – те никога не са синхронизирани в разпределени системи. Те могат да се разминават, дори при наличието на NTP, все още не работят много добре. "Спаннер" разполага с атомни часовници и синхронизация, изглежда, до микросекунди.
Защо не го избрахме? Не предполагахме, че нашите потребители разполагат с вградени атомни часовници. Когато те станат налични, вградени в всеки лаптоп, ще има суперстрашна GPS-синхронизация – тогава да… А в момента най-доброто, което е възможно, е "Амазон", базови станции – за фанатиците… Затова използвахме други часовници.
Хибридни часове (Hybrid Clock)
Това всъщност е това, което тикта в «MongoDB» при осигуряване на каузална консистентност. Хибридни са в какво? Хибрид е скаларна стойност, но се състои от два компонента:

- Първото е Unix епохата (колко секунди са изминали от «началото на компютърния свят»).
- Второто е някакъв инкремент, също 32-битен unsigned int.
Това всъщност е всичко. Има такъв подход: частта, която отговаря за времето, постоянно се синхронизира с часовниците; всеки път, когато се извършва актуализация, тази част се синхронизира с часовниците и резултатът е, че времето винаги е повече или по-малко правилно, а инкрементът позволява различаване на събития, които са се случили в един и същ момент на времето.
Защо това е важно за «MongoDB»? Защото позволява да се правят резервни възстановявания на определен момент от времето, тоест събитието се индексира по време. Това е важно, когато са необходими определени събития; за БД събитията са изменения в БД, които са се случили в определени периоди от време.
Най-важната причина ще кажа само на вас (моля, само на никого не казвайте)! Направихме го по този начин, защото така изглеждат подредените, индексирани данни в MongoDB OpLog. OpLog е структура от данни, която съдържа абсолютно всички изменения в базата: те първо попада в OpLog, а после вече се прилагат на същото хранилище в случай, че това е реплицирана дата или шард.
Това беше основната причина. Все пак съществуват и практическите изисквания за разработване на базата, а това означава, че трябва да бъде просто – малко код, колкото се може по-малко счупени неща, които трябва да се пренапишат и тестват. Това, че нашите oplogs се оказаха индексирани с хибридни часовници, изключително помогна и позволи да се направи правилен избор. Това наистина се отплати и по някакъв начин вълшебно заработи, още на първия прототип. Беше наистина страхотно!
Синхронизиране на часовниците
Съществуват няколко начина за синхронизация, описани в научната литература. Говоря за синхронизация, когато имаме две различни шардове. Ако имаме реплика-сет – там няма нужда от синхронизация: това е „сингъл-мастър“; имаме OpLog, в който всички промени влизат – в този случай всичко вече е последователно подредено в самия „Оплог“. Но ако имаме два различни шарда, тук синхронизацията на времето е важна. Тук векторните часовници са от голяма помощ! Но ние ги нямаме.

Вторият подход е „Хартбити“ (Heartbeats). Можем да обменяме някои сигнали, които се появяват на всяка единица време. Но „Хартбити“ – твърде бавни са, не можем да осигурим необходимата латентност на нашия клиент.
Истинското време – разбира се, е прекрасно нещо. Но, отново, вероятно това е бъдещето… Въпреки че в „Атласа“ вече може да се направи, вече съществуват бързи „амазоновски“ синхронизатори на времето. Но това няма да бъде достъпно за всички.
Gossiping – това е, когато всички съобщения включват времето. Това е приблизително това, което използваме. Всяко съобщение между нодове, драйвера, рутера на дата-нодовете, абсолютно всичко за „МонгоДБ“ – това са елементи, компоненти на базата данни, които съдържат часовници, които течат. Навсякъде имат стойност на хибридно време, която се предава. 64 бита? Това е възможно.
Как всичко това работи заедно?
Тук разглеждам един реплика-сет, за да е малко по-лесно. Има Primary и Secondary. Secondary прави репликация и не винаги е напълно синхронизиран с Primary.
Става запис (insert) в „Праймери“ с някаква стойност на времето. Този insert увеличава вътрешния каунтер с 11, ако е максималният. Или ще провери стойностите на часовника и ще синхронизира по часовниците, ако стойностите на часовниците са по-високи. Това позволява времево подреждане.
След като извърши записа, настъпва важен момент. Часовниците в „МонгоДБ“ инкрементират само в случай на запис в „Оплог“. Това е събитието, което променя състоянието на системата. В абсолютно всички класически статии събитието се счита за попадение на съобщение в нода: съобщението е получено – значи, системата е променила своето състояние.
Това е свързано с факта, че при проучването не може напълно да се разбере как ще бъде интерпретирано това съобщение. Знаем със сигурност, че ако не е отразено в "Оплога", то никак няма да бъде интерпретирано, а единственото изменение на състоянието на системата е записът в "Оплог". Това ни опростява всичко: и моделът е опростен, и позволява да се води подредба в рамките на един реплика-сет, и много други полезни неща.
Връща се стойност, която вече е записана в "Оплог" – знаем, че в "Оплога" вече е налична тази стойност, а времето й е 12. Сега, да предположим, започва четене от друг нод (Secondary), и той предава вече afterClusterTime в самото съобщение. Той казва: "Трябва ми всичко, което се е случило поне след 12 или по време на дванадесет" (вж. рис. по-горе).
Това, което се нарича Causal a consistent (CAT). Има такова понятие в теорията, че това е някакъв времеви срез, който е последователен сам по себе си. В този случай можем да кажем, че това е състоянието на системата, което е наблюдавано в момента на време 12.
Сега тук все още няма нищо, защото това някак си имитира ситуацията, когато е необходимо Secondary да репликира данни с Primary. Той чака... И ето данните идват – връща назад тези стойности.

Така горе-долу всичко и работи. Почти.
Какво означава „почти“? Нека предположим, че има някой човек, който е прочел и е разбрал как всичко това работи. Разбрал е, че всеки път се случва ClusterTime, който обновява вътрешните логически часовници, а следващият запис увеличава с единица. Тази функция заема 20 реда. Нека кажем, че този човек предава най-голямото 64-битово число, минус единица.
Защо „минус единица“? Защото вътрешните часовници ще бъдат подставени в тази стойност (очевидно, това е най-голямото възможно и повече от текущото време), след това ще се състои запис в "Оплог", и часовниците ще се увеличат с още една единица – и вече ще бъде абсолютно максималната стойност (там просто са всичките единици, по-далеч няма къде, unsaint int’и).
Разбираемо е, че след това системата става абсолютно недостъпна за всичко. Може само да бъде извлечена, почистена – много ръчна работа. Пълен availability:

Впрочем, ако това се репликира и някъде другаде, целият клъстър просто спира. Абсолютно неприемлива ситуация, която всеки човек може да организира много бързо и лесно! Затова разглеждахме този момент като един от най-важните. Как да предотвратим това?
Нашият път е – подписване на clusterTime
Така то се предава в съобщението (преди синята част от текста). Но ние също започнахме да генерираме подпис (синия текст):

Подписът се генерира с ключ, който се съхранява в базата данни, в защитения периметър; самият той се генерира и обновява (потребителите не виждат нищо от това). Генерира се хеш и всяко съобщение при създаване се подписва, а при получаване – се валидира.
Вероятно у хората възниква въпросът: "Насколько это все забавя?" Аз вече казах, че трябва да работи бързо, особено в отсъствието на тази функция.
Какво означава да се използва Causal consistency в този случай? Това е показването на параметъра afterClusterTime. А без това, той просто ще предава стойности в какъвто и да е случай. Gossiping, започвайки от версия 3.6, работи винаги.
Ако оставим постоянното генериране на подписи, това ще забавя системата дори в отсъствието на функцията, което не съответства на нашите подходи и изисквания. И какво направихме?
Прави го бързо!
Доста лесна работа, но трикът е интересен – ще споделя, може би на някого ще му бъде интересно.
Имаме хеш, в който се съхраняват подписаните данни. Всички данни преминават през кеш. Кешът подписва не конкретно време, а Range. Когато дойде определена стойност, генерираме Range, маскираме последните 16 бита и тази стойност подписваме:

Получавайки такъв подпис, ускоряваме системата (умерено) 65000 пъти. Тя работи прекрасно: когато бяха поставени експериментите – времето реално се е съкратило 10000 пъти, когато имаме последователно обновление. Ясно е, че когато са в разнобой, това не работи. Но в повечето практически случаи действа. Комбинацията от подписа Range заедно с подписа реши проблема със сигурността.
На какво се научихме?
Уроките, които извлякохме от това:
- Трябва да четем материали, истории, статии, защото имаме много интересни неща. Когато работим по някаква функция (особено сега, когато правим транзакции и т.н.), трябва да четем и да се запознаваме. Това отнема време, но е наистина много полезно, защото става ясно къде се намираме. Не сме измислили нищо ново – просто сме взели съставките.
Всъщност, наблюдава се определена разлика в мисленето, когато става въпрос за академична конференция (например „Сигмон“), където всички се фокусират върху нови идеи. В какво се състои новизната на нашия алгоритъм? Тук новизна няма. Новизна по-скоро се състои в начина, по който сме смесили съществуващите подходи. Затова първото нещо – трябва да четем класиците, започвайки от Лампарт.
- В продукцията изискванията са напълно различни. Уверен съм, че много от вас не се сблъскват с „сферични“ бази данни в абстрактен вакуум, а с нормални, реални неща, които имат проблеми с наличността, латентността и устойчивостта на отказ.
- Последното е, че ни се наложи да разгледаме различни идеи и да комбинираме няколко напълно различни статии в един подход. Идеята за подписването, например, дойде от статия, която разглеждаше Paxos-протокола, който е за не-византийски откази в рамките на авторизационния протокол, а за византийските – извън авторизационния протокол... В общи линии, точно това в крайна сметка направихме.
Нищо ново тук абсолютно няма! Но веднага щом всичко това смесихме... Също е така, както да кажеш, че рецептата за салат Оливие е глупост, защото яйцата, майонезата и краставиците вече са измислени... Това е точно същата история.

Тук приключвам. Благодаря ви!
Въпроси
Въпрос от залата (по-нататък – В): – Благодаря, Михаил, за доклада! Темата за времето е интересна. Вие използвате Gossiping. Казахте, че всеки има своето време, всички знаят своето локално време. Така разбрах, че имаме драйвер – клиентите с драйвери може да са много, и query-planner-ите, и шардовете също много... Но какво ще се случи със системата, ако изведнъж получим разминаване: някой реши, че е напред с минута, а друг – назад с минута? Къде ще се окажем?
МТ: – Отличен въпрос всъщност! Всъщност исках да говоря за шардовете. Ако разбирам правилно въпроса, имаме следната ситуация: има шард 1 и шард 2, четенето става от тези два шарда – те разполагат с различна информация, не взаимодействат помежду си, тъй като времето, което те знаят, е различно, особено времето, което имат в оплогите.
Да предположим, шард 1 е направил милион записа, шард 2 – абсолютно никакви, а заявката е изпратена на двата шарда. И при първия има afterClusterTime над милион. В такава ситуация, както обясних, шард 2 никога няма да отговори.
В: – Исках да разбера как те синхронизират и избират едно логическо време?
МТ: – Много просто се синхронизират. Шардът, когато получи afterClusterTime и не намира време в "Оплога" – инициира no approved. Тоест, той ръчно повишава своето време до тази стойност. Това означава, че няма събития, отговарящи на тази заявка. Той създава това събитие изкуствено и по този начин става Causal Consistent.
В: – А какво, ако след това дойдат някакви събития, които някъде в мрежата са се загубили?
МТ: – Шардът е проектиран така, че те вече няма да дойдат, тъй като това е единичен мастер. Ако вече е записал, те вече не могат да дойдат, а ще бъдат след това. Не може да се получи, че нещо се е задържало, а след това той направи no write и тези събития дойдоха – и нарушиха Causal consistency. Когато прави no write, те всички трябва да дойдат по-нататък (той ще ги изчака).

В: – Имам няколко въпроса относно опашките. Causal consistency предполага, че има определена опашка от действия, които трябва да бъдат изпълнени. Какво ще се случи, ако един пакет изчезне? Ето, 10-ият минете, 11… 12-ият изчезна, а всички останали чакат, когато той бъде изпълнен. И изведнъж машината умре, нямаме какво да направим. Има ли максимална дължина на опашката, която се натрупва, преди да бъде изпълнена? Какъв фатален провал настъпва при загубата на кое да е състояние? Особено, ако записваме, че има някакво предходно състояние, от което трябва да се основание? А не можем да се базираме на него!
МТ: – Също прекрасен въпрос! Какво правим? В MongoDB има понятието за кворумни записи, кворумно четене. В кои случаи съобщението може да изчезне? Когато записът не е кворумен или когато четенето не е кворумно (може също да се появи някакъв garbage).
Относно Causal consistency проведохме голямо експериментално изследване, резултат от което показа, че в случаите, когато записите и четенето са некворумни, се появяват нарушения на Causal consistency. Точно това, което казвате!
Нашият съвет: използвайте поне кворумно четене при работа с Causal consistency. В този случай нищо няма да изчезне, дори ако кворумният запис изчезне... Това е ортогонална ситуация: ако потребителят не иска данните да изчезнат, трябва да се използва кворумен запис. Causal consistency не дава гаранция за durability. Гаранцията за durability се осигурява от репликацията и механизма, свързана с репликацията.
В: – Когато създаваме инстанция, която извършва шардинг (не master, а slave съответно), тя се опира на unix времето на собствената машина или на времето на "мастера"; синхронизира се за първи път или периодично?
МТ: – Сега ще изясня. Шард (т.е. хоризонтална партиция) – там винаги има Primary. А в шарда може да има "мастер" и реплики. Но шардът винаги поддържа запис, защото трябва да поддържа определен домейн (в шарда стои Primary).
В: – Тоест всичко зависи изцяло от "мастера"? Винаги се използва времето на "мастера"?
МТ: – Да. Може да се каже, че часовниците тикат, когато се извършва запис в "мастера", в "Оплог".
В: – Имаме клиент, който се свързва, и не трябва да знае нищо за времето?
МТ: – Изобщо не трябва да знае нищо! Ако говорим за това как работи при клиента: при клиента, когато иска да ползва Causal consistency, той трябва да отвори сесия. Сега там има всичко: и транзакции в сесията, и retrieve a rights… Сесията е подреждане на логическите събития, които се случват с клиента.
Ако той отвори сесията и там каже, че иска Causal consistency (ако по подразбиране сесията поддържа Causal consistency), всичко автоматично работи. Драйверът запомня това време и го увеличава, когато получи ново съобщение. Той запомня какъв отговор е върнал предишният от сървъра, който е върнал данните. Следващото запитване ще съдържа afterCluster ("време по-голямо от това").
Клиентът не трябва да знае абсолютно нищо! Това е напълно непрозрачно за него. Ако хората използват тези функции, какво може да се постигне? Първо, може безопасно да се четат вторични данни: може да се записва на Primary, а да се чете от географски реплицирани вторични и да си сигурен, че работи. При това сесиите, записани на Primary, могат да бъдат предавани дори на Secondary, т.е. може да се използват не една, а няколко сесии.
В: – Темата за Eventual consistency е тясно свързана с новия пласт на компютърните науки – типовете данни CRDT (Conflict-free Replicated Data Types). Разглеждали ли сте интеграцията на тези типове данни в базата и какво можете да кажете по този въпрос?
МТ: – Добър въпрос! CRDT има смисъл при конфликти при запис: в MongoDB – един главен.
В: – Имам въпрос от девопсите. В настоящия свят има ситуации, в които се получава така нареченият византийски провал, и зли хора вътре в защитения периметър започват да се намесват в протокола, специално подготвяйки пакети?

МТ: – Злите хора вътре в периметъра са все едно троянски конь! Злите хора в периметъра могат да направят много лоши неща.
В: – Ясно е, че оставянето на дупка в сървъра, грубо казано, през която могат да минат слонове и да разрушат целия кластер завинаги… ще отнеме време за ръчно възстановяване… Това, меко казано, е неправилно. От друга страна, интересно е следното: в реалния живот, в практиката, срещат ли се подобни ситуации, когато вътрешни нападения наистина се случват?
МТ: – Понеже не често се сблъсквам с нарушения на сигурността в реалния живот, не мога да кажа – може би те и наистина се случват. Но ако говорим за философията на разработване, то ние смятаме така: имаме периметър, който осигурява момчетата, които се занимават с сигурност – това е заключва, стена; а вътре в периметъра може да се прави всичко. Ясно е, че има потребители, които могат само да гледат, а има и потребители, които могат да изтриват каталози.
В зависимост от правата, щетите, които потребителите могат да нанесат, могат да бъдат с мишка, а може и със слон. Ясно е, че потребител с пълни права може да направи абсолютно всичко. Потребител с ограничени права може да нанесе значително по-малко щета. В частност, той не може да срине системата.
В: – В защитената зона някой активно формулира неочаквани протоколи за сървъра, с цел да го срине, а ако има късмет, може и целия клъстер... Може ли да е толкова "добре"?
МТ: – Никога не съм чувал за такива неща. Това, че може да се срине сървъра по този начин, не е тайна. Да сринеш отвътре, докато си авторизиран потребител, който може да запише нещо в съобщението... Всъщност не може, защото пак ще се верифицира. Има възможност да се изключи тази аутентификация за потребители, които не искат - това е техен проблем; те, грубо казано, сами разрушават стените и тогава може да се вкара слон, който да ги смачка... А всъщност, може да се облечеш като ремонтник, да дойдеш и да го извадиш!
В: – Благодаря за доклада. Сергей ("Яндекс"). В "Mongo" има константа, която лимитира броя на гласуващите членове в Replica Set-а, и тази константа е равна на 7 (седем). Защо е тази константа? Защо не е някакъв параметър?
МТ: – Replica Set при нас може да има и 40 нода. Там винаги има мнозинство. Не знам коя версия...
В: – В Replica Set-а е възможно да се пускат и не гласуващи членове, но гласуващите – максимум 7. Как в този случай да се справим с изключването, ако нашият Replica Set е разпределен на 3 дата центъра? Един дата център може лесно да се изключи, като и още една машинка да отпадне.
МТ: – Това вече е малко извън темата на доклада. Това е общ въпрос. Може би по-късно мога да го обсъдим.


Малко реклама 🙂
Благодарим ви, че оставате с нас. Харесвате ли нашите статии? Искате ли да виждате повече интересни материали? Подкрепете ни, като направите поръчка или препоръчате на познати, , уникален аналог на entry-level сървъри, който е създаден от нас за вас: (с налични опции за RAID1 и RAID10, до 24 ядра и до 40GB DDR4).
Dell R730xd на половин цена в дата центъра Equinix Tier IV в Амстердам? Всичко това само при нас в Нидерландия! Dell R420 — 2x E5-2430 2.2GHz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — от 99 $! Чете се за това
Източник: habr.com
