Глава 45. Фоновые рабочие процессы#

Глава 45. Фоновые рабочие процессы

Глава 45. Фоновые рабочие процессы

PostgreSQL может быть расширен для выполнения пользовательского кода в отдельных процессах. Такие процессы запускаются, останавливаются и контролируются с помощью postgres, что позволяет им иметь близкую связь с состоянием сервера. Эти процессы присоединяются к общей области памяти Tantor SE-1C и имеют возможность подключаться к базам данных внутренне; они также могут выполнять несколько транзакций последовательно, как обычный сервер, подключенный клиентом процесс. Кроме того, путем связывания с libpq они могут подключаться к серверу и вести себя как обычное клиентское приложение.

Предупреждение

В использовании фоновых рабочих процессов существуют значительные риски в отношении надежности и безопасности, поскольку они написаны на языке C и имеют неограниченный доступ к данным. Администраторы, желающие включить модули, которые включают фоновые рабочие процессы, должны быть особенно осторожны. Разрешено запускать фоновые рабочие процессы только для тщательно проверенных модулей.

Фоновые рабочие процессы могут быть инициализированы во время запуска Tantor SE-1C, включив имя модуля в shared_preload_libraries. Модуль, желающий запустить фоновый рабочий процесс, может зарегистрировать его, вызвав функцию RegisterBackgroundWorker(BackgroundWorker *worker) из своей функции _PG_init(). Фоновые рабочие процессы также могут быть запущены после того, как система уже работает, вызвав функцию RegisterDynamicBackgroundWorker(BackgroundWorker *worker, BackgroundWorkerHandle **handle). В отличие от функции RegisterBackgroundWorker, которую можно вызывать только из процесса постмастер, функцию RegisterDynamicBackgroundWorker необходимо вызывать из обычного backend или другого фонового рабочего процесса.

Структура BackgroundWorker определена следующим образом:

typedef void (*bgworker_main_type)(Datum main_arg);
typedef struct BackgroundWorker
{
    char        bgw_name[BGW_MAXLEN];
    char        bgw_type[BGW_MAXLEN];
    int         bgw_flags;
    BgWorkerStartTime bgw_start_time;
    int         bgw_restart_time;       /* in seconds, or BGW_NEVER_RESTART */
    char        bgw_library_name[BGW_MAXLEN];
    char        bgw_function_name[BGW_MAXLEN];
    Datum       bgw_main_arg;
    char        bgw_extra[BGW_EXTRALEN];
    int         bgw_notify_pid;
} BackgroundWorker;

bgw_name и bgw_type - это строки, которые используются в сообщениях журнала, списках процессов и подобных контекстах. bgw_type должен быть одинаковым для всех фоновых рабочих процессов одного типа, чтобы можно было группировать такие рабочие процессы в списке процессов, например. С другой стороны, bgw_name может содержать дополнительную информацию о конкретном процессе. (Обычно строка для bgw_name будет содержать тип каким-то образом, но это не является строго обязательным).

bgw_flags - это битовая маска, полученная путем побитового ИЛИ, указывающая на возможности, которые модуль хочет получить. Возможные значения:

BGWORKER_SHMEM_ACCESS

Запрашивает доступ к общей памяти. Этот флаг обязателен.

BGWORKER_BACKEND_DATABASE_CONNECTION

Запрашивает возможность установить соединение с базой данных, через которое впоследствии можно будет выполнять транзакции и запросы. Фоновый рабочий процесс, использующий BGWORKER_BACKEND_DATABASE_CONNECTION для подключения к базе данных, также должен присоединить общую память, используя BGWORKER_SHMEM_ACCESS, иначе запуск рабочего процесса будет неудачным.

