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(
dans son BackgroundWorker *worker)_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(
.
À la différence de BackgroundWorker *worker, BackgroundWorkerHandle **handle)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 */
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 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_ACCESSRéclame un accès à la mémoire partagée. Les processus sans accès à la mémoire partagée ne peuvent pas accéder aux structures de données partagées de PostgreSQL, tels que les verrous (lourds ou légers), la mémoire partagée et toute structure de données personnalisée que le processus pourrait vouloir créer et utiliser.
BGWORKER_BACKEND_DATABASE_CONNECTION
Réclame la capacité à établir une connexion à une base à partir de
laquelle il peut ensuite exécuter des transactions et des requêtes. Un
processus en tâche de fond utilisant
BGWORKER_BACKEND_DATABASE_CONNECTION pour se connecter
à une base doit aussi s'attacher à la mémoire partagée en utilisant
BGWORKER_SHMEM_ACCESS. Dans le cas contraire, son
démarrage é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_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é. La bibliothèque nommée sera chargée dynamiquement
par le processus en tâche de fond et bgw_function_name
sera utiliser pour identifier la fonction à appeler. S'il charge une fonction
du code du moteur, il faudrait plutôt le configurer à
« postgres ».
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.
bgw_main_arg est l'argument Datum de
la fonction principale du processus. Cette fonction principale devrait prendre
un seul argument de type Datum et renvoyer void.
bgw_main_arg sera passé comme argument. De plus,
la variable globale MyBgworkerEntry pointe vers une copie
de la structure BackgroundWorker passé au moment de
l'enregistrement ; le processus pourrait trouver utile d'examiner cette
structure.
Sur Windows (et partout où EXEC_BACKEND est défini) ou
dans des processus en tache de fond dynamiques, il n'est pas sûre de passer
un Datum par référence, il faut le passe par valeur. Si un
argument est requis, il est plus sûr de passer un int32 ou toute autre petite
valeur et l'utiliser comme un index d'un tableau alloué en mémoire partagée.
Si une valeur comme un cstring ou un text est
passée, alors le pointeur ne sera pas valide à partir du nouveau processus
en tâche de fond.
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( ou
char *dbname, char *username)BackgroundWorkerInitializeConnectionByOid(.
Cela autorise le processus à exécuter des transactions et des requêtes en
utilisant l'interface Oid dboid, Oid useroid)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 principale du background worker, 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( ou
BackgroundWorkerHandle *, pid_t *)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(.
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
BackgroundWorkerHandle
*handle, pid_t *)BGWH_STARTED, et le PID sera écrit à l'adresse fournie.
Sinon, la valeur de retour sera BGWH_STOPPED ou
BGWH_POSTMASTER_DIED.
Si un processus en tâche de fond envoie des notifications asynchrones avec
la commande NOTIFY via SPI), il devrait
appeler ProcessCompletedNotifies explicitement après
avoir validé la transaction englobante pour que les notifications soient
envoyées. Si un processus en tâche de fond se déclare pour recevoir des
notifications asynchrones avec LISTEN via
SPI, le processus tracera les notifications. Cependant,
il n'existe pas de façon programmé pour que le processus intercepte et
réponde à ces notifications.
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.