1. Background Worker 简介
PostgreSQL 提供了一种叫作后台工作进程(Background Worker)的特性,用来在独立的进程中执行用户提供的代码,入口函数可以是 pg 内核源码中的函数,也可以是动态库中的函数。这些后台工作进程被 postmaster 启动、停止和监控,他们的生命周期与 pg server 密切联系。
Background Worker 在创建时能够通过选项(flags)获取访问 pg 共享内存区域的能力,也能够以内部方式连接到 pg 数据库。它们可以执行事务和查询,就像是一个正常连接 pg 的客户端,另外通过 psql,他们能够连接其他 pg server,就像是一个普通的客户端应用程序。
2. Background Worker 启动
bg worker 可以在 pg 启动时,通过参数 shared_preload_libraries 初始化。在动态库的 _PG_init() 函数中调用 RegisterBackgroundWorker(BackgroundWorker *worker) 进行注册。
如果 pg 已经启动,此时可以通过 RegisterDynamicBackgroundWorker(BackgroundWorker *worker,BackgroundWorkerHandle **handle)函数进行启动。此方式可以在任何一个 backend 进程或者另外一个 background worker 中启动。
3. 数据结构 BackgroundWorker
结构体 BackgroundWorker 定义了一个 Background Worker 的基本信息,如下:
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];
pid_t bgw_notify_pid; /* SIGUSR1 this backend on start/stop */
} BackgroundWorker;
bgw_name 和 bgw_type 用于日志消息、进程列表以及类似的上下文。bgw_name 包含名称信息,对于同一个类型的 bg worker,其 bgw_type 应当保持一致,这样的 bg workers 可以作为一组进程列表。
bgw_flags 标识相对应的能力,可选取值:
- BGWORKER_SHMEM_ACCESS,访问共享内存的能力,包括重量级锁,轻量锁,共享内存,以及其他自定义数据结构等。
- BGWORKER_BACKEND_DATABASE_CONNECTION,建立与数据库连接的能力,能够执行事务和查询。由于建立数据库连接需要访问共享内存,因此必须与 BGWORKER_SHMEM_ACCESS 一起使用,否则启动失败。
bgw_start_time 标识 postmaster 在什么状态下启动 bg worker 进程,可选值为:
- gWorkerStart_PostmasterStart,在 postmaster 初始化之后,尽快启动,这时还没有用户连接过来。
- BgWorkerStart_ConsistentState,在热备节点上达到一致性状态时,启动进程。允许进程连接数据库,执行只读查询。
- BgWorkerStart_RecoveryFinished,在系统进入正常的读写状态后启动。
bgw_restart_time 是一个整数值,单位为秒,在 postmaster 重启 bg worker 进程时,需要等待的秒数,可以指定为 BGW_NEVER_RESTART,标识从不重启。
bgw_library_name,标识动态加载的动态库名称,如果是从 pg 内核代码加载一个函数,则指定为 postgres。
bgw_function_name,标识入口函数名称,它是 bg worker 进程的执行入口点。
bgw_main_arg 是一个 Datum 类型的参数,传给 bg worker 的入口函数。
bgw_extra 包含额外的数据,传递给 bg worker,可通过 MyBgworkerEntry 访问这些数据。
bgw_notify_pid 是一个 pg backend 进程 ID,在 bg worker 进程启动或者退出,postmaster 会发送 SIGUSR1 信号给这个进程 ID。在 postmaster 启动时注册 bg worker 时,或者 backend 注册 bg worker 且并不想等待它完成时,该参数应该为0。
4. 连接数据库
如果 bg worker 想连接数据库,需要调用如下函数:
BackgroundWorkerInitializeConnection(char *dbname, char *username,uint32 flags)
或者
BackgroundWorkerInitializeConnectionByOid(Oid dboid,Oid useroid, uint32 flags)
这两个函数允许使用 SPI 接口执行事务和查询。
5. 信号处理
关于 bg worker 的信号处理,默认是阻塞的,可通过如下两个函数进行设置。
- BackgroundWorkerUnblockSignals
- BackgroundWorkerBlockSignals
6. backend 与 bg worker 的交互
backend 创建 bg worker 之后,会获取一个对应的 handle,通过 handle 可以获取 bg worker的相关信息以及停止 bg worker:
- GetBackgroundWorkerPid(BackgroundWorkerHandle *,pid_t *)
- TerminateBackgroundWorker(BackgroundWorkerHandle *)
等待 bg worker 启动或者停止完成:
- 设置 bgw_notify_pid 为 MyProcPid
- WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle,pid_t *)
- WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
7. 最后
pg 中能够注册的最大 bg worker 数量由参数 max_worker_processes 限制。
关于 bg worker 的示例,见源码如下目录:src/test/modules/worker_spi