HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Наступна конференція HighLoad++ пройде 6 та 7 квітня 2020 року в Санкт-Петербурзі.
Подробиці та квитки за посиланням. HighLoad++ Siberia 2019. Зал "Красноярськ". 25 червня, 12. Тези та презентація.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Буває, що практичні вимоги конфліктують із теорією, де не враховано важливі для комерційного продукту аспекти. У цій доповіді представлено процес вибору та комбінування різних підходів до створення компонентів Causal consistency на основі академічних досліджень, виходячи з вимог комерційного продукту. Слухачі дізнаються про існуючі теоретичні підходи до logical clocks, dependency tracking, system security, clock synchronization і чому MongoDB зупинилися на тих чи інших рішеннях.

Михайло Тюленєв (далі – МТ): – Я розповідатиму про Causal consistency – це фіча, над якою ми працювали у MongoDB. Я працюю у групі розподілених систем, ми її зробили приблизно два роки тому.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

У процесі довелося ознайомитися з великою кількістю академічного Research, тому що ця фіча досить добре вивчена. З'ясувалося, що жодна стаття не вписується в те, що потрібно в продакшні, базі даних через дуже специфічні вимоги, які є, напевно, в будь-якому production applications.

Я розповідатиму про те, як ми, будучи споживачем академічного Research, готуємо з нього щось таке, що ми потім можемо піднести нашим користувачам як готову страву, якою зручно, безпечно користуватися.

Причинна узгодженість (Causal consistency). Визначимося з поняттями

Для початку я хочу загалом сказати, що ж таке Causal consistency. Є два персонажі – Леонард та Пенні (серіал «Теорія великого вибуху»):

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Припустимо, що Пенні в Європі, а Леонард хоче зробити для неї якийсь сюрприз, тусовку. І він нічого краще не вигадує, ніж викинути її з френд-листа, послати всім друзям апдейт на feed: "Давайте порадуємо Пенні!" (Вона в Європі, поки спить, не бачить цього всього і не може побачити, тому що вона не там). Зрештою видаляє цю посаду, стирає з «Фіда» і відновлює access, щоб вона нічого не помітила і скандалу не було.
Це все чудово, але припустимо, що система розподілена, і події пішли не трохи не так. Може, наприклад, станеться, що обмеження access Пенні відбулося після того, як з'явився цей пост, якщо події не пов'язані між собою причинно-наслідковими зв'язками. Власне, це приклад того, коли потрібна наявність Causal consistency для того, щоб виконати бізнес-функцію (в даному випадку).

Насправді це досить нетривіальні властивості бази даних – мало хто їх підтримує. Давайте перейдемо до моделей.

Моделі узгодженості (Consistency Models)

Що таке модель консистенції в базах даних? Це деякі гарантії, які розподілена система дає щодо того, які дані і в якій послідовності клієнт може отримати.

У принципі, всі моделі консистенції зводяться до того, наскільки розподілена система схожа на систему, яка працює, наприклад, на одному nod'е на лептопі. І ось наскільки система, яка працює на тисячах георозподілених "Нодів", схожа на лептоп, в якому всі ці властивості виконуються в принципі автоматично.

Тому моделі консистенції лише до розподілених систем і застосовуються. Усі системи, які раніше існували та працювали на одному вертикальному масштабуванні, таких проблем не мали. Там був один Buffer Cache і з нього все завжди вичитувалося.

Модель Strong

Власне, перша модель – це Strong (або лінія rise ability, як її часто називають). Це модель консистенції, яка гарантує, що кожна зміна, як тільки виходить підтвердження про те, що вона сталася, стає видно всім користувачам системи.

Це створює глобальний порядок всіх подій у БД. Це дуже сильна властивість консистенції, і вона взагалі дуже дорога. Проте воно дуже добре підтримується. Воно просто дуже дороге та повільне – ним просто рідко користуються. Це називається rise ability.

Є ще одна, сильніша властивість, яка підтримується у «Спаннері» – називається External Consistency. Ми про нього поговоримо трохи згодом.

Причинний

Наступне – це Causal, саме те, про що я говорив. Між Strong і Causal існує ще кілька підрівнів, про які я не говоритиму, але вони всі зводяться до Causal. Це важлива модель, тому що вона – найсильніша з усіх моделей, найсильніша консистенція за наявності мережі чи partitions.

Causals – це власне ситуація, коли події пов'язані причинно-наслідковим зв'язком. Найчастіше їх сприймають як Read your on rights з погляду клієнта. Якщо клієнт спостерігав якісь значення, він не може побачити значення, які були в минулому. Він починає бачити префіксні читання. Це все зводиться до того самого.
Causals як модель консистенції - часткове впорядкування подій на сервері, при якому події з усіх клієнтів спостерігаються в одній послідовності. У цьому випадку – Леонард та Пенні.

