Documentation PostgreSQL 15.15 » Programmation serveur » Processus en tâche de fond (background worker)

Précédent | Niveau supérieur | Suivant
47.6. Exemples | Sommaire | Chapitre 49. Décodage logique (Logical Decoding)

## Chapitre 48. Processus en tâche de fond (background worker)

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.

### Avertissement

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 écrits 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 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[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 possible 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. C'est requis.

`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 passer 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(`char *dbname`, `char *username`, `uint32 flags`)` ou `BackgroundWorkerInitializeConnectionByOid(`Oid dboid`, `Oid useroid`, `uint32 flags`)`. 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 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. 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(`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`.

Un processus peut aussi attendre l'arrêt d'un autre processus en tâche de fond, en utilisant la fonction `WaitForBackgroundWorkerShutdown(`BackgroundWorkerHandle *handle`)` et en passant le `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.