bgw_start_time - это состояние сервера, в котором postgres должен запустить процесс; это может быть одно из BgWorkerStart_PostmasterStart (запуск сразу после завершения собственной инициализации postgres; процессы, запрашивающие это, не могут получить соединение с базой данных), BgWorkerStart_ConsistentState (запуск сразу после достижения согласованного состояния в горячем резервном экземпляре, позволяющий процессам подключаться к базам данных и выполнять только чтение запросы) и BgWorkerStart_RecoveryFinished (запуск сразу после того, как система перешла в нормальное состояние чтения-записи). Обратите внимание, что последние два значения эквивалентны в сервере, который не является горячим резервным экземпляром. Обратите внимание, что эта настройка указывает только, когда процессы должны быть запущены; они не останавливаются, когда достигается другое состояние.

bgw_restart_time - это интервал, в секундах, который postgres должен ждать перед перезапуском процесса в случае его аварийного завершения. Он может быть любым положительным значением или BGW_NEVER_RESTART, что означает не перезапускать процесс в случае аварийного завершения.

bgw_library_name - это имя библиотеки, в которой будет искаться начальная точка входа для фонового процесса. Указанная библиотека будет динамически загружена рабочим процессом, а bgw_function_name будет использоваться для идентификации вызываемой функции. Если требуется загрузить функцию из основного кода, это значение должно быть установлено в "postgres".

bgw_function_name - это имя функции в динамически загружаемой библиотеке, которая должна быть использована в качестве начальной точки входа для нового фонового рабочего процесса.

bgw_main_arg - это аргумент типа Datum, передаваемый в основную функцию фонового рабочего процесса. Эта основная функция должна принимать единственный аргумент типа Datum и возвращать void. bgw_main_arg будет передан в качестве аргумента. Кроме того, глобальная переменная MyBgworkerEntry указывает на копию структуры BackgroundWorker, переданной при регистрации; рабочий процесс может найти полезным изучить эту структуру.

На Windows (и в любом другом месте, где определен EXEC_BACKEND) или в динамических фоновых процессах небезопасно передавать Datum по ссылке, только по значению. Если требуется аргумент, наиболее безопасно передавать int32 или другое маленькое значение и использовать его в качестве индекса в массиве, выделенном в общей памяти. Если передается значение, такое как cstring или text, то указатель не будет действительным в новом фоновом процессе.

bgw_extra может содержать дополнительные данные, которые будут переданы фоновому рабочему процессу. В отличие от bgw_main_arg, эти данные не передаются в качестве аргумента в основную функцию рабочего процесса, но к ним можно получить доступ через MyBgworkerEntry, как обсуждалось выше.

bgw_notify_pid - это PID процесса PostgreSQL, к которому постмастер должен отправить сигнал SIGUSR1, когда процесс запускается или завершается. Значение должно быть 0 для рабочих процессов, зарегистрированных при запуске постмастера, или когда бэкэнд, регистрирующий рабочий процесс, не желает ожидать запуска рабочего процесса. В противном случае, значение должно быть инициализировано значением MyProcPid.

После запуска процесс может подключиться к базе данных, вызвав функцию BackgroundWorkerInitializeConnection(char *dbname, char *username, uint32 flags)) или BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, uint32 flags). Это позволяет процессу выполнять транзакции и запросы с использованием интерфейса SPI. Если dbname равно NULL или dboid равно InvalidOid, сессия не подключена к конкретной базе данных, но можно получить доступ к общим каталогам. Если username равно NULL или useroid равно InvalidOid, процесс будет выполняться от имени суперпользователя, созданного во время initdb. Если флаг BGWORKER_BYPASS_ALLOWCONN указан в качестве flags, можно обойти ограничение на подключение к базам данных, не разрешающим пользовательские подключения. Фоновый рабочий процесс может вызывать только одну из этих двух функций и только один раз. Невозможно переключиться на другую базу данных.

Сигналы изначально блокируются, когда управление достигает основной функции фонового рабочего процесса, и должны быть разблокированы им; это позволяет процессу настроить свои обработчики сигналов, если необходимо. Сигналы могут быть разблокированы в новом процессе путем вызова функции BackgroundWorkerUnblockSignals и заблокированы путем вызова функции BackgroundWorkerBlockSignals.