Подія

Третя модель – це Eventual Consistency. Це те, що підтримує абсолютно всі розподілені системи, мінімальна модель, яка взагалі має сенс. Вона означає таке: коли у нас відбуваються деякі зміни в даних, вони в якийсь момент стають консистентними.

У такий момент вона нічого не каже, інакше вона перетворилася б на External Consistency – була б зовсім інша історія. Проте це дуже популярна модель, найпоширеніша. За замовчуванням, всі користувачі розподілених систем використовують саме Eventual Consistency.

Я хочу навести деякі порівняльні приклади:

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Що означають ці стрілочки?

  • Затримка. При збільшенні сили консистенції вона стає більше зі зрозумілих причин: потрібно зробити більше записів, отримати підтвердження від усіх хостів та нодів, які беруть участь у кластері, що дані вже є. Відповідно в Eventual Consistency найшвидша відповідь, тому що там, як правило, можна навіть у memory закомітіті і цього буде в принципі достатньо.
  • Наявність Якщо це розуміти як можливість системи відповісти за наявності розривів мережі, partitions, або якихось відмов – стійкість до відмови зростає при зменшенні моделі консистенції, оскільки нам достатньо того, щоб один хост жив і при цьому видавав якісь дані. Eventual Consistency взагалі нічого не гарантує щодо даних – це може бути все, що завгодно.
  • Аномалії. При цьому, звісно, ​​зростає кількість аномалій. У Strong Consistency їх взагалі практично не повинно бути, а Eventual Consistency вони можуть бути які завгодно. Виникає питання: чому люди вибирають Eventual Consistency, якщо вона містить аномалії? Відповідь полягає в тому, що Eventual Consistency-моделі застосовні, а аномалії існують, наприклад, у короткий проміжок часу; існує можливість використовувати майстер для читання та більш-менш читати консистентні дані; часто можна використовувати сильні моделі консистенції. Практично це працює, і часто кількість аномалій обмежена за часом.

Теорема CAP

Коли ви бачите слова consistency, availability – що вам спадає на думку? Правильно - CAP theorem! Я зараз хочу розвіяти міф… Це не я – є Мартін Клеппман, який написав чудову статтю, чудову книжку.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Теорема CAP – це принцип, сформульований у 2000-х роках про те, що Consistency, Availability, Partitions: take any two, і не можна вибрати три. Це був принцип. Він був доведений як теорема через кілька років, це зробили Джілберт і Лінч. Потім це стало використовуватися як мантра – системи стали ділитися на CA, CP, AP тощо.

Ця теорема була доведена насправді для яких випадків… По-перше, Availability розглядалася не як безперервне значення від нуля до сотні (0 – система «мертва», 100 – відповідає швидко; ми її так звикли розглядати), а як властивість алгоритму що гарантує, що при всіх його виконаннях він повертає дані.

Про час відповіді там взагалі немає жодного слова! Є алгоритм, який повертає дані через 100 років – прекрасний available-алгоритм, які є частиною теореми CAP.
Друге: доводилася теорема змін у значеннях однієї й тієї ж ключа, тому що ці зміни – лінія resizable. Це означає те, що насправді вони практично не використовуються, тому що моделі інші Eventual Consistency, Strong Consistency (можливо).

Навіщо це все? До того що теорема CAP саме у тій формі, в якій вона доведена, практично не застосовна, рідко використовується. У теоретичній формі вона якимось чином обмежує все. Виходить якийсь принцип, який інтуїтивно вірний, але ніяк, загалом, не доведений.

Causal consistency – найсильніша модель

Те, що відбувається зараз – можна отримати всі три речі: Consistency, Availability отримати за допомогою Partitions. Зокрема Causal consistency – найсильніша модель консистенції, яка за наявності Partitions (розривів у мережі) все одно працює. Тому вона і становить такий великий інтерес, тому ми нею й зайнялися.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Вона, по-перше, спрощує працю розробників додатків. Зокрема, наявність великої підтримки з боку сервера: коли всі записи, які відбуваються всередині одного клієнта, гарантовано прийдуть в такій послідовності на іншому клієнті. По-друге, вона витримує partitions.

Внутрішня кухня MongoDB

Пам'ятаючи, що ланч, ми переміщуємося на кухню. Я розповім про модель системи, а саме – що таке MongoDB для тих, хто вперше чує про таку базу даних.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

MongoDB (далі – «МонгоБД») – це розподілена система, яка підтримує горизонтальне масштабування, тобто шардинг; і всередині кожного шарду вона також підтримує надмірність даних, тобто реплікацію.

