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 sont attachés à la zone de
mémoire partagée de PostgreSQL et ont l'option de
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 processus
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[MAXPGPATH]; char bgw_function_name[BGW_MAXLEN]; Datum bgw_main_arg; char bgw_extra[BGW_EXTRALEN]; pid_t 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
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 appelle une fonction
du code du moteur, il faudrait plutôt le configurer à
"postgres"
.
bgw_function_name
est le nom de la fonction à utiliser
comme point d'entrée vers le nouveau background worker. Si cette fonction est
dans une bibliothèque chargée dynamiquement, elle doit être marquée
PGDLLEXPORT
(et non pas static
).
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 superutilisateur
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.
Si BGWORKER_BYPASS_ROLELOGINCHECK
est indiqué pour le
paramètre flags
, il est possible de contourner la
vérification du compte pour le rôle utilisé pour se connecter aux bases.
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
.
Les processus en tâche de fond peuvent envoyer des messages de notificatio
asynchrones, soit en utilisant la commande NOTIFY
via
SPI, soit directement avec Async_Notify
()
. De telles notifications seront envoyées au moment du commit.
Les processus en tâche de fond ne doivent pas s'enregistrer pour recevoir
des notifications asynchrones avec la commande LISTEN
,
car il n'existe pas d'infrastructure pour qu'un tel processus puisse
consommer ce type de 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.