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 ou un processus en
tâche de fond.
La structure BackgroundWorker
est définie ainsi :
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[BGW_MAXLEN]; char bgw_function_name[BGW_MAXLEN]; Datum bgw_main_arg; char bgw_extra[BGW_EXTRALEN]; int bgw_notify_pid; } BackgroundWorker;
bgw_name
et bgw_type
sont des chaînes de caractères à utiliser
dans les messages de trace, liste de processus et autres listes similaires.
bgw_type
devrait être identique pour tous les
processus en tâche de fond du même type pour qu'il soit possibl de grouper
ces processus avec une liste des processus par exemple. Par contre,
bgw_name
peut contenir des informations
supplémentaires sur ce processus spécifique. (Typiquement, la chaîne de
bgw_name
contiendra le type en quelque sort, mais
ce n'est pas requis strictement.)
bgw_flags
est un masque de bit OR indiquant les
capacités que veut le module. Les valeurs possibles sont
BGWORKER_SHMEM_ACCESS
Ré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 tâche de fond dynamiques, il n'est pas sûr 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
, uint32 flags
)BackgroundWorkerInitializeConnectionByOid(
.
Cela autorise le processus à exécuter des transactions et des requêtes en
utilisant l'interface Oid dboid
, Oid useroid
, uint32 flags
)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
. Si
BGWORKER_BYPASS_ALLOWCONN
est indiqué pour le paramètre
flags
, il est possible de contourner la restriction de se
connecter aux bases de données ne permettant pas une connexions des
utilisateurs. 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
.
Un processus peut aussi attendre l'arrêt d'un autre processus en tâche de
fond, en utilisant la fonction
WaitForBackgroundWorkerShutdown(
et en passant le
BackgroundWorkerHandle
*handle
)BackgroundWorkerHandle *
obtenu à l'enregistrement. Cette
fonction bloquera l'exécution jusqu'à l'arrêt de l'autre processus ou
jusqu'à la mort de postmaster. Quand le processus en tâche de fond quitte,
la valeur de retour est BGWH_STOPPED
. Si postmaster
meurt, il renverra 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.