Шардинг у «МонгоБД» (не реляційна БД) виконує автоматичне балансування, тобто кожна колекція документів (або «таблиця» в термінах реляційних даних) на шматочки, і сервер автоматично рухає їх між шардами.

Query Router, який розподіляє запити, є для клієнта деяким клієнтом, через який він працює. Він уже знає, де і які дані знаходяться, надсилає всі запити до правильного шарду.

Ще один важливий момент: MongoDB це single master. Є один Primary – він може брати записи, що підтримують ключі, які він містить. Не можна зробити Multi-master write.

Ми зробили випуск 4.2 – там з'явилися нові цікаві речі. Зокрема, вставили Lucene - пошук - саме executable java прямо в "Монго", і там стало можливим виконувати пошук через Lucene, такий самий, як в "Еластіці".

І зробили новий продукт - Charts, він також доступний на "Атласі" (власний Cloud "Монго"). Вони мають Free Tier – можна погратися з цим. Charts мені дуже сподобався – візуалізація даних, дуже інтуїтивна.

Інгредієнти Causal consistency

Я нарахував близько 230 статей, які були опубліковані на цю тему від Леслі Ламперта. Зараз із своєї пам'яті я вам їх донесу якісь частини цих матеріалів.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Все почалося зі статті Леслі Ламперта, написаної в 1970-х роках. Як бачите, досі продовжуються якісь дослідження у цій темі. Зараз Causal consistency переживає інтерес у зв'язку з розвитком саме розподілених систем.

Обмеження

Які обмеження є? Це насправді один із головних моментів, тому обмеження, які накладає продакшн системи, дуже відрізняються від обмежень, які існують в академічних статтях. Часто вони досить штучні.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

  • По-перше, "МонгоДБ" - це single master, як я вже казав (це сильно спрощує).
  • Ми вважаємо, що близько 10 тисяч шардів система має підтримувати. Ми не можемо приймати якихось архітектурних рішень, які явно обмежуватимуть це значення.
  • Є у нас хмара, але ми припускаємо, що у людини повинна залишатися можливість, коли вона завантажує binary, запускає у себе на лептопі, і все чудово працює.
  • Ми припускаємо те, що Research рідко використовується: зовнішні клієнти можуть робити що завгодно. "МонгоДБ" - це опенсорс. Відповідно клієнти можуть бути такі розумні, злі – можуть хотіти все зламати. Ми вважаємо, що візантійські Фейлори можуть відбуватися.
  • Для зовнішніх клієнтів, які за межами периметра – важливе обмеження: якщо ця фіча вимкнена, то жодної performance degradation не повинно спостерігатися.
  • Ще один момент – взагалі антиакадемічний: сумісність попередніх версій та майбутніх. Старі драйвери повинні підтримувати нові апдейти і БД повинна підтримувати старі драйвери.

Загалом усе це накладає обмеження.

Компоненти Causal consistency

Я зараз розповім про деякі компоненти. Якщо розглядати взагалі Causal consistency, можна виділити блоки. Ми вибирали з робіт, які відносяться до якогось блоку: Dependency Tracking, вибір годинника, як цей годинник можна між собою синхронізувати, і як ми забезпечуємо безпеку – це зразковий план того, про що я говоритиму:

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Повне відстеження залежностей (Full Dependency Tracking)

Навіщо воно потрібне? Для того, коли дані реплікуються – кожен запис, кожна зміна даних містила в собі інформацію про те, від яких змін вона залежить. Найперша та наївна зміна – це коли кожне повідомлення, яке містить у собі запис, містить інформацію про попередні повідомлення:

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

У цьому прикладі номер у фігурних дужках – це номери записів. Іноді ці записи зі значеннями передаються навіть цілком, іноді версії якісь передаються. Суть полягає в тому, що кожна зміна містить інформацію про попереднє (явно несе це все в собі).

Чому ми вирішили не скористатися таким підходом (повний трекінг)? Очевидно, тому що цей підхід є непрактичним: будь-яка зміна в соціальній мережі залежить від усіх попередніх змін у цій соцмережі, передаючи, скажімо, «Фейсбук» або «Вконтакте» у кожному оновленні. Тим не менш, є багато досліджень саме Full Dependency Tracking – це пресоціальні мережі, для якихось ситуацій це дійсно працює.

Явне відстеження залежностей (Explicit Dependency Tracking)

Наступний – більш обмежений. Тут також розглядається передача інформації, але тільки тієї, яка явно залежить. Що від чого залежить, як правило, визначає вже Application. Коли дані реплікуються, при запиті видаються лише відповіді, коли попередні задоволені, тобто показані. У цьому й суть того, як Causal consistency працює.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Вона бачить, що запис 5 залежить від записів 1, 2, 3, 4 – відповідно, він чекає, перш ніж клієнт отримує доступ до змін, внесених постановою access'а Пенні, коли всі попередні зміни вже пройшли в базі даних.