Если bgw_restart_time для фонового рабочего процесса настроен как BGW_NEVER_RESTART, или если он завершается с кодом выхода 0 или прерывается с помощью TerminateBackgroundWorker, он будет автоматически отменен регистрацией постмастером при выходе. В противном случае он будет перезапущен после временного периода, настроенного через bgw_restart_time, или немедленно, если постмастер повторно инициализирует кластер из-за сбоя бэкенда. Бэкенды, которым нужно приостановить выполнение только временно, должны использовать прерываемый сон вместо выхода; это можно сделать, вызвав WaitLatch(). Убедитесь, что при вызове этой функции установлен флаг WL_POSTMASTER_DEATH, и проверьте код возврата для немедленного выхода в чрезвычайном случае, когда postgres сам завершается.

Когда фоновый рабочий процесс регистрируется с использованием функции RegisterDynamicBackgroundWorker, возможно, что бэкенд, выполняющий регистрацию, может получить информацию о статусе рабочего процесса. Бэкенды, желающие сделать это, должны передать адрес BackgroundWorkerHandle * в качестве второго аргумента функции RegisterDynamicBackgroundWorker. Если рабочий процесс успешно зарегистрирован, этот указатель будет инициализирован непрозрачным дескриптором, который впоследствии можно передать функциям GetBackgroundWorkerPid(BackgroundWorkerHandle *, pid_t *) или TerminateBackgroundWorker(BackgroundWorkerHandle *). Функция GetBackgroundWorkerPid может использоваться для опроса статуса рабочего процесса: возвращаемое значение BGWH_NOT_YET_STARTED указывает на то, что рабочий процесс еще не был запущен постмастером; BGWH_STOPPED указывает на то, что он был запущен, но больше не работает; и BGWH_STARTED указывает на то, что он в настоящее время работает. В последнем случае PID также будет возвращен через второй аргумент. Функция TerminateBackgroundWorker заставляет постмастер отправить сигнал SIGTERM рабочему процессу, если он работает, и отменить его регистрацию, как только он перестанет работать.

В некоторых случаях процесс, который регистрирует фоновый рабочий процесс, может желать дождаться его запуска. Это можно сделать, инициализировав bgw_notify_pid значением MyProcPid и затем передав BackgroundWorkerHandle *, полученный при регистрации, в функцию WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *). Эта функция будет блокироваться до тех пор, пока постмастер не попытается запустить фоновый рабочий процесс или пока постмастер не умрет. Если фоновый рабочий процесс работает, возвращаемое значение будет BGWH_STARTED, а PID будет записан по указанному адресу. В противном случае возвращаемое значение будет BGWH_STOPPED или BGWH_POSTMASTER_DIED.

Процесс также может ожидать завершения фонового рабочего процесса, используя функцию WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle) и передавая BackgroundWorkerHandle *, полученный при регистрации. Эта функция будет блокировать выполнение до тех пор, пока фоновый рабочий процесс не завершится или не умрет постмастер. При завершении фонового рабочего процесса возвращается значение BGWH_STOPPED, если умирает постмастер, то возвращается BGWH_POSTMASTER_DIED.

Фоновые процессы могут отправлять асинхронные уведомления, либо с помощью команды NOTIFY через SPI, либо непосредственно через функцию Async_Notify(). Такие уведомления будут отправлены при коммите транзакции. Фоновые процессы не должны регистрироваться для получения асинхронных уведомлений с помощью команды LISTEN, так как нет инфраструктуры для обработки таких уведомлений фоновым процессом.

Модуль src/test/modules/worker_spi содержит рабочий пример, демонстрирующий некоторые полезные техники.

Максимальное количество зарегистрированных фоновых процессов ограничено значением max_worker_processes.