Открываем новую серию постов. Смысл такой: берём известный проект, смотрим, как в нём решили какую-нибудь неочевидную проблему, и разбираемся, чем этот приём полезен лично тебе в твоём коде. Без душной теории — только то, что реально пригодится. И начать хочу с истории, которая случалась с каждым, кто хоть раз что-то куда-то сохранял.
Ты сохранил данные. Сервис ответил «готово». Ты выдохнул и пошёл дальше. Знакомо? А теперь неприятная новость: «сервис ответил готово» и «данные действительно на месте» — это два разных события. Между ними есть щель, и данные умеют в неё проваливаться. Причём случается это ровно в тот момент, когда что-то ломается, — то есть в самый неподходящий. Чтобы увидеть эту щель во всей красе, посмотрим, как с ней воюет Kafka. А потом вернёмся к твоему коду, потому что проблема там ровно та же.
Как Kafka бережёт данные. Она хранит каждую порцию сообщений сразу на нескольких серверах: один главный, он принимает записи, остальные — его копии, они за ним повторяют. Звучит надёжно. Но вот ловушка, в которую попадают почти все. Главный сервер принял сообщение, записал у себя, ответил «принято» — и в следующую секунду умер. А копии не успели повторить за ним. На его место встаёт одна из копий, но у неё этого сообщения нет. Всё, сообщение исчезло. А отправитель-то уверен, что всё хорошо, — ему же ответили «принято».
Как эту дыру закрывают. У отправителя есть настройка, которая решает, когда считать сообщение сохранённым. Можно сказать «мне хватит, что его принял главный» — быстро, но это ровно та история с исчезновением. А можно потребовать «считать сохранённым, только когда его повторили и живые копии тоже». Вот тогда появляется настоящая гарантия: даже если главный умрёт, сообщение уже есть на других.
Но и это не всё. Что, если копии отстали и по одной повыпадали, а живым остался только главный? Тогда правило «пусть подтвердят копии» тихо превращается в «пусть подтвердит главный» — и мы снова там же, откуда начали. Поэтому есть ещё одна настройка-предохранитель: «если живых копий осталось меньше, чем нужно, — вообще перестань принимать записи и честно скажи об этом». Лучше отказать сразу, чем сделать вид, что всё сохранилось, и потерять данные молча.
И вот тут главная мысль, ради которой всё затевалось. Это выбор, а не значение по умолчанию. Хочешь надёжность — требуй подтверждения от нескольких копий, но тогда при поломке части серверов запись может встать колом (некому подтверждать). Хочешь, чтобы запись шла всегда, — ослабляешь требования, но растёт шанс что-то потерять. Идеального варианта «для всех» нет. Для денег ты выберешь надёжность и переживёшь, что иногда нельзя записать. Для какой-нибудь статистики — наоборот, пусть пишется всегда, а пара потерянных точек погоды не сделает.
А теперь — зачем тебе это, даже если ты Kafka в глаза не видел. Принцип простой и универсальный: ответ «готово» стоит ровно столько, сколько мест реально успели сохранить твои данные. Так что каждый раз, когда твой код получает «ок» откуда-то извне, спроси себя — «ок» от кого и что за ним стоит.
Пара примеров из обычной жизни. Пишешь в базу, у которой есть копии для чтения. Запись прошла, но копии могли ещё не успеть её получить. Читаешь сразу после записи из копии — а там пусто. Та же щель, вид сбоку. Или дёргаешь чужой сервис по сети и получаешь 200. Это значит «я принял твой запрос», а вовсе не «я довёл дело до конца» — если тебе важен именно результат, надо его проверить, а не верить на слово. И везде, где надёжность важнее скорости, ты за неё платишь — тем, что иногда придётся подождать или получить отказ. Это нормально. Ненормально — надеяться, что «да обычно долетает».
Если совсем коротко: надёжность — это не галочка «включил копии и забыл», а осознанное решение, сколько подтверждений тебе достаточно и чем ты за это готов заплатить. Почти все истории про «данные загадочно пропали» — и в Kafka, и в обычном коде — на самом деле про неправильно понятое слово «готово».
Подписаться: @codeof_art
Вступить в чатик: @code_of_art
Post #327
805
- 🔥 5
- 👍 1
- 🎉 1