Це теж нас не влаштовує, тому що все одно інформації надто багато, і це сповільнюватиме. Є інший підхід.

Годинник Лемпорта (Lamport Clock)

Вони дуже старі. Lamport Clock має на увазі те, що ці dependency згортаються в скалярну функцію, яка називається Lamport Clock.

Скалярна функція – це абстрактне число. Часто його називають логічним часом. У кожній події цей counter збільшується. Counter, який зараз відомий процесу, посилає кожне повідомлення. Зрозуміло, що процеси можуть бути розсинхронізовані, у них може бути зовсім різний час. Тим не менш, таким обміном повідомленнями система якось балансує годинник. Що відбувається у цьому випадку?

Я розбив той великий шард надвоє, щоб було зрозуміло: Friends можуть жити в одному ноді, що містить шматок колекції, а Feed – взагалі в іншому ноді, в якому міститься шматок цієї колекції. Зрозуміло, як вони можуть потрапити не в чергу? Спочатку Feed скаже: "Реплікувався", а потім - Friends. Якщо система не забезпечує якихось гарантій, що Feed не буде показаний, поки залежність Friends в колекції Friends теж не буде доставлена, то у нас виникне ситуація, про яку я згадав.

Ви бачите, як логічно збільшується час counter на Feed'е:

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Таким чином, основна властивість цього Lamport Clock та Causal consistency (поясненого через Lamport Clock) полягає в наступному: якщо у нас є події A та B, і подія B залежить від події A*, то з цього випливає, що LogicalTime від Event A менше ніж LogicalTime від Event 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:

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Чим Векторні годинники кращі? Тим, що дозволяють розібратися, які події є одночасними і коли вони відбуваються на різних нодах. Це дуже важливо для системи шардування як «МонгоБД». Однак ми це не вибрали, хоча це і чудова штука, і чудово працює, і нам підійшла б, мабуть.

Якщо ми маємо 10 тисяч шардів, ми не можемо передавати 10 тисяч компонентів, навіть якщо стискаємо, ще щось вигадуємо – все одно корисне навантаження буде в рази менше, ніж обсяг усього цього вектора. Тому, скрипучи серцем та зубами, ми відмовилися від цього підходу і перейшли до іншого.

Spanner TrueTime. Атомний годинник

Я казав, що буде розповідь про «Спаннера». Це крута штука, прямий XXI століття: атомний годинник, GPS-синхронізація.

Ідея яка? "Спаннер" - це гуглівська система, яка нещодавно навіть стала доступна для людей (вони приробили до неї SQL). Кожна транзакція там має деякий time stamp. Оскільки час синхронізований*, кожній події можна призначити певний times – атомний годинник має час очікування, після якого гарантовано «відбувається» вже інший час.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Таким чином, просто записуючи в БД і чекаючи якогось періоду часу, автоматично гарантується Serializability події. Вони найсильніша Consistency-модель, яку у принципі можна уявити – вона External Consistency.

* Це основна проблема годинника Лемпарта - вони ніколи не синхронні на розподілених системах. Вони можуть розходитися, навіть якщо NTP все одно працюють не дуже добре. «Спаннер» має атомний годинник і синхронізацію, здається, то мікросекунд.

Чому ми не обрали? Ми не припускаємо, що у наших користувачів є вбудований атомний годинник. Коли вони з'являться, будучи вбудованими у кожен лептоп, буде якась суперкрута GPS-синхронізація – тоді так… А поки що найкраще, що можливо – це «Амазон», Base Stations – для фанатиків… Тому ми використали інший годинник.

Гібридний годинник (Hybrid Clock)

Це фактично те, що цокає в МонгоБД при забезпеченні Causal consistency. Чи гібридні вони в чому? Гібрид - це скалярне значення, але воно складається з двох компонентів:

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

  • Перше - це unix'я епоха (кілька секунд пройшло з «початку комп'ютерного світу»).
  • Друге – деякий інкремент, також 32-бітовий unsigned int.

Це, власне, і все. Є такий підхід: частина, яка відповідає за час, постійно синхронізується з годинником; щоразу, коли відбувається оновлення, ця частина синхронізується з годинником і виходить, що час завжди більш-менш правильний, а increment дозволяє розрізняти події, що відбулися в той самий момент часу.

Чому це важливо для «МонгоБД»? Тому що дозволяє робити якісь бекап-рестори на певний момент часу, тобто подія індексується часом. Це важливо, коли потрібні деякі події; для БД події – це зміни у БД, які відбулися у певні проміжки часу.

