On this page
章 47.后台工作进程
PostgreSQL 可以扩展为在单独的进程中运行用户提供的代码。此类进程由postgres
启动,停止和监视,从而使它们的生存期与服务器状态紧密相关。这些进程可以选择附加到 PostgreSQL 的共享内存区域并在内部连接到数据库。它们还可以串行运行多个事务,就像常规的 Client 端连接服务器进程一样。而且,通过链接到 libpq,他们可以连接到服务器,并且表现得像普通的 Client 端应用程序。
Warning
使用后台工作进程具有很大的健壮性和安全性风险,因为使用C
语言编写的后台工作进程可以不受限制地访问数据。希望启用包含后台工作进程的模块的 Management 员应格外小心。仅允许经过仔细审核的模块运行后台工作进程。
通过在shared_preload_libraries
中包含模块名称,可以在启动 PostgreSQL 时初始化后台工作程序。希望运行后台工作程序的模块可以通过从_PG_init()
调用RegisterBackgroundWorker(BackgroundWorker *worker)
进行注册。也可以在系统启动并运行后通过调用函数RegisterDynamicBackgroundWorker(BackgroundWorker *worker, BackgroundWorkerHandle **handle)
来启动后台工作程序。与RegisterBackgroundWorker
只能从邮局内部调用不同,RegisterDynamicBackgroundWorker
必须从常规后端调用。
因此定义结构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 */
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_flags
是按位或位的掩码,指示模块所需的功能。可能的值为:
BGWORKER_SHMEM_ACCESS
BGWORKER_BACKEND_DATABASE_CONNECTION
bgw_start_time
是服务器状态,在此期间postgres
应开始该过程;它可以是BgWorkerStart_PostmasterStart
中的一种(postgres
本身完成其自身的初始化后立即开始;请求此初始化的进程不符合数据库连接的条件),BgWorkerStart_ConsistentState
(在热备用数据库中达到一致状态后立即启动,从而允许进程连接到数据库并运行只读查询)和BgWorkerStart_RecoveryFinished
(在系统进入正常读写状态后立即启动)。请注意,在不是热备用服务器的服务器中,最后两个值是等效的。请注意,此设置仅指示何时开始进程。当达到其他状态时,它们不会停止。
bgw_restart_time
是间隔postgres
应该 await 的时间间隔(以秒为单位),以防崩溃。它可以是任何正值或BGW_NEVER_RESTART
,表示在发生崩溃时不重新启动该过程。
bgw_library_name
是应该在其中寻找后台工作者的初始入口点的库的名称。命名库将由工作进程动态加载,而bgw_function_name
将用于标识要调用的函数。如果从核心代码加载函数,则必须将其设置为“ postgres”。
bgw_function_name
是动态加载的库中的函数名称,应将其用作新后台工作程序的初始入口点。
bgw_main_arg
是后台工作程序主函数的Datum
参数。此主要函数应采用Datum
类型的单个参数并返回void
。 bgw_main_arg
将作为参数传递。另外,全局变量MyBgworkerEntry
指向注册时传递的BackgroundWorker
结构的副本;Worker 可能会发现检查这种结构很有帮助。
在 Windows(以及定义EXEC_BACKEND
的其他任何位置)上或在动态后台工作程序中,仅通过值传递Datum
并不安全。如果需要一个参数,则最安全的方法是传递一个 int32 或其他较小的值,并将其用作索引到共享内存中分配的数组中。如果传递了cstring
或text
之类的值,则该指针在新的后台工作进程中将无效。
bgw_extra
可以包含要传递给后台工作人员的其他数据。与bgw_main_arg
不同,此数据不会作为参数传递给工作程序的 main 函数,但可以通过MyBgworkerEntry
进行访问,如上所述。
bgw_notify_pid
是 PostgreSQL 后端进程的 PID,在启动或退出该进程时,邮局主管应将SIGUSR1
发送给该进程。对于在邮局主管启动时注册的工作人员,或在注册该工作人员的后端不希望 await 该工作人员启动时注册的工作人员,它应该为 0.否则,应将其初始化为MyProcPid
。
运行后,该进程可以通过调用BackgroundWorkerInitializeConnection(char *dbname, char *username)
或BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid)
连接到数据库。这允许进程使用SPI
接口运行事务和查询。如果dbname
为 NULL 或dboid
为InvalidOid
,则会话未连接到任何特定数据库,但是可以访问共享目录。如果username
为 NULL 或useroid
为InvalidOid
,则该进程将以initdb
期间创建的超级用户身份运行。后台工作者只能调用这两个函数之一,并且只能调用一次。无法切换数据库。
当控制权到达后台工作者的主要功能时,signal 最初会被阻塞,并且必须被其阻塞。这是为了允许该进程自定义其 signal 处理程序(如有必要)。可以通过调用BackgroundWorkerUnblockSignals
来取消阻塞 signal,并通过调用BackgroundWorkerBlockSignals
来阻塞 signal。
如果将后台工作人员的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
发送给工作程序(如果正在运行),并在工作程序不运行时立即注销。
在某些情况下,注册后台工作人员的进程可能希望 await 该工作人员启动。这可以通过将bgw_notify_pid
初始化为MyProcPid
,然后将在注册时获得的BackgroundWorkerHandle *
传递给WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *)
函数来实现。该功能将一直阻塞,直到邮局主管尝试启动后台工作人员或邮局主管去世为止。如果后台运行程序正在运行,则返回值将为BGWH_STARTED
,并且 PID 将被写入提供的地址。否则,返回值为BGWH_STOPPED
或BGWH_POSTMASTER_DIED
。
如果后台工作人员通过服务器编程接口(SPI)使用NOTIFY
命令发送异步通知,则它应在提交封闭事务后显式调用ProcessCompletedNotifies
,以便可以传递任何通知。如果后台工作程序注册为通过 SPI 通过LISTEN
接收异步通知,则工作程序将记录这些通知,但是工作程序无法通过编程方式来拦截和响应这些通知。
src/test/modules/worker_spi
模块包含一个工作示例,其中演示了一些有用的技术。
注册的后台工作人员的最大数量受max_worker_processes限制。