Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

第 46 章。后台工作器进程

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

警告

在后台工作进程中使用时有着相当大的鲁棒性和安全风险,因为是用 C 语言编写的,它具有无限制的数据访问权限。希望启用包含后台工作进程的模块的管理员应当极度谨慎。只有仔细审核的模块才能被允许运行后台工作进程。

在启动 PostgreSQL 时可以通过在 shared_preload_libraries 中包含模块名称来初始化后台工作进程。希望运行后台工作进程的模块可以通过从 _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];
    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 是按位或'd 的比特掩码,指示了模块希望的性能。可能的值是

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_time 是如果进程崩溃,postgres 在重新启动进程之前应等待的时间间隔(以秒为单位)。它可以是任何正值,或 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,在进程启动或退出时,邮寄器应当将其传递至 SIGUSR1。对于在邮寄器启动时间注册的工作进程,或者注册工作进程的后端不想等待工作进程启动,其应当为 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_ALLOWCONNflags 中指定,可以绕过连接未允许用户连接的数据库的限制。如果 BGWORKER_BYPASS_ROLELOGINCHECKflags 中指定,可以绕过用于连接数据库的角色登录检查。一个后台工作进程只可以调用这两个函数中的一个,且只能调用一次。不能切换数据库。

当控制权到达后台工作程序的主函数时,信号会被立即阻塞,并且必须由它解除阻塞;这样是为了允许该进程定制其信号处理程序(如有必要)。可以在新进程中通过调用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

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

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

已注册后台工作人员的最大数量受 max_worker_processes 限制。