Найголовнішу причину я скажу лише вам (будь ласка, тільки нікому не говоріть)! Ми так зробили тому, що так виглядають впорядковані, індексовані дані MongoDB OpLog. OpLog – це структура даних, яка містить у собі абсолютно всі зміни в базі: вони спочатку потрапляють до OpLog, а потім вже застосовуються вже власне до Storage у тому випадку, коли це реплікована дата чи шард.

То була головна причина. Все-таки існують ще й практичні вимоги до розробки бази, а це означає, що має бути просто – мало коду, якнайменше зламаних речей, які потрібно переписувати та тестувати. Те, що у нас оплоги виявилися проіндексованими гібридним годинником, сильно допомогло, і дозволило зробити правильний вибір. Це справді виправдало себе і якось чарівно запрацювало на першому ж прототипі. Було дуже круто!

Синхронізація годинника

Існує кілька способів синхронізації, описаних у науковій літературі. Я говорю про синхронізацію, коли у нас є два різні шарди. Якщо одна репліка-сет – там жодної синхронізації не потрібно: це сингл-майстер; у нас є OpLog, в який всі зміни потрапляють – у цьому випадку все вже sequentially ordered у самому «Оплозі». Але якщо у нас є дві різні шарди, тут синхронізація часу важлива. Ось тут векторний годинник більше допоміг! Але у нас їх нема.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Другий підходить - це "Хартбіти" (Heartbeats). Можна обмінюватися деякими сигналами, які кожну одиницю часу. Але «Хартбіти» – надто повільні, ми не можемо latency забезпечити нашому клієнту.

True time – звісно, ​​чудова штука. Але, знову ж таки, це, напевно, майбутнє… Хоча в «Атласі» вже можна зробити, вже є швидкі синхронізатори часу. Але це не буде доступним для всіх.

Gossiping – це коли всі повідомлення включають час. Це приблизно те, що ми використовуємо. Кожне повідомлення між нодами, драйвер, роутер дата-ноди, абсолютно все для «МонгоДБ» – це якісь елементи, компоненти бази даних, які містять годинник, який тече. Вони скрізь є значення гібридного часу, воно передається. 64 біти? Це дозволяє це можна.

Як все це працює разом?

Тут я розглядаю один репліка-сет, щоб було трохи простіше. Є Primary та Secondary. Secondary робить реплікацію та не завжди повністю синхронізований з Primary.

Відбувається вставка (insert) у "Праймері" з деяким значенням часу. Цей insert збільшує внутрішній каунтер на 11, якщо це максимально. Або він буде перевіряти значення годинника і синхронізується по годинниках, якщо значення годинника більше. Це дозволяє впорядкувати за часом.

Після того, як він робить запис, відбувається важливий момент. Годинник у «МонгоДБ» та інкрементується лише у разі запису в «Оплог». Це і є подією, що змінює стан системи. Абсолютно у всіх класичних статтях подією вважається попадання повідомлення в нід: повідомлення прийшло – значить система змінила свій стан.

Це з тим, що з дослідженні не зовсім можна зрозуміти, як це повідомлення буде інтерпретовано. Ми точно знаємо, що якщо вона не відображена в «Оплозі», то вона ніяк не буде інтерпретована, і зміною стану системи є лише запис «Оплог». Це нам все спрощує: і модель спрощує, і дозволяє вести впорядкування в рамках одного репліка-сету, і багато іншого корисного.

Повертається значення, яке вже записано в "Оплог" - ми знаємо, що в "Оплозі" вже лежить це значення, і його час - 12. Тепер, скажімо, починається читання з іншого нода (Secondary), і він передає вже afterClusterTime в самому повідомленні. Він каже: «Мені потрібне все, що сталося як мінімум після 12 або під час дванадцяти» (див. рис. вище).

Це те, що називається Causal a consistent (CAT). Є таке поняття теоретично, що це деякі зріз часу, що консистентно саме собою. У разі можна сказати, що це стан системи, яке спостерігалося на час 12.

Зараз тут поки що нічого немає, тому що це нібито імітує ситуацію, коли потрібно, щоб Secondary реплікував дані з Primary. Він чекає ... І ось дані прийшли - повертає ці значення.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Ось так приблизно все працює. Майже що.

Що означає «майже що»? Давайте припустимо, що є деяка людина, яка прочитала і зрозуміла, як це все працює. Зрозумів, що кожного разу відбувається ClusterTime, він оновлює внутрішній логічний годинник, і потім наступний запис збільшує на одиницю. Ця функція займає 20 рядків. Допустимо, ця людина передає максимально велике 64-бітове число, мінус одиниця.

