PostgreSQL 可以通过运行用户提供的代码到独立的进程中来扩展。这些进程由 postgres
启动、停止和监控,这使得它们能够与服务器的状态紧密关联。这些进程附加到 PostgreSQL 的共享内存区域,并可以选择在内部连接到数据库;它们也可以像常规的客户端连接服务器进程一样串行运行多个事务。此外,通过链接 libpq,它们可以连接到服务器并表现得像常规的客户端应用程序。
使用后台工作进程存在相当大的健壮性和安全风险,因为它们是用 C
语言编写的,对数据有无限制的访问。希望启用包含后台工作进程的模块的管理员应格外小心。只应允许经过仔细审计的模块运行后台工作进程。
后台工作进程可以在 PostgreSQL 启动时初始化,方法是将模块名称包含在 shared_preload_libraries
中。希望运行后台工作进程的模块可以通过在其 _PG_init()
函数中调用 RegisterBackgroundWorker(
来注册它。后台工作进程也可以在系统运行后启动,方法是调用 BackgroundWorker
*worker
)RegisterDynamicBackgroundWorker(
。与只能在 postmaster 进程内调用的 BackgroundWorker
*worker
, BackgroundWorkerHandle
**handle
)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_name
和 bgw_type
是用于日志消息、进程列表和类似上下文的字符串。bgw_type
对于同一类型的后台工作进程应该是相同的,这样在进程列表中就可以将这些工作进程分组。另一方面,bgw_name
可以包含关于特定进程的附加信息。(通常,bgw_name
的字符串会以某种方式包含类型,但这并非严格要求。)
bgw_flags
是一个按位或运算的位掩码,指示模块想要的特性。可能的值有:
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
是用作新后台工作进程初始入口点的函数名称。如果此函数位于动态加载的库中,则必须将其标记为 PGDLLEXPORT
(而不是 static
)。
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
是一个 PostgreSQL 后端进程的 PID,当进程启动或退出时,postmaster 应向其发送 SIGUSR1
信号。对于在 postmaster 启动时注册的工作进程,或者当注册工作进程的后端不希望等待工作进程启动时,该值应为 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
,则可以绕过连接不允许用户连接的数据库的限制。如果指定 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(
函数来实现。此函数将阻塞,直到 postmaster 尝试启动后台工作进程,或者直到 postmaster 死亡。如果后台工作进程正在运行,返回值将是 BackgroundWorkerHandle *handle
, pid_t *
)BGWH_STARTED
,并且 PID 将被写入提供的地址。否则,返回值将是 BGWH_STOPPED
或 BGWH_POSTMASTER_DIED
。
进程还可以通过使用 WaitForBackgroundWorkerShutdown(
函数并传递注册时获得的 BackgroundWorkerHandle *handle
)BackgroundWorkerHandle *
来等待后台工作进程关闭。此函数将阻塞,直到后台工作进程退出,或者 postmaster 死亡。当后台工作进程退出时,返回值为 BGWH_STOPPED
,如果 postmaster 死亡,则返回 BGWH_POSTMASTER_DIED
。
后台工作进程可以通过使用 NOTIFY
命令通过SPI,或直接通过 Async_Notify()
发送异步通知消息。这些通知将在事务提交时发送。后台工作进程不应使用 LISTEN
命令注册接收异步通知,因为没有基础结构供工作进程消耗此类通知。
src/test/modules/worker_spi
模块包含一个工作示例,演示了一些有用的技术。
已注册后台工作进程的最大数量受 max_worker_processes 的限制。
如果您在文档中发现任何不正确、与您对特定功能的经验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。