支援的版本:目前 (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_time 是伺服器狀態,在此期間 postgres 應啟動進程;它可以是 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

背景工作程序可以傳送非同步通知訊息,方法是透過以下方式使用 NOTIFY 命令:SPI,或直接透過 Async_Notify()。此類通知將在交易提交時傳送。背景工作程序不應使用 LISTEN 命令註冊以接收非同步通知,因為沒有基礎結構供工作程序使用此類通知。

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

已註冊的背景工作程序的最大數量受到 max_worker_processes 的限制。

提交更正

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