支持的版本: 当前 (17) / 16 / 15 / 14 / 13
开发版本: 开发版
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3

第46章 背景工作进程

PostgreSQL 可以扩展为在单独的进程中运行用户提供的代码。 这些进程由 postgres 启动、停止和监控,这允许它们具有与服务器状态密切相关的生命周期。这些进程附加到 PostgreSQL 的共享内存区域,并且可以选择在内部连接到数据库;它们还可以像常规的客户端连接服务器进程一样,串行运行多个事务。 此外,通过链接到 libpq,它们可以连接到服务器并像常规客户端应用程序一样运行。

警告

使用后台工作进程存在相当大的健壮性和安全风险,因为它们是用 C 语言编写的,可以无限制地访问数据。 希望启用包含后台工作进程的模块的管理员应格外谨慎。 只应允许经过仔细审核的模块运行后台工作进程。

可以通过在 shared_preload_libraries 中包含模块名称,在 PostgreSQL 启动时初始化后台工作进程。 希望运行后台工作进程的模块可以通过从其 _PG_init() 函数调用 RegisterBackgroundWorker(BackgroundWorker *worker) 来注册它。 还可以通过调用 RegisterDynamicBackgroundWorker(BackgroundWorker *worker, BackgroundWorkerHandle **handle) 在系统启动并运行后启动后台工作进程。 与只能从 postmaster 进程中调用的 RegisterBackgroundWorker 不同,RegisterDynamicBackgroundWorker 必须从常规后端或其他后台工作进程中调用。

结构体 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[MAXPGPATH];
    char        bgw_function_name[BGW_MAXLEN];
    Datum       bgw_main_arg;
    char        bgw_extra[BGW_EXTRALEN];
    pid_t       bgw_notify_pid;
} BackgroundWorker;

bgw_namebgw_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_timepostgres 应该启动进程的服务器状态;它可以是 BgWorkerStart_PostmasterStart(一旦 postgres 本身完成其自身的初始化就立即启动;请求此项的进程没有资格进行数据库连接)、BgWorkerStart_ConsistentState(一旦在热备用中达到一致状态就立即启动,允许进程连接到数据库并运行只读查询)和 BgWorkerStart_RecoveryFinished(一旦系统进入正常的读写状态就立即启动)。 请注意,在非热备用的服务器中,最后两个值是等效的。 请注意,此设置仅指示何时启动进程;当达到不同的状态时,它们不会停止。

bgw_restart_timepostgres 在进程崩溃时应等待重新启动进程的时间间隔(以秒为单位)。 它可以是任何正值,也可以是 BGW_NEVER_RESTART,表示在发生崩溃的情况下不重新启动进程。

bgw_library_name 是一个库的名称,应在其中查找后台工作进程的初始入口点。 指定的库将由工作进程动态加载,并且 bgw_function_name 将用于标识要调用的函数。 如果调用核心代码中的函数,则必须将其设置为 "postgres"

bgw_function_name 是用作新后台工作进程初始入口点的函数的名称。 如果此函数在动态加载的库中,则必须将其标记为 PGDLLEXPORT(而不是 static)。

bgw_main_arg 是后台工作进程主函数的 Datum 参数。 此主函数应采用 Datum 类型的单个参数并返回 voidbgw_main_arg 将作为参数传递。 此外,全局变量 MyBgworkerEntry 指向注册时传递的 BackgroundWorker 结构的副本;工作进程可能会发现检查此结构很有帮助。

在 Windows(以及定义 EXEC_BACKEND 的任何其他位置)或动态后台工作进程中,通过引用传递 Datum 是不安全的,只能通过值传递。 如果需要参数,最安全的方法是传递 int32 或其他小值,并将其用作共享内存中分配的数组的索引。 如果传递诸如 cstringtext 之类的值,则来自新的后台工作进程的指针将无效。

bgw_extra 可以包含要传递给后台工作进程的额外数据。 与 bgw_main_arg 不同,此数据不会作为参数传递给工作进程的主函数,但可以通过 MyBgworkerEntry 访问,如上所述。

bgw_notify_pid 是 PostgreSQL 后端进程的 PID,当进程启动或退出时,postmaster 应向该进程发送 SIGUSR1。 对于在 postmaster 启动时注册的工作进程,或者当注册工作进程的后端不希望等待工作进程启动时,它应为 0。 否则,应将其初始化为 MyProcPid

