Chapter 45. 后台工作者进程

PostgreSQL可以被扩展来在独立进程中运行用户提供的代码。这种进程被postgres启动、停止和监控,这使它们的生命期与服务器的状态紧密联系。这些进程具有选项可以挂接上PostgreSQL的共享内存区域,并且可以从内部连接到数据库。它们也可以连续地运行多个事务,就像一个正常的被客户端连接的服务器进程。同样,通过链接到libpq,它们可以连接到服务器并像一个正常客户端应用工作。

Warning

在使用后台工作者进程时具有相当大的鲁棒性和安全性风险,因为它们由C语言编写,对数据具有无限制的访问权。希望使用包括后台工作者进程在内的模块的管理员必须要极度小心。只有仔细审计过的模块才会被允许运行后台工作者进程。

只有列在shared_preload_libraries中的模块才能运行后台工作者。一个希望运行后台工作者的模块需要通过在其_PG_init()中调用RegisterBackgroundWorker(BackgroundWorker *worker)来注册它。这样BackgroundWorker的结构将被定义:

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

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_main是进程被启动后要运行的函数指针。该函数必须有一个类型为Datum的单一参数并返回voidbgw_main_arg将被传递给它作为它唯一的参数。注意全局变量MyBgworkerEntry指向在注册阶段被传递的BackgroundWorker结构副本。

一旦运行起来,进程可以通过调用BackgroundWorkerInitializeConnection(char *dbname, char *username)来连接到一个数据库。这使得该进程可以使用SPI接口运行事务和查询。如果dbname为NULL,该会话没有连接到任何特定数据库,但共享的目录可以被访问。如果username为NULL,该进程将以在initdb阶段创建的超级用户身份运行。在每一个后台进程中,BackgroundWorkerInitializeConnection只能被调用一次,所以不可能切换数据库。

当控制到达bgw_main函数时,信号初始会被阻塞,并且必须被它解除阻塞。这是为了允许进程自定义它的信号处理器。在新进程中可以通过调用BackgroundWorkerUnblockSignals来解除对信号的阻塞,还可以通过调用BackgroundWorkerBlockSignals来阻塞信号。

我们期望后台工作者能持续运行,如果它们干净地退出,postgres将会立刻重启它们。当它们无事可做时考虑进行可中断的休眠,这可以通过调用WaitLatch()实现。调用该函数时要确保WL_POSTMASTER_DEATH标志被设置,并且验证在postgres本身被终止的紧急情况下产生的快速退出返回码。

worker_spi贡献模块包含一个工作例子,它证明了一些有用的技术。