Чому "мінус одиниця"? Тому що внутрішній годинник підставиться в це значення (очевидно, це найбільше можливе і більше поточного часу), потім відбудеться запис в «Оплог», і годинник інкрементується ще на одиницю – і вже буде взагалі максимальне значення (там просто всі одиниці, далі нікуди) , unsaint int'и).

Зрозуміло, що після цього система стає абсолютно недоступною ні для чого. Її можна тільки вивантажити, почистити багато ручної роботи. Повний availability:

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Причому якщо це реплікується ще кудись, то просто лягає весь кластер. Абсолютно неприйнятна ситуація, яку будь-яка людина може організувати дуже швидко та просто! Тому ми розглядали цей момент як один із найважливіших. Як йому запобігти?

Наш шлях – підписувати clusterTime

Так воно передається у повідомленні (до синього тексту). Але ми почали ще й генерувати підпис (синій текст):

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Підпис генерується ключем, який зберігається всередині бази даних, всередині периметра захищеного; сам генерується, оновлюється (користувачі цього не бачать). Генерується hash, і кожне повідомлення під час створення підписується, а при отриманні – валідується.
Напевно, виникає запитання у людей: "Наскільки це все сповільнює?" Я ж казав, що маю швидко працювати, особливо за відсутності цієї фічі.

Що означає користуватися Causal consistency у цьому випадку? Це показує afterClusterTime-параметр. А без цього він просто передаватиме значення у будь-якому випадку. Gossiping починаючи з версії 3.6 працює завжди.

Якщо ми залишимо постійне генерування підписів, це уповільнюватиме систему навіть за відсутності фічі, що відповідає нашим підходам і вимогам. І що ми зробили?

Роби це швидко!

Досить проста річ, але трюк цікавий – поділюсь, може, комусь буде цікаво.
У нас є хеш, у якому зберігаються підписані дані. Усі дані йдуть через кеш. Кеш підписує не саме час, а Range. Коли настає деяке значення, ми генеруємо Range, маскуємо останні 16 біт, і це значення ми підписуємо:

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Отримуючи такий підпис, ми прискорюємо систему (умовно) у 65 тисяч разів. Воно чудово працює: коли поставили експерименти – там реально у 10 тисяч разів скоротився час, коли у нас послідовний апдейт. Зрозуміло, що коли вони рознобій, цього не виходить. Але у більшості практичних випадків це працює. Комбінація підпису Range разом із підписом дозволила вирішити проблему безпеки.

Чого ми навчилися?

Уроки, які ми з цього винесли:

  • Потрібно читати матеріали, історії, статті, бо нам багато чого цікавого є. Коли ми працюємо над якоюсь фічею (особливо зараз, коли ми робили транзакції і т. д.), треба читати, розбиратися. Це забирає час, але це дійсно дуже корисно, тому що стає зрозуміло, де ми знаходимося. Ми нічого нового начебто й не вигадали – просто взяли інгредієнти.

    Взагалі, спостерігається певна різниця у мисленні, коли має місце академічна конференція («Сігмон», наприклад) – там усі фокусуються на нових ідеях. У чому новизна нашого алгоритму? Тут особливої ​​новизни немає. Новизна швидше полягає в тому, як ми змішали існуючі підходи разом. Тому перше – треба читати класиків, починаючи з Лемпарта.

  • У продакшні зовсім інші вимоги. Я впевнений, що багато хто з вас стикається не зі «сферичними» базами даних в абстрактному вакуумі, а з нормальними, реальними речами, у яких існують проблеми щодо availability, latency та відмовостійкості.
  • Останнє – це те, що нам довелося розглянути різні ідеї та скомбінувати кілька взагалі різних статей в один підхід разом. Ідея про підписування, наприклад, взагалі прийшла зі статті, яка розглядала Paxos-протокол, які для невізантійських Фейлорів усередині авторизаційного протоколу, для візантійських – за межами авторизаційного протоколу… Загалом, це те, що ми зрештою і зробили.

    Нового тут нічого немає! Але як тільки ми це все разом змішали ... Все одно що сказати, що рецепт салату Олів'є - нісенітниця, тому що яйця, майонез і огірки вже придумали ... Це приблизно та сама історія.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

На цьому закінчу. Дякую!

Питання

Питання із зали (далі – В): – Дякую, Михайле за доповідь! Тема про час є цікавою. Ви використовуєте Gossiping. Сказали, що всі мають свій час, всі знають свій локальний час. Я так зрозумів, що у нас є драйвер - клієнтів з драйверами може бути багато, query-plannerів теж, шардів теж багато ... А до чого скочується система, якщо у нас раптом виникне розбіжність: хтось вирішить, що він на хвилину попереду, хтось на хвилину позаду? Де ми опинимося?

