Познакомился недавно чуть подробнее с концепцией хуков в PostgreSQL. Начал читать разные статьи про это. Статья от arctype.com понравилась, решил её перевести. Вот что получилось (есть кое-какие огрехи, но понимание статьи не влияет).
Ссылка на оригинал.
Хуки Постгреса — это мощный способ модифицировать работу сервера. На примерах популярных проектов мы посмотрим, как их использовать.
Что имеют в виду разработчики, когда говорят, что Постгрес «расширяем»? Они имеют в виду низкоуровневые API, которые могут изменить основные функции (возможности ядра) базы данных. В этом посте мы рассмотрим секретную, то есть недокументированную, возможность, называемую хуками, которая позволяет разработчикам не только добавлять функции в Постгрес, но и изменять способ выполнения запросов и анализа данных.
Хуки используются некоторыми из самых популярных проектов в экосистеме Постгрес, включая Timescale, pg_stat_statements и Supabase. Чтобы дать вам представление о том, что же можно с ними сделать, мы увидим, как в каждом из этих проектов используются определенные хуки. В конце статьи мы предоставим Makefile [хм, нет, не предоставили], немного кода на C и инструкции по компиляции, которые помогут вам приступить к самостоятельной модификации Постгреса.
Что такое хуки?
Хуки — это функции, основанные на событиях, присутствующие в Постгресе. Мы можем создать хук на основе события/триггера, и база данных будет его вызывать. Хуки широко используются многими библиотеками и проектами, но все они имеют одну и ту же базовую структуру.
Структура
Хук Постгреса начинается с включения заголовочных файлов и использования некоторых стандартных обёрток, о которых знает механизм базы данных.
include "postgres.h" // Код функции, которую будем вызывать с помощью хука static void custom_hook() // Все хуки вызываются из этой функции void _PG_init(void); // Логика хуков заканчивается в этой функции void _PG_fini(void);
Инициализация
Хуки необходимо поместить в папку исходного кода contrib Постгреса. Здесь вы можете проверить существующие расширения, уже поставляемые с Постгресом.
Как только код хука будет готов, вы сможете собрать его из исходного кода или использовать его отдельно. Создание хука как расширения выходит за рамки этой статьи, но именно так его используют все три проекта, описанные здесь. Как только плагин будет установлен, вы можете загрузить его, используя команду LOAD «имя_плагина», чтобы протестировать.
Типы хуков
Существует около 30 хуков в 6 категориях, которые можно вызвать в Си. Проект psql-hooks на Github представляет собой неофициальную документацию всех этих функций:
- Общие хуки — хуки, которые работают с общими функциями Постгрес .
- Хуки безопасности — хуки со специальными функциями безопасности, например, создание пароля и пользователя.
- Хуки функций — работают во время выполнения функции.
- Хуки планировщика — используются на этапе планирования. Например, Timescale использует planner_hook для изменения плана выполнения запроса.
- Хуки выполнения запросов — перехват на этапе выполнения. Например, pg_stat_statements использует ExecutorRun_hook для проверки выполняемых запросов, а Supabase использует ProcessUtility_hook для реализации безопасности на основе ролей.
- Хуки PL/pgSQL— удобно использовать при работе с функциями PL/pgSQL.
Лучшая документация — с примерами, поэтому давайте рассмотрим, как Timescale, pg_stat_statements и Supabase используют хуки. Для бо́льшего удовольствия прочитайте связанные с этими проектами файлы Cи (ссылки есть ниже), где вы найдете полезные советы от их разработчиков о том, как использовать эти низкоуровневые API.
Хуки в Timescale
TimescaleDB расширяет Постгрес, «добавляя хуки глубоко в планировщик запросов, модели данных и хранения, а также механизм выполнения запросов». Это дает нам подсказку о том, к каким частям архитектуры базы данных они подключаются. В частности, Timescale имеет дело с временными рядами во фрагментах (chunk, автоматически управляемых секциях таблицы), минимальных кусках памяти, в которых хранится непрерывный набор дат, времени, событий или других данных временных рядов. Вторая основная функция — непрерывные агрегаты, где фрагменты обновляются по мере поступления новых данных. Наконец, Timescale имеет несколько очень интересных функций планирования, которые опять же напрямую реализованы в Постгрес с помощью хуков. Теперь, когда мы знаем, какие типы хуков существуют, мы можем изучить открытую базу кода Timescale и выяснить, как они используются для создания вышеперечисленных функций.
Планирование запросов и их выполнение
Самый важный хук для Timescale, вероятно, — это planner_hook, который имеет дело с планом запроса, который создает Постгрес, когда пользователь или приложение отправляет ему оператор SQL. В src/planner.c мы видим, как они используют функцию timescaledb_planner для изменения стандартного плана запроса, включив в него фрагменты и гарантируя, что HyperTables запрашиваются правильно. После завершения работы планировщика Timescale выполняет дальнейшие преобразования запроса, используя post_parse_analyze_hook, который они инициализируют в самом начале src/loader/loader.c.
Аналогично, в src/process_utility.c хук ProcessUtility используется для изменения механизма выполнения запроса Постгресом после создания плана. Этот файл, содержащий более 4000 строк языка C, гарантирует правильное выполнение таких операторов, как ALTER TABLE и использование уникальных функций сжатия Timescale для достижения максимального эффекта.
Дополнительные возможности
Помимо планирования и выполнения, есть еще два интересных хука, которые помогают Timescale обеспечивать плавные обновления и тестировать их расширения. shmem_startup_hook срабатывает при инициализации и используется для обеспечения того, чтобы работники имели правильную версию расширения во время обновлений, чтобы планирование выполнялось правильно и для других задач. И вся эта функциональность не могла бы обойтись без тестирования, поэтому emmit_log_hook используется в src/bgw/log.c для добавления журналов в систему автоматического тестирования.
Хуки в pg_stat_statements
Citus Data назвала pg_stat_statements самым полезным расширением Постгреса. Это один из лучших инструментов для мониторинга производительности Постгреса, и его легко включить. pg_stat_statements активно использует ExecutorStart_hook, ExecutorRun_hook, ExecutorFinish_hook и ExecutorEnd_hook, поскольку ему нужна информация о характеристиках выполняемого запроса. Вот пример из pg_stat_statements.c, где мы отслеживаем общее время выполнения запроса.
/* * ExecutorStart хук: запустить отслеживание, если необходимо */ static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags) { if (prev_ExecutorStart) prev_ExecutorStart(queryDesc, eflags); else standard_ExecutorStart(queryDesc, eflags); /* * Если запрос имеет нулевой queryId, не отслеживайте его. Это предотвращает двойной * подсчет оптимизируемых операторов, непосредственно содержащихся в * дополнительных определниях. */ if (pgss_enabled(exec_nested_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0)) { /* * Настройте отслеживание общего затраченного времени в ExecutorRun. Убедитесь, что * пространство выделяется в контексте каждого запроса, поэтому оно исчезнет в * ExecutorEnd. */ if (queryDesc->totaltime == NULL) { MemoryContext oldcxt; oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt); queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false); MemoryContextSwitchTo(oldcxt); } } }
Хуки в Supabase
Мы могли бы подробно рассказать, как Supabase — Firebase с открытым исходным кодом, реализованная в Постгресе, — использует хуки, но они сами сделали это в отличной статье в блоге о защите зарезервированных ролей в Постгрес. Достаточно сказать, что они используют только один хук, ProcessUtility_hook, для перехвата глобального указателя, который срабатывает непосредственно перед выполнением каждого оператора SQL.
Supabase использует ProcessUtility для перехода к supautils.c, библиотеке, которая проверяет, разрешено ли роли, выполняющей оператор, выполнять его. Это помогает им сопоставить веб-аутентификацию на сайте supabase.com с ролью базы данных внутри Постгреса и обеспечить очень плавную аутентификацию. Помимо чтения Си кода pg_stat_statements, их сообщение в блоге и файл supautils.c являются лучшим справочником по реализации умеренно сложного хука.
Полностью рабочий пример
Давайте посмотрим на полностью рабочий пример, который можно найти в репозитории Github здесь. Примечание: Лицензию для приведенного ниже кода можно найти тут.
//Инициализация переменных хука static ClientAuthentication_hook_type original_client_auth_hook = NULL; //Код функции, которую будем вызывать с помощью хука static void auth_delay_checks(Port *port, int status) { if (original_client_auth_hook) original_client_auth_hook(port, status); if (status != STATUS_OK) { pg_usleep(1000000L); } } //Пользовательская функция вызывается в _PG_init-методе void _PG_init(void) { original_client_auth_hook = ClientAuthentication_hook; ClientAuthentication_hook = auth_delay_checks; } //Вернём всё как было, как только работа нашей функции будет завершена void _PG_fini(void) { ClientAuthentication_hook = original_client_auth_hook; }
Приведенный выше фрагмент кода представляет собой элементарную демонстрацию хука. Если аутентификация неверна, перед выдачей ошибки запускается пауза в 1 секунду. Этот код можно использовать в качестве средства защиты от подбора пароля (это всего лишь пример, а не рекомендуемая функция/стратегия безопасности).
Другие архитектуры на основе событий для Постгрес
Существуют и другие архитектуры для сбора событий из Постгреса. Например, популярный Apache Kafka, использующий Debezium. У Постгреса есть журнал фиксации транзакций, который можно отслеживать и реплицировать между серверами для резервного копирования и организации экземпляров только для чтения.
Вывод
Хуки — продвинутая концепция. Обычный пользователь базы данных никогда не будет использовать их в повседневной работе, но интересно понять, как они работают, потому что никогда не знаешь, когда они могут пригодиться. Кроме того, некоторые библиотеки уже используют их, поскольку им необходимо подключаться к различным метрикам.
Примерная стратегия, которую вы можете использовать, чтобы определить, подходят ли хуки для вашего проекта, включает следующие моменты:
- У вас используются события?
- Это невозможно сделать на стороне приложения, а только на стороне базы данных. Это нормально?
- Можем ли мы не использовать инструменты CDC (Change Data Capture) для решения этой проблемы?
- Есть ли у вас знания Си и необходимые знания о внутреннем устройстве Постгреса?
- Есть ли у вас возможность упаковать Постгрес как локальную установку? Облачные установки и службы, такие как RDS/CloudSQL, не будут иметь такой возможности.
Если на эти вопросы даны ответы, хуки станут отличным вариантом.
Ещё раз ссылка на оригинал.
Leave a Reply