Недавно я получил статус Major Contributor в проекте PostgreSQL. Это довольно радостное для меня событие и интересное, поэтому коллеги попросили написать статью об этом. А чтобы я не сомневался — заботливо составили список достижений за меня. Получилось замечательно, но публиковать от своего имени статью вида «как я крут» я не хочу. Я совсем не против про это говорить, и из каждого утюга вещаю про разные технологии, сделанные моей командой или вот прям вообще мной. Но только в контексте «как использовать эти технологии», либо в узком кругу или личной беседе.
Я решил написать другую статью: что у меня не получилось. Писал довольно спешно, поэтому, возможно, местами будет понятно только специалистам. Не расстраивайтесь, если что‑то неясно и пришлось гуглить. А вот если всё понятно — возможно, стоит меньше смотреть в монитор и чаще трогать траву.
Инкрементальное улучшение любой популярной технологии зачастую имеет негативные последствия. И в большинстве случаев предлагаемых в PostgreSQL доработок — вред превышает пользу. Построить что‑то новое, ничего не сломав, бывает трудно и в чистом поле, а ядро PostgreSQL в этом смысле — лабиринт с граблями.
Из всего, что я обещал сделать пользователям в Яндексе, клиентам Yandex Cloud, сотрудникам технической поддержки (и особенно Паше aka Amatol), своим руководителям и своим сотрудникам — из всего этого я сделал, по ощущениям, меньше трети. Большинство приключений конечной точкой имеют несколько технических тупиков, иногда я возвращаюсь к старым делам и нахожу новый путь. Чтобы оказаться в тупике подальше.
На этом пути я не один такой, достаточно вспомнить долгострой zheap, 64-битный счётчик транзакций, TDE, инкрементальные материализованные представления и много‑много других проектов поменьше.
Что же не получилось у меня на пути от первого сообщения в pgsql‑hackers до статуса Major Contributor?
Слияние страниц в B-дереве
До работы в Яндексе я специализировался на индексах. В первую же неделю моей работы мы разбирали различные проекты, которые Володя Бородин хотел поручить мне продвигать. Там были технологии резервного копирования, средства мониторинга и диагностики, управление трафиком, некоторые оптимизации — всё то, что он видел нужными компонентами создаваемой им платформы данных. На будущем сайте Yandex Cloud тогда было только видео с крутящимися вентиляторами серверов, а внутри Яндекса его продуктом уже пользовались!
И вот одной из особенностей создававшегося тогда PostgreSQL‑as‑a‑Service было регулярное пятничное перестроение B‑tree индексов с накопленным bloat. Тогда речь шла о десятках терабайт регулярно перестраиваемых индексов. Думаю, сейчас счёт уже идёт на петабайты.
Одна из причин того, что после очистки от удалённых данных страницы индекса оказываются расположены неоптимально — отсутствие в PostgreSQL реализации механизма «объединения» страниц B‑дерева. Алгоритм этой операции описан в статье «A symmetric concurrent b‑tree algorithm».
B B‑дереве алгоритм этот довольно простой, его две стадии иллюстрируются вот такой вот диаграммой из статьи:

Конечно же, я знал про статью и оценил реализацию этого алгоритма в два дня работы. Искренне был уверен, что взял время с запасом. Но коллеги из сообщества разработчиков PostgreSQL сказали, что, скорее всего, ничего работать не будет, алгоритм плохо совместим с нашей реализацией B‑дерева. Я много раз возвращался к этому проекту, но пока упираюсь в два структурных ограничения:
Алгоритм, предложенный учёными в модели pin‑ов PostgreSQL не даёт гарантии того, что IndexScan увидит какую‑либо индексируемую строчку не более одного раза.
В PostgreSQL существует Backward scan, который может пропустить часть строк в гонке с объединением страниц.
Подробно последний подход к этой проблеме можно почитать тут.
Сжатие протокола репликации
В Яндексе принята модель резервирования на случай отказа «−1 дата‑центр». Поэтому базы PostgreSQL в Яндексе всегда распределены между дата‑центрами. А значит, каждое изменение в пишущем узле базы данных (Replication Primary) необходимо передавать на узел горячего резерва (Standby), который может выполнять читающие запросы.
По нашим экспериментам сжатие протокола репликации могло бы повысить нам производительность репликации в 20 раз!
И самое многообещающее обстоятельство этого вопроса: когда я задался вопросом об этом, в сообществе уже был готовый патч Константина Книжника!
Мы с командой подключились к обсуждению, от нас решением занимался Даниил Захлыстов. Со временем у автора оригинального патча интерес к вопросу, кажется, иссяк, и Даниил предпринял попытку оживить работу.
Что же было препятствием для этой технологии? Кажется, что на ранних этапах сильный тормозящий импульс имели соображения безопасности. Например, в TLS сжатие данных относительно недавно удалили. Почему? Потому что сжатие потенциально раскрывает криптографию. Этот эффект известен как CRIME. Если смешать секретные данные и данные, контролируемые нападающим, — сжатие данных длиной сетевого пакета будет выдавать, насколько данные нападающего похожи на секретные данные.
Но MySQL и Oracle в своём протоколе имеют сжатие данных, оно там появилось ещё до того, как CRIME был открыт. И эти базы данных не спешат удалять сжатие протокола: оно очень полезно, а эксплуатация CRIME крайне затруднительна. Да и разновидность CRIME есть в PostgreSQL в виде сжатия WAL. Именно поэтому управление WAL compression доступно только суперпользователю, который должен оценить риски.
К тому времени, как сообщество пришло к консенсусу, что технология достаточно нужна, чтобы принять эти риски, — энергии авторов патча уже было недостаточно, чтобы продолжить работу над ним. Возможно, мы ещё вернёмся к этой теме.
Крайние случаи гарантий синхронной репликации
Гарантии того, что ни один байт не будет потерян в случае потери узла кластера, основаны на синхронной репликации. Фиксация транзакции клиенту подтверждается только после того, как реплики получили все данные транзакции. В случае временного перебоя связи клиенту приходится дожидаться подтверждения, потенциально довольно долго.

Почему нужно ждать? Потому что другие узлы кластера могут сформировать новый кластер и больше не принять транзакцию.
Однако клиенту доступна отмена транзакции, после чего эффект её становится видимым, и клиент может ошибочно подумать, что изменения данных произошли успешно.
Про это у нас с Женей Дюковым есть подробный доклад на Highload и доклад на FOSDEM.
Этот вопрос неоднократно обсуждался с сообществом. Крупные облака (AWS RDS, Azure, Yandex Cloud) используют патч, функционально эквивалентный тому, что мы предложили сообществу.
Но убедить коммиттеров в необходимости запретить отмену нереплицированного запроса я пока не смог.
Ускорение компрессии pglz
В 2019 году за обедом я рассказал Володе Лескову о том, как в ClickHouse ускорили декомпрессию lz4 при помощи ансамбля аппаратно‑зависимых методов оптимизации.
Володя увлекается олимпиадами и имеет медаль с чемпионата мира ACM ICPC. На идеи перекладывать байты более крупными фиксированными пачками он сказал что‑то в духе: «Асимптотически такие оптимизации ничего не меняют. Надо просто перекладывать байты расширяющимся диапазоном». Когда что‑то действительно просто, олимпиадники не говорят, что это просто — они пытаются показать пантомимой или вспомнить подходящий анекдот. А вот если они сказали «просто» — скорее всего, это что‑то, что можно найти в научных статьях или профильных форумах. Идею мы реализовали сразу после обеда, и она стала частью PostgreSQL, ускоряя декомпрессию кодека pglz.
Но Володя не остановился. В воскресенье, пока команда, которую он тренирует, писала контест, он написал аналогичное ускорение для сжатия.
Но эта оптимизация получилась громоздкой, и строчек кода там слишком много.

В результате обсуждения мы пришли к тому, что надо оптимизацию разделить на четыре части. Но интереса и энергии уже не хватает, чтобы это сделать: Володя занимается совсем другими вещами, а у меня не хватает мозгов, чтобы разобраться в деталях кода в патче.
BFS vs DFS
Это лишь малая часть проектов, результат которых — нагретый воздух и чуть‑чуть улучшенная модель процессов базы данных в головах разработчиков. Как сказал коллега Кирилл Решке: «Теперь ты меньше не понимаешь Postgres».
Из этих проектов может показаться, что где‑то просто надо было быть настойчивее, выбирать меньшее направлений разработки, но зато в большем количестве случаев доводить проект до некоторого успеха, когда польза от разработки превышает вред и её можно использовать. Меня и самого иногда пугает, что некоторые проекты выглядят как известный мем:

Но я убеждён, что аналогия с выкапыванием туннелей тут подходит плохо. Большинство незавершённых проектов создают инфраструктуру для того, чтобы какие‑то другие проекты могли завершиться и причинить пользу.
Говорят: «дорогу осилит идущий». Так вот, лабиринт с граблями тоже строится поступательно, но во многих направлениях сразу.