МТ: - Відмінне питання насправді! Я якраз про шарди хотів сказати. Якщо я правильно розумію питання, у нас така ситуація: є шард 1 і шард 2, читання відбувається з цих двох шардів – у них розбіжність, вони між собою не взаємодіють, тому що час, який вони знають – різний, особливо час, який у них існує в оплогах.
Припустимо, шард 1 зробив мільйон записів, шард 2 взагалі нічого, а запит прийшов на два шарди. І перший має afterClusterTime більше мільйона. У такій ситуації, як я пояснив, шард 2 взагалі ніколи не відповість.

В: – Я хотів дізнатися, як вони синхронізуються та оберуть один логічний час?

МТ: – Дуже просто синхронізуються. Шард, коли до нього приходить після ClusterTime, і він не знаходить часу в «Оплозі» – ініціює no approved. Тобто він руками піднімає свій час до цього значення. Це означає, що він не має подій, які відповідають цьому запиту. Він створює цю подію штучно і таким чином стає Causal Consistent.

В: - А якщо до нього після цього ще приїдуть якісь події, які в мережі десь загубилися?

МТ: – Шард так влаштований, що вони вже не приїдуть, бо це є single master. Якщо він уже записав, то вони вже не приїдуть, а будуть згодом. Не може вийти так, що десь щось застрягло, потім він зробить no write, а потім ці події приїхали – і порушилася Causal consistency. Коли він робить no write, всі вони повинні приїхати далі (він їх зачекає).

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

В: – У мене є кілька питань щодо черг. Causal consistency передбачає, що є певна черга дій, які необхідно виконати. Що станеться, якщо у нас один пакет пропадає? Ось пішов 10-й, 11... 12-й зник, а решта чекає, коли він виповниться. І в нас раптом померла машина, ми нічого не можемо зробити. Чи максимальна довжина черги, яка накопичується, перш ніж виконується? Який fatal failure відбувається при втраті одного стану? Тим більше, якщо ми записуємо, що є якийсь попередній стан, то від нього ж ми повинні якось відштовхуватися? А від нього не відштовхнулися!

МТ: - Теж чудове питання! Що ми робимо? MongoDB має поняття кворумних записів, кворумного читання. У яких випадках повідомлення може зникнути? Коли запис некворумний або коли читання не кворумне (теж може пристати якийсь garbage).
Щодо Causal consistency виконали велику експериментальну перевірку, результатом якої стало те, що у випадку коли записи та читання – некворумні, виникають порушення Causal consistency. Рівно те, що ви кажете!

Наша порада: використовувати хоча б кворумне читання під час використання Causal consistency. У цьому випадку пропадати нічого не буде, навіть якщо кворумний запис пропаде… Це ортогональна ситуація: якщо користувач не хоче, щоб зникли дані, потрібно використовувати кворумний запис. Causal consistency не дає гарантії тривалості. Гарантію тривалості дає repplication і machinery, пов'язана з repplication.

В: - Коли ми створюємо instance, який у нас шардинг виконує (не master, а slave відповідно), він спирається на unix-час власної машини або на час "майстра"; синхронізується вперше чи періодично?

МТ: - Зараз проясню. Шард (тобто горизонтальна партиція) там завжди є Primary. А в шарді може бути «майстер» і може бути репліки. Але шард завжди підтримує запис, тому що він повинен підтримувати певний домен (у шарді стоїть Primary).

В: – Тобто все залежить лише від «майстра»? Чи завжди використовується «майстер»-час?

МТ: – Так. Можна образно сказати: годинник цокає, коли відбувається запис у «майстер», в «Оплог».

В: - У нас є клієнт, який коннектиться, і йому не потрібно знати про час нічого?

МТ: - Взагалі нічого не треба знати! Якщо говорити про те, як це працює на клієнті: у клієнта, коли він хоче користуватись Causal consistency, йому потрібно відкрити сесію. Зараз там все: і транзакції у сесії, і retrieve a rights… Сесія – це впорядкування логічних подій, які відбуваються з клієнтом.

Якщо він відкриває цю сесію і там каже, що хоче Causal consistency (якщо за умовчанням сесія підтримує Causal consistency), все автоматично працює. Драйвер запам'ятовує цей час та збільшує його, коли отримує нове повідомлення. Він запам'ятовує, яку відповідь повернуло попереднє із сервера, який повернув дані. Наступний запит міститиме afterCluster («time більше цього»).

Клієнту не потрібно знати зовсім нічого! Це для нього абсолютно непрозоро. Якщо люди використовують ці фічі, що дозволяє зробити? По-перше, можна безпечно читати secondaries: можна писати на Primary, а читати з географічно реплікованих secondaries і бути впевненим, що це працює. При цьому сесії, які записав на Primary, можна передати навіть на Secondary, тобто можна використовувати не одну сесію, а кілька.

В: – З темою Eventual consistency дуже пов'язаний новий пласт Compute science – типи даних CRDT (Conflict-free Replicated Data Types). Чи розглядали ви інтеграцію цих типів даних до бази і що можете сказати про це?

МТ: - Гарне питання! CRDT має сенс для конфліктів під час запису: в MongoDB – single master.

В: - У мене питання від девопсів. У світі зустрічаються такі єзуїтські ситуації, коли візантійський Failure відбувається, і злі люди всередині захищеного периметра починають втикатися в протокол, спеціальним чином надсилати крафтові пакети?

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

МТ: - Злі люди всередині периметра - все одно що троянський кінь! Злі люди усередині периметру можуть зробити багато поганих речей.

В: - Зрозуміло, що залишати в сервері, грубо кажучи, дірочку, через яку можна зоопарк слонів просунути і обвалити весь кластер назавжди... Потрібен час для ручного відновлення... Це, м'яко кажучи, неправильно. З іншого боку, цікаво ось що: у реальному житті, у практиці зустрічаються такі ситуації, коли подібні внутрішні атаки відбуваються?

МТ: – Оскільки я нечасто стикаюся із security breach'ами у реальному житті, не можу сказати – може, вони й відбуваються. Але якщо говорити про девелоперську філософію, то ми вважаємо так: у нас є периметр, який забезпечує хлопців, які роблять security – це замок, стіна; а всередині периметра можна робити все, що завгодно. Зрозуміло, що користувачі з можливістю тільки подивитися, а є користувачі з можливістю стерти каталог.

Залежно від прав, damage, що користувачі можуть зробити, може бути мишею, а може бути і слоном. Зрозуміло, що користувач із повними правами може зробити взагалі все, що завгодно. Користувач із неширокими правами шкоди може заподіяти значно менше. Зокрема він не може зламати систему.

В: – У захищеному периметрі хтось поліз формувати несподівані протоколи для сервера, щоб раком поставити сервер, а якщо пощастить, то весь кластер… Чи буває настільки «добре»?

МТ: – Жодного разу не чув про такі речі. Те, що таким чином можна завалити сервер, – це не секрет. Завалити всередині, перебуваючи з протоколу, будучи авторизованим користувачем, який може записати у повідомлення щось таке… Насправді не можна, бо все одно він верифікуватиметься. Є можливість вимкнути цю автентифікацію для користувачів, які не хочуть, – це тоді їхні проблеми; вони, грубо кажучи, самі зруйнували стіни і можна запхати туди слона, який розтопче... А взагалі, можна вдягнутися ремонтником, прийти і витягти!

В: – Дякую за доповідь. Сергій ("Яндекс"). У «Монзі» є константа, яка лімітує кількість голосуючих членів у Replica Set'е, і ця константа дорівнює 7 (семи). Чому це константа? Чому це не параметр якийсь?

МТ: - Replica Set у нас буває і по 40 нодів. Там завжди є majority. Я не знаю яка версія…

В: – У Replica Set'і можна не голосуючих членів запускати, але голосуючих – максимум 7. Як у цьому випадку переживати вимкнення, якщо у нас Replica Set стягнуто на 3 дата-центри? Один дата-центр може запросто вимкнутись, і ще одна машинка випасти.

МТ: – Це вже трохи поза доповіддю. Це спільне питання. Може потім його можу розповісти.

HighLoad++, Михайло Тюленєв (MongoDB): Causal consistency: від теорії до практики

Небагато реклами 🙂

Дякую, що залишаєтеся з нами. Вам подобаються наші статті? Бажаєте бачити більше цікавих матеріалів? Підтримайте нас, оформивши замовлення або порекомендувавши знайомим, хмарні VPS для розробників від $4.99, унікальний аналог entry-level серверів, який був винайдений нами для Вас: Вся правда про VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps від $19 чи як правильно ділити сервер? (Доступні варіанти з RAID1 і RAID10, до 24 ядер і до 40GB DDR4).

Dell R730xd вдвічі дешевше в дата-центрі Equinix Tier IV в Амстердамі? Тільки в нас 2 х Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 ТБ від $199 у Нідерландах! Dell R420 – 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB – від $99! Читайте про те Як побудувати інфраструктуру корп. класу із застосуванням серверів Dell R730xd Е5-2650 v4 вартістю 9000 євро за копійки?

Джерело: habr.com

Додати коментар або відгук