2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3

第 46 章 後臺工作程序

PostgreSQL 可以透過執行使用者提供的程式碼到獨立的程序中來擴充套件。這些程序由 postgres 啟動、停止和監控,這使得它們能夠與伺服器的狀態緊密關聯。這些程序附加到 PostgreSQL 的共享記憶體區域,並可以選擇在內部連線到資料庫;它們也可以像常規的客戶端連線伺服器程序一樣序列執行多個事務。此外,透過連結 libpq,它們可以連線到伺服器並表現得像常規的客戶端應用程式。

警告

使用後臺工作程序存在相當大的健壯性和安全風險,因為它們是用 C 語言編寫的,對資料有無限制的訪問。希望啟用包含後臺工作程序的模組的管理員應格外小心。只應允許經過仔細審計的模組執行後臺工作程序。

後臺工作程序可以在 PostgreSQL 啟動時初始化,方法是將模組名稱包含在 shared_preload_libraries 中。希望執行後臺工作程序的模組可以透過在其 _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_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,當程序啟動或退出時,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

後臺工作程序可以透過使用 NOTIFY 命令透過SPI,或直接透過 Async_Notify() 傳送非同步通知訊息。這些通知將在事務提交時傳送。後臺工作程序不應使用 LISTEN 命令註冊接收非同步通知,因為沒有基礎結構供工作程序消耗此類通知。

src/test/modules/worker_spi 模組包含一個工作示例,演示了一些有用的技術。

已註冊後臺工作程序的最大數量受 max_worker_processes 的限制。

提交更正

如果您在文件中發現任何不正確、與您對特定功能的經驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。