运行后,进程可以通过调用 BackgroundWorkerInitializeConnection(char *dbname, char *username, uint32 flags)BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, uint32 flags) 连接到数据库。 这允许进程使用 SPI 接口运行事务和查询。 如果 dbname 为 NULL 或 dboidInvalidOid,则会话不会连接到任何特定数据库,但可以访问共享目录。 如果 username 为 NULL 或 useroidInvalidOid,则该进程将以在 initdb 期间创建的超级用户身份运行。 如果将 BGWORKER_BYPASS_ALLOWCONN 指定为 flags,则可以绕过连接到不允许用户连接的数据库的限制。 如果将 BGWORKER_BYPASS_ROLELOGINCHECK 指定为 flags,则可以绕过用于连接到数据库的角色的登录检查。 后台工作进程只能调用这两个函数之一,并且只能调用一次。 无法切换数据库。

当控制到达后台工作进程的主函数时,信号最初被阻止,并且必须由其解除阻止; 这是为了允许进程根据需要自定义其信号处理程序。 可以通过调用 BackgroundWorkerUnblockSignals 来取消阻止新进程中的信号,并通过调用 BackgroundWorkerBlockSignals 来阻止信号。

如果后台工作进程的 bgw_restart_time 配置为 BGW_NEVER_RESTART,或者它以退出码 0 退出,或者被 TerminateBackgroundWorker 终止,那么它将在退出时被 postmaster 自动注销。否则,它将在通过 bgw_restart_time 配置的时间段之后重新启动,或者如果 postmaster 由于后端故障而重新初始化集群,则会立即重新启动。只需要临时暂停执行的后端应该使用可中断的休眠,而不是退出;这可以通过调用 WaitLatch() 来实现。在调用该函数时,请确保设置 WL_POSTMASTER_DEATH 标志,并在紧急情况下,如果 postgres 本身已终止,则验证返回代码以进行快速退出。

当使用 RegisterDynamicBackgroundWorker 函数注册后台工作进程时,执行注册的后端可以获取有关该工作进程状态的信息。希望这样做的后端应该将一个 BackgroundWorkerHandle * 的地址作为第二个参数传递给 RegisterDynamicBackgroundWorker。如果工作进程成功注册,则此指针将使用一个不透明的句柄进行初始化,该句柄随后可以传递给 GetBackgroundWorkerPid(BackgroundWorkerHandle *, pid_t *)TerminateBackgroundWorker(BackgroundWorkerHandle *)GetBackgroundWorkerPid 可以用来轮询工作进程的状态:返回值 BGWH_NOT_YET_STARTED 表示 postmaster 尚未启动该工作进程;BGWH_STOPPED 表示它已经启动但不再运行;而 BGWH_STARTED 表示它当前正在运行。在最后一种情况下,PID 也会通过第二个参数返回。TerminateBackgroundWorker 会导致 postmaster 向正在运行的工作进程发送 SIGTERM 信号,并在它不再运行时立即注销它。

在某些情况下,注册后台工作进程的进程可能希望等待工作进程启动。这可以通过将 bgw_notify_pid 初始化为 MyProcPid,然后将注册时获得的 BackgroundWorkerHandle * 传递给 WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *) 函数来实现。此函数将阻塞,直到 postmaster 尝试启动后台工作进程,或者直到 postmaster 死机。如果后台工作进程正在运行,则返回值将为 BGWH_STARTED,并且 PID 将被写入提供的地址。否则,返回值将为 BGWH_STOPPEDBGWH_POSTMASTER_DIED

进程还可以通过使用 WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle) 函数并传递注册时获得的 BackgroundWorkerHandle * 来等待后台工作进程关闭。此函数将阻塞,直到后台工作进程退出,或者 postmaster 死机。当后台工作进程退出时,返回值是 BGWH_STOPPED,如果 postmaster 死机,它将返回 BGWH_POSTMASTER_DIED

后台工作进程可以发送异步通知消息,可以使用通过SPINOTIFY 命令,或者直接通过 Async_Notify()。此类通知将在事务提交时发送。后台工作进程不应使用 LISTEN 命令注册接收异步通知,因为没有基础设施供工作进程使用此类通知。

src/test/modules/worker_spi 模块包含一个工作示例,演示了一些有用的技术。

注册的后台工作进程的最大数量受 max_worker_processes 限制。

提交更正

如果您在文档中看到任何不正确、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。