Хочется разбавить все мои истории про то как я набыдлокодила и рассказать что я умею не только лишь быдлокодить. Но и отвечать за большой продакшен, где важна внимательность, кропотливость и я даже способна писать тесты!
Я пришла в Яндекс.Облако в 2018 году. Тогда Облако для внешних пользователей (Yandex.Cloud) только что запустили. Но к этому моменту уже давно существовала внутренняя инсталляция. Кому интересно можно послушать мой рассказ про это тут.
Так вот, я занималась postgres'ом (до конца своих дней в Яндексе, с 2018 по 2025 год). И мы предоставляли инфру для внутренних заказчиков, проектов Яндекса, там почти все сервисы: Почта, Диск, Драйв, Такси, Маркет и пр. С некоторыми сервисами мы дружили в дёсна, и не только предоставляли им базы данных как сервис, но и были DBA as a Service и разработчиками. Один из таких сервисов достался мне.
В Yandex.Cloud он называется Object Storage. Внутри это просто S3. Одна из критичных частей инфраструктуры, потому что им (как и нами) пользуются почти все сервисы Яндекса. Через S3 (правда за дополнительным слоем кеширования) раздаётся статика на главной странице Поиска и остальных страницах. На тот момент когда мне достался этот сервис, он был критичным, но в каком-то смысле "умирающим", потому что они с постгреса должны были переехать на YDB. Вот в Yandex.Cloud (внешней инсталляции) уже переехали. В общем мне достался сервис который одной ногой уже не у нас. Типа на поддержку мне дали, пока окончательно не уедут. Но случилось иначе.
А пока 2019 год. Логика S3 почти вся на хранимках. Бекенд там довольно "тупой", он просто ходит вызывает хранимки в базе. Под капотом шардированный постгрес, где в основную базу прилетает 150 тыс RPS (запросов в секунду). И репликация естественно там тоже есть. В некоторых шардах по 5 реплик было. Одна база — 2.5 Тб.
Для иллюстрации был момент когда я неудачно катнула миграцию, и она заблокировала на запись один из 8ми шардов (на тот момент их было 8, сейчас 2 инсталляции по 128 шардов). Индцидент длился меньше 5ти минут. За это время к инцидентному тикету привязали в районе 50ти инцидентных тикетов сервисов, которые пострадали. 10 тикетов в минуту. Плюс иногда к нам приходят за каждой 500кой. А вот мы там сделали GET а вы ответили 500, объясните ситуацию! И я напомню что этих GET'ов нам приходит сотня тысяч в секунду.
Все обновления кода, все обновления базы данных, мажорные и минорные, все новые фичи, все миграции на базу данных (которые довольно большие и нагруженные), нужно было разрабатывать и катать в такой напряжённой обстановке. Стоит ли говорить что там не было никаких докеров, никаких рестартов с ноги, никаких CI/CD и всё то что модно? Оно просто не применимо. Никаких автовыкаток. Когда ты за каждую секунду простоя "теряешь деньги", то ты уж будешь консервативен. Если нужно катнуть код, то ты его катишь в несколько этапов, миграция может состоять из 5ти частей, которые катаются на разные компоненты, в той последовательности чтобы ни одной секунды не просыпать, выкатка должна быть плавной. Ещё и по шардам катаешь последовательно. Занимает это часа 4. Начинать надо на спаде нагрузки после 8ми вечера, а желательно после 9ти, предупреждая коллег из сервиса и договариваясь чтобы кто-то ещё подстраховывал. В итоге до 12ти сидишь и смотришь на графики, делая всё аккуратно. И заглядывая в яндексовые чаты, не заметил ли кто чего.
В общем в 2019 мне этот сервис достался "выезжающим", а потом оказалось что он мало того что не выезжает, так он и второй ногой к нам заезжает обратно. Вместо того чтобы ехать на YDB решили облачную инсталляцию из YDB обратно перевезти на postgres. Я занималась этим переездом, который был сопряжён с рядом разработок, пожалуй основная и самая ответственная — это биллинг. Во внутреннем облаке биллинга нет, там всё типа бесплатно (было), а во внешнем деньги надо считать. Этот биллинг написала я. Типа это штука которая считает деньги за каждую байто-секунду. Положили 1 килобайт, через час его удалили, надо забиллить ровно за столько. Я (знаменитый быдлокодер) написала весь биллинг для сервиса Object Storage в Yandex Cloud, через который каждый день проходит по несколько миллионов рублей. Несколько лет я была главной ответственной за базы данных под S3.
Можете прикинуть какая тут цена ошибки. Нууу я думаю абсолютное большинство среди тех кто считает что я делаю что-то некачественно, не так как подобает приличным инженерам, никогда не сталкивался и не столкнётся с таким объёмом ответственности. Тут тебе и деньги, и нагрузка, и критичность, потерять ни одного байтика данных недопустимо, и на чтение сервис должен работать всегда! Это же S3, на него многие опираются. Как и на сами базы данных, кстати. Мало есть мест где можно получить такой опыт.
Ну и напоследок расскажу весёлую историю про то как мы нашли баг в постгресе.
Есть у нас такая штука в S3 под названием "счётчики", это денормализация в которой хранится то, в каких бакетах и чанках (часть бакета) сколько хранится объектов и их суммарный объём. Держать эти счётчики в консистентном состоянии — это та ещё задача! Счётчики влияют на биллинг и на квоты. У нас есть проверки на консистентность, которые обходят реальные данные и данные в счётчиках и зажигают мониторинг если они не совпадают даже на байт.
И вот в 2020 году, в ночь с пятницы на субботу, мне приходит смс от мониторинга о том что разошлись счётчики. Я спросоня прочитала и думаю ладно утром посмотрю. Потом мне в эту ночь снилось то что злоумышленники зашли в мою базку, проапдейтили там себе счётчики чтобы меньше платить за S3)) Утром я полезла смотреть. Гляжу: потерялась пара чанков с объектами (один чанк примерно 60 тыс объектов в S3). Выглядело это так: я делаю SELECT, а он не возвращает ничего, но по данным счётчика объекты там должны быть. Если их запросит пользователь, то ему вернётся 404. Выключаю поиск по индексам (SET enable_indexscan TO off и остальные тоже), и вижу в селекте строчки! В общем они из индекса пропали. А причина была в редком баге (настолько редком что никто в мире до нас его не обнаружил, а был он во всех мажорных постгерсах начиная с древних времён), race condition который стреляет при некотором стечении обстоятельств (одновременный перенос данных через двухфайзный коммит и CREATE INDEX CONCURRENTLY), и то редко. На куче шардов и куче времени когда постоянно происходят эти параллельные процессы, мы увидели это лишь пару-тройку раз за несколько лет.
В общем поймала я багу и отдала её Андрею Бородину, контрибьютеру postgres'а из нашей команды, он воспроизвёл и исправил. Фикс был настолько важный, что его не просто закоммитили в следующий мажор, но и бекпортировали во все следующие миноры предыдущих мажоров вплоть до устаревших версий. Фикс состоял из трёх основных коммитов.
А поймали мы эту багу благодаря тому что у нас высокие требования к консистентности и надёжности, всё обмазано мониторингами, и это помножено на высокую нагрузку и большой объём данных. Поэтому я умею не только навасянить MVP. Все инструменты и подходы нужно применять там где они уместны. Я могу и так и так. Главное — здравый смысл. Но, признаюсь, всё-таки тяготею я к быстрым результатам. Однако масштабировать и развивать большую критическую инфраструктуру я тоже могу.