PostgreSQL peut être étendu pour lancer du code utilisateur dans des processus séparés. Ces processus sont démarrés, arrêtés et supervisés par postgres, ce qui leur permet d'avoir un cycle de vie étroitement lié au statut du serveur. Ces processus ont des options pour s'attacher à la zone de mémoire partagée de PostgreSQL™ et pour se connecter aux bases de manière interne ; ils peuvent également exécuter de multiples transactions séquentiellement, comme n'importe quel processus client standard connecté au serveur. De plus, en se liant avec la bibliothèque libpq, ils peuvent se connecter au serveur et se comporter comme une application cliente standard.
Il y a de considérables risques de robustesse et sécurité lorsque l'on utilise des processus background worker. En effet, ceux-ci étant écrit en langage C, ils ont un accès total aux données. Les administrateurs désirant activer des modules incluant des processus background worker devraient prendre énormément de précautions. Seuls les modules soigneusement testés devraient être autorisés à lancer des processus background worker.
Les processus en tâche de fond peuvent être initialisés au moment où PostgreSQL™ est démarré en incluant le nom du module dans shared_preload_libraries. Un module qui souhaite fonctionner comme un processus en tâche de fond peut s'enregistrer en appelant RegisterBackgroundWorker(BackgroundWorker *worker) dans son _PG_init(). Les processus en tâche de fond peuvent également être démarrés après que le système ait démarré et soit en fonctionnement en appelant la fonction RegisterDynamicBackgroundWorker( BackgroundWorker *worker, BackgroundWorkerHandle **handle). À la différence de RegisterBackgroundWorker, qui ne peut être appelée que depuis le postmaster,RegisterDynamicBackgroundWorker doit être appelée depuis un processus client standard.
La structure BackgroundWorker est définie ainsi :
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 */ bgworker_main_type bgw_main; char bgw_library_name[BGW_MAXLEN]; /* only if bgw_main is NULL */ char bgw_function_name[BGW_MAXLEN]; /* only if bgw_main is NULL */ Datum bgw_main_arg; char bgw_extra[BGW_EXTRALEN]; int bgw_notify_pid; } BackgroundWorker;
bgw_name est une chaîne de caractères à utiliser dans les messages de trace, liste de processus et autres listes similaires.
bgw_flags est un masque de bit OR indiquant les capacités que veut le module. Les valeurs possibles sont BGWORKER_SHMEM_ACCESS (demandant un accès à la mémoire partagée) et BGWORKER_BACKEND_DATABASE_CONNECTION (demandant la capacité d'établir une connexion à la base, grâce à laquelle il pourra exécuter des transactions et requêtes ultérieurement). Un processus en tâche de fond utilisant BGWORKER_BACKEND_DATABASE_CONNECTION pour se connecter à une base de données doit aussi attacher de la mémoire partagée en utilisant BGWORKER_SHMEM_ACCESS. Dans le cas contraire, le démarrage de la tâche de fond échouera.
bgw_start_time spécifie l'état du serveur dans lequel postgres devrait démarrer le processus ; les valeurs possibles sont BgWorkerStart_PostmasterStart (démarrer dès que postgres lui-même a fini sa propre initialisation ; les processus réclamant celà ne sont pas éligibles à une connexion à la base de données), BgWorkerStart_ConsistentState (démarrer dès qu'un état cohérent a été atteint sur un serveur esclave en lecture seule, permettant aux processus de se connecter aux bases et d'exécuter des requêtes en lecture seule), et BgWorkerStart_RecoveryFinished (démarrer dès que le système est entré dans un état de lecture-écriture normal). Notez que les deux dernières valeurs sont équivalentes sur un serveur qui n'est pas un esclave en lecture seule. Notez également que ces valeurs indiquent uniquement quand les processus doivent être démarrés ; ils ne s'arrêtent pas quand un état différent est atteint.
bgw_restart_time est un intervalle, en secondes, que postgres doit attendre avant de redémarrer un processus, si celui-ci a subi un arrêt brutal. Cet intervalle peut être une valeur positive ou BGW_NEVER_RESTART, indiquant de ne pas redémarrer le processus suite à un arrêt brutal.
bgw_main est un pointeur vers la fonction à lancer quand le processus est démarré. Cette fonction doit prendre un unique argument de type Datum et retourner void. bgw_main_arg lui sera passé comme unique argument. Notez que la variable globale MyBgworkerEntry pointe vers une copie de la structure BackgroundWorker passée lors de l'enregistrement. bgw_main peut valoir NULL ; dans ce cas, bgw_library_name et bgw_function_name seront utilisés pour déterminer le point d'entrée. C'est utile pour les processus en tâche de fond lancés après le démarrage du postmaster, où le postmaster n'a pas les bibliothèques nécessaires chargées.
bgw_library_name est le nom d'une bibliothèque dans laquelle le point d'entrée initial pour le processus en tâche de fond devrait être recherché. Il est ignoré à moins que bgw_main ne vaille NULL. Mais si bgw_main vaut NULL, alors cette bibliothèque sera chargée dynamiquement par le processus en tâche de fond et bgw_function_name sera utilisé pour identifier la fonction à appeler.
bgw_function_name est le nom d'une fonction dans une bibliothèque chargée dynamiquement qui devrait être utilisée comme point d'entrée initial pour un nouveau processus en tâche de fond. Il est ignoré à moins que bgw_main ne vaille NULL.
bgw_extra peut contenir des données supplémentaires à fournir au background worker. Contrairement à bgw_main_arg, cette donnée n'est pas fourni comme argument de la fonction principale du processus. Elle est accessible via la variable MyBgworkerEntry, comme discuté ci-dessus.
bgw_notify_pid est le PID d'un processus client PostgreSQL auquel le postmaster devrait envoyer un signal SIGUSR1 quand le processus est démarré ou quitte. Il devrait valoir 0 pour les processus en tâche de fond enregistrés lors du démarrage du postmaster, ou quand le processus client enregistrant le processus en tâche de fond ne souhaite pas attendre que le processus en tâche de fond ne démarre. Sinon, il devrait être initialisé à MyProcPid.
Une fois démarré, le processus peut se connecter à une base en appelant BackgroundWorkerInitializeConnection(char *dbname, , char *username) ou BackgroundWorkerInitializeConnectionByOid(Oid dboid, , Oid useroid). Cela autorise le processus à exécuter des transactions et des requêtes en utilisant l'interface SPI. Si dbname vaut NULL ou que dboid vaut InvalidOid, la session n'est pas connectée à une base en particulier, mais les catalogues partagés peuvent être accédés. Si username vaut NULL ou que useroid vaut InvalidOid, le processus sera démarré avec le super utilisateur créé durant initdb. Un background worker ne peut être appelé que par une de ces deux fonctions, et seulement une fois. Il n'est pas possible de changer de base de données.
Les signaux sont initialement bloqués jusqu'à ce que le contrôle atteigne la fonction bgw_main, et doivent être débloqués par elle ; cela permet une personnalisation des gestionnaires de signaux du processus, si nécessaire. Les signaux peuvent être débloqués dans le nouveau processus en appellant BackgroundWorkerUnblockSignals et bloqués en appelant BackgroundWorkerBlockSignals.
Si bgw_restart_time est configuré à BGW_NEVER_RESTART pour un processus en tâche de fond ou s'il quitte avec un code de sortie 0, ou encore s'il est terminé par TerminateBackgroundWorker, il sera automatiquement désenregistré par le postmaster lors de sa sortie. Sinon, il sera redémarré après que la période de temps configurée via bgw_restart_time, ou immédiatement si le postmaster réinitialise l'instance à cause d'une défaillance d'un processus client. Les processus en tâche de fond qui nécessitent de suspendre leur exécution seulement temporairement devraient utiliser un sommeil interruptible plutôt que de quitter. Vérifiez que le drapeau WL_POSTMASTER_DEATH est positionné lors de l'appel à cette fonction, et vérifiez le code retour pour une sortie rapide dans le cas d'urgence où postgres lui-même se termine.
Quand un processus en tâche de fond est enregistré en utilisant la fonction RegisterDynamicBackgroundWorker, le processus client effectuant cet enregistrement peut obtenir des informations concernant le statut du processus en tâche de fond. Les processus clients souhaitant faire cela devraient fournir l'adresse d'un BackgroundWorkerHandle * comme second argument pour RegisterDynamicBackgroundWorker. Si l'enregistrement du processus en tâche de fond est réussi, ce pointeur sera initialisé avec un handle opaque qui peut alors être fourni à GetBackgroundWorkerPid(BackgroundWorkerHandle *, , pid_t *) ou TerminateBackgroundWorker(BackgroundWorkerHandle *). GetBackgroundWorkerPid peut être utilisé pour interroger le statut du processus en tâche de fond : une valeur de retour valant BGWH_NOT_YET_STARTED indique que le processus en tâche de fond n'a pas encore été démarré par le postmaster; BGWH_STOPPED indique qu'il a été démarré mais n'est plus en fonctionnement; et BGWH_STARTED indique qu'il est actuellement en fonctionnement. Dans le dernier cas, le PID sera également renvoyé via le deuxième argument. TerminateBackgroundWorker demande postmaster d'envoyer un signal SIGTERM au processus en tâche de fond s'il est en train de fonctionner, et de le désenregistrer dès qu'il ne sera plus en fonctionnement.
Dans certains cas, un processus qui enregistre un processus en tâche de fond peut souhaiter attendre le démarrage du processus en tâche de fond. Ceci peut être fait en initialisant bgw_notify_pid à MyProcPid et en fournissant ensuite le BackgroundWorkerHandle * obtenu au moment de l'enregistrement à la fonction WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, , pid_t *). Cette fonctionne bloquera jusqu'à ce que le postmaster ait tenté de démarrer le processus en tâche de fond, ou jusqu'à l'arrêt du postmaster. Si le processus en tâche de fond est en fonctionnement, la valeur retournée sera BGWH_STARTED, et le PID sera écrit à l'adresse fournie. Sinon, la valeur de retour sera BGWH_STOPPED ou BGWH_POSTMASTER_DIED.
Le module contrib src/test/modules/worker_spi contient un exemple fonctionnel, qui démontre quelques techniques utiles.
Le nombre maximum de processus en tâche de fond enregistré est limité par max_worker_processes.