Есть вопрос, который клиенты задают мне почти на каждом проекте, только разными словами: «А можно, чтобы система никогда не падала, всегда отвечала мгновенно и при этом данные везде были абсолютно точными?» Звучит разумно. Кто же хочет, чтобы система падала или показывала неправильные цифры? Но в мире распределённых систем на этот вопрос есть честный ответ, и он неприятный: всё сразу не бывает. Это не лень разработчиков и не ограничение конкретной технологии. Это закон, примерно такой же, как закон сохранения энергии, и называется он теоремой CAP.
Три желания и одна золотая рыбка
Когда система живёт не на одном сервере, а на нескольких, в разных стойках, дата-центрах или странах, у неё появляются три свойства, которых хочется одновременно.
- Согласованность: любой пользователь в любой момент видит одни и те же, самые свежие данные. Если Вы списали деньги со счёта в Киеве, то в Варшаве уже видят новый остаток.
- Доступность: система всегда отвечает на запрос, не выдаёт ошибку и не заставляет ждать бесконечно.
- Устойчивость к разделению: система продолжает работать, даже если связь между её частями прервалась, например, порвался кабель между дата-центрами.
Теорема утверждает, что в момент, когда связь между частями системы прервалась, нельзя сохранить и согласованность, и доступность одновременно. Приходится выбирать. Это как золотая рыбка, которая исполняет три желания, но с мелким шрифтом в договоре: в шторм только два.
Почему разделение не выбирают
Часто эту теорему пересказывают как «выбери два из трёх», и это не совсем точно. На самом деле устойчивость к разделению в распределённой системе не опция, а данность. Сеть рвётся. Кабели перерезают экскаваторы, дата-центры теряют питание, провайдеры ошибаются в настройках. Если у Вас больше одного сервера, рано или поздно связь между ними пропадёт. Поэтому реальный выбор звучит так: что делать системе, когда это случилось?
Вариант первый: пожертвовать доступностью ради точности. Система говорит: «Я не уверена, что мои данные свежие, поэтому лучше откажу в операции, чем покажу неправду». Так ведут себя банковские и финансовые системы. Лучше, чтобы перевод временно не прошёл, чем чтобы одни и те же деньги потратили дважды.
Вариант второй: пожертвовать точностью ради доступности. Система говорит: «Покажу то, что у меня есть, даже если это слегка устарело, а потом догоню». Так ведут себя социальные сети, каталоги товаров, новостные ленты. Если лайк под постом появится у Вашего друга на две секунды позже, мир не рухнет. А вот если лента не загрузится, пользователь уйдёт.
Архитектор не тот, кто обещает всё сразу. Архитектор тот, кто знает, чем бизнес готов пожертвовать в шторм, и решает это заранее, а не в момент аварии.
А когда шторма нет
Есть продолжение теоремы, которое мне кажется даже более практичным. Его называют PACELC, и смысл простой: даже когда сеть работает нормально, приходится выбирать между скоростью ответа и согласованностью данных. Чтобы все копии данных были идеально одинаковыми, каждая запись должна подтвердиться во всех местах, а это время. Чтобы отвечать мгновенно, нужно позволить копиям ненадолго расходиться.
И здесь начинается самое интересное для бизнеса. Внутри одной системы разные данные могут требовать разных решений. Остаток денег на счёте клиента должен быть строго согласованным. Количество просмотров товара может отставать на минуту. Остаток на складе для интернет-магазина где-то посередине: показывать его можно приблизительно, а вот при оформлении заказа резервировать строго. Умение разложить данные по таким полкам это и есть хорошая архитектура.
Что это значит для Вашего проекта
Скажу честно: большинству небольших и средних систем теорема CAP напрямую не грозит, потому что они живут на одном сервере с одной базой данных, и там согласованность обеспечивается гораздо проще. Я много раз видел, как команды без нужды строили распределённую архитектуру «на вырост», а потом героически боролись с проблемами, которых у них могло просто не быть. Распределённость это не признак зрелости, а инструмент для конкретных задач: огромная нагрузка, географически разнесённые пользователи, жёсткие требования к отказоустойчивости.
Но как только система выходит за пределы одного сервера, или начинает обмениваться данными с внешними сервисами, которые тоже по сути части распределённой системы, эти вопросы становятся неизбежными. И лучше задать их на этапе проектирования, чем обнаружить ответ в момент, когда клиенту пришло два одинаковых счёта.
Вопросы, которые стоит задать заранее
Пройдитесь по ключевым данным Вашей системы и для каждого вида спросите: что хуже, показать слегка устаревшее значение или отказать в операции? Насколько свежими должны быть эти данные: мгновенно, через секунду, через минуту? Что произойдёт с бизнесом, если две части системы какое-то время будут видеть разные значения? Ответы на эти вопросы дают архитектору гораздо больше, чем любое общее «чтобы работало быстро и надёжно».
В Systems мы проектируем такие решения, исходя из того, какие данные действительно критичны для денег и репутации клиента, а не из моды на микросервисы. Иногда правильный ответ это один надёжный сервер и хорошая база данных, иногда распределённая система с продуманными компромиссами. Теорема CAP не запрещает строить хорошие системы. Она просто напоминает, что у каждой хорошей системы есть честно выбранная цена.