O PostgreSQL pode ser estendido para
executar código fornecido pelo usuário em processos separados.
Esses processos são iniciados, parados e monitorados pelo
postgres
, o que permite que tenham uma vida útil
intimamente ligada ao estado do servidor.
Esses processos têm a opção de se conectar à área de memória
compartilhada do PostgreSQL, e de se
conectar a bancos de dados internamente; eles também podem executar
múltiplas transações em série, da mesma forma como um processo
servidor normal conectado ao cliente faria.
Além disso, vinculando-se à libpq eles
podem se conectar ao servidor e se comportar como uma aplicação
cliente normal.
Existem riscos consideráveis de robustez e segurança no uso de
processos trabalhadores em segundo plano, porque sendo escritos
na linguagem C
, têm acesso irrestrito aos dados.
Os administradores que desejem ativar módulos que incluam processos
trabalhadores em segundo plano devem ter extremo cuidado.
Somente módulos cuidadosamente auditados devem ter permissão para
executar processos trabalhadores em segundo plano.
Os processos trabalhadores em segundo plano podem ser iniciados no
momento em que o PostgreSQL é iniciado,
incluindo o nome do módulo em shared_preload_libraries
.
Um módulo que deseja executar um processo trabalhador em segundo plano
pode registrá-lo chamando
RegisterBackgroundWorker(
em sua função
BackgroundWorker
*worker
)_PG_init()
.
Os processos trabalhadores em segundo plano também podem ser iniciados
depois que o sistema estiver carregado e funcionando, chamando
RegisterDynamicBackgroundWorker(
.
Ao contrário de BackgroundWorker
*worker
, BackgroundWorkerHandle
**handle
)RegisterBackgroundWorker
, que só
pode ser chamado de dentro do processo postmaster
,
RegisterDynamicBackgroundWorker
deve ser chamado
de um processo servidor regular, ou de outro processo trabalhador em
segundo plano.
A estrutura BackgroundWorker
é definida assim:
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; /* em segundos, ou 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;
Os membros bgw_name
e
bgw_type
são cadeias de caracteres a serem
usadas em mensagens de registro de eventos (log), listagens de
processos e contextos semelhantes.
O membro bgw_type
deve ser igual para
todos os processos trabalhadores em segundo plano do mesmo tipo,
para ser possível agrupar esses processos trabalhadores em uma
listagem de processos, por exemplo.
Por outro lado, o membro bgw_name
pode
conter informações adicionais sobre o processo específico.
(Normalmente, a cadeia de caracteres para
bgw_name
irá conter o tipo de alguma
forma, mas isso não é estritamente necessário.)
O membro bgw_flags
é uma máscara bit-a-bit
que indica os recursos que o módulo deseja usar.
Os valores possíveis são:
BGWORKER_SHMEM_ACCESS
Requer acesso à memória compartilhada. Os processos trabalhadores sem acesso à memória compartilhada não podem acessar nenhuma estrutura de dados compartilhada do PostgreSQL, como bloqueios pesados ou leves, buffers compartilhados, ou quaisquer estruturas de dados personalizadas que o próprio processo trabalhador queira criar e usar.
BGWORKER_BACKEND_DATABASE_CONNECTION
Requer a capacidade de estabelecer uma conexão com o banco de
dados, por meio da qual poderá executar transações e consultas
posteriormente.
Um processo trabalhador em segundo plano usando
BGWORKER_BACKEND_DATABASE_CONNECTION
para se conectar a um banco de dados também deve anexar memória
compartilhada usando BGWORKER_SHMEM_ACCESS
,
ou a ativação do processo trabalhador irá falhar.
O membro bgw_start_time
é o estado do
servidor durante o qual o postgres
deve iniciar
o processo; pode ser um entre
BgWorkerStart_PostmasterStart
(inicia assim que
o próprio postgres
tiver terminado sua própria
ativação; processos que solicitam isso não são elegíveis para
conexões com banco de dados),
BgWorkerStart_ConsistentState
(inicia assim que
for alcançado um estado consistente em um servidor em-espera ativa
(hot standby), permitindo que os
processos se conectem aos bancos de dados e executem consultas de
leitura-apenas), e BgWorkerStart_RecoveryFinished
(inicia assim que o sistema entra no estado normal de leitura e escrita).
Note que os dois últimos valores são equivalentes em um servidor
que não esteja em-espera ativa.
Note, também, que essa configuração indica apenas quando os processos
serão iniciados; eles não param quando é alcançado um estado diferente.
O membro bgw_restart_time
é o intervalo,
em segundos, que o postgres
deve esperar antes
de reiniciar o processo caso ele trave.
Pode ser qualquer valor positivo, ou BGW_NEVER_RESTART
,
indicando não reiniciar o processo em caso de travamento.
O membro bgw_library_name
é o nome de uma
biblioteca na qual o ponto de entrada inicial para o processo
trabalhador em segundo plano deve ser procurado.
A biblioteca indicada será carregada dinamicamente pelo processo
trabalhador, e será usado bgw_function_name
para identificar a função a ser chamada.
Se a função estiver sendo carregada a partir do código principal,
deverá ser definido como “postgres”.
O membro bgw_function_name
é o nome de uma
função em uma biblioteca carregada dinamicamente que deve ser usada
como ponto de entrada inicial para um novo processo trabalhador em
segundo plano.
O membro bgw_main_arg
é o argumento
Datum
para a função principal do processo trabalhador
em segundo plano.
Essa função principal deve receber um único argumento do tipo
Datum
e retornar void
.
O membro bgw_main_arg
será passado como
argumento.
Além disso, a variável global MyBgworkerEntry
aponta para uma cópia da estrutura
BackgroundWorker
passada no momento do
registro; o processo trabalhador pode achar útil examinar essa
estrutura.
No Windows (e em qualquer
outro lugar onde EXEC_BACKEND
esteja definido),
ou em processos trabalhadores dinâmicos em segundo plano, não é
seguro passar Datum
por referência, apenas por valor.
Se for requerido um argumento, será mais seguro passar um
int32
, ou outro valor pequeno, e usá-lo como
índice em uma matriz alocada na memória compartilhada.
Se for passado um valor como cstring
ou text
,
o ponteiro não será válido no novo processo trabalhador em segundo plano.
O membro bgw_extra
pode conter dados
extras a serem passados para o processo trabalhador em segundo plano.
Diferentemente de bgw_main_arg
, esses dados
não são passados como argumento para a função principal do processo
trabalhador, mas podem ser acessados via
MyBgworkerEntry
, conforme discutido acima.
O membro bgw_notify_pid
é o PID do processo
servidor do PostgreSQL para o qual o
postmaster
deve enviar SIGUSR1
quando o processo for iniciado ou encerrado.
Deve ser 0 para processos trabalhadores registrados no momento da
ativação do postmaster
, ou quando o processo
servidor que registra o processo trabalhador não deseje esperar a
ativação do processo trabalhador.
Caso contrário, deve ser iniciado como MyProcPid
.
Uma vez em execução, o processo pode se conectar a um banco de
dados chamando
BackgroundWorkerInitializeConnection(
, ou
char
*dbname
, char *username
,
uint32 flags
)BackgroundWorkerInitializeConnectionByOid(
.
Isso permite que o processo execute transações e consultas usando
a interface Oid
dboid
, Oid useroid
,
uint32 flags
)SPI
.
Se dbname
for NULL
ou
dboid
for InvalidOid
,
a sessão não estará conectada a nenhum banco de dados específico,
mas os catálogos compartilhados poderão ser acessados.
Se username
for NULL
, ou
useroid
for InvalidOid
,
o processo será executado como o superusuário criado durante
initdb
.
Se BGWORKER_BYPASS_ALLOWCONN
for especificado como
flags
, será possível contornar a restrição de
conexão com bancos de dados que não permitem conexões de usuários.
Um processo trabalhador em segundo plano só pode chamar uma dessas
duas funções, e apenas uma vez.
Não é possível mudar de banco de dados.
Os sinais são inicialmente bloqueados quando o controle atinge a
função principal do processo trabalhador em segundo plano, devendo
ser desbloqueados por ele; isso permite que o processo personalize
seus tratadores de sinal, se necessário.
Os sinais podem ser desbloqueados no novo processo chamando
BackgroundWorkerUnblockSignals
, e bloqueados
chamando BackgroundWorkerBlockSignals
.
Se bgw_restart_time
para um processo
trabalhador em segundo plano estiver configurado como
BGW_NEVER_RESTART
, ou se sair com um código de
saída 0, ou for encerrado por
TerminateBackgroundWorker
, seu registro será
automaticamente cancelado pelo postmaster
ao sair.
Caso contrário, será reiniciado após o período configurado via
bgw_restart_time
, ou imediatamente se o
postmaster
reiniciar a instância devido a uma
falha de processo servidor.
Os processos servidores que precisem suspender a execução apenas
temporariamente devem usar uma dormida interrompível em vez de sair;
isso pode ser conseguido chamando WaitLatch()
.
Certifique-se de que o sinalizador WL_POSTMASTER_DEATH
esteja definido ao chamar essa função, e verifique o código de retorno
para uma saída imediata no caso de emergência em que o próprio
postgres
tenha encerrado.
Quando um processo trabalhador em segundo plano é registrado usando
a função RegisterDynamicBackgroundWorker
,
é possível que o processo servidor que realiza o registro obtenha
informações sobre o status do processo trabalhador.
Os processos servidores que desejam fazer isso devem passar o endereço
de BackgroundWorkerHandle *
como o segundo argumento
para RegisterDynamicBackgroundWorker
.
Se o processo trabalhador for registrado com sucesso, esse ponteiro
será inicializado com um identificador opaco que poderá posteriormente
ser passado para
GetBackgroundWorkerPid(
ou
BackgroundWorkerHandle
*
, pid_t *
)TerminateBackgroundWorker(
.
A função BackgroundWorkerHandle
*
)GetBackgroundWorkerPid
pode ser usada
para pesquisar o status do processo trabalhador: o valor retornado
BGWH_NOT_YET_STARTED
indica que o processo
trabalhador ainda não foi iniciado pelo postmaster
;
BGWH_STOPPED
indica que foi iniciado, mas não está
mais em execução; e BGWH_STARTED
indica que está
em execução.
Nesse último caso, também será retornado o PID através do segundo
argumento.
A função TerminateBackgroundWorker
faz com que
o postmaster
envie SIGTERM
para o processo trabalhador se ele estiver em execução, e cancele
o registro assim que não estiver mais.
Em alguns casos, um processo que registra um processo trabalhador em
segundo plano pode desejar aguardar a ativação do processo trabalhador.
Isso pode ser feito inicializando
bgw_notify_pid
como MyProcPid
,
e depois passar o BackgroundWorkerHandle *
obtido no
momento do registro para a função
WaitForBackgroundWorkerStartup(
.
Essa função ficará bloqueada até que o BackgroundWorkerHandle
*handle
, pid_t *
)postmaster
tente iniciar o processo trabalhador em segundo plano, ou até que o
postmaster
deixe de existir.
Se o processo trabalhador em segundo plano estiver em execução,
o valor retornado será BGWH_STARTED
e o PID
será escrito no endereço fornecido.
Caso contrário, o valor retornado será BGWH_STOPPED
ou BGWH_POSTMASTER_DIED
.
Um processo também pode aguardar o encerramento de um processo
trabalhador em segundo plano, usando a função
WaitForBackgroundWorkerShutdown(
e passando o
BackgroundWorkerHandle
*handle
)BackgroundWorkerHandle *
obtido no registro.
Essa função ficará será bloqueada até que o processo trabalhador em
segundo plano termine, ou o postmaster
não exista mais.
Quando o processo trabalhador em segundo plano encerra, o valor
retornado é BGWH_STOPPED
, mas se o
postmaster
encerrar será retornado
BGWH_POSTMASTER_DIED
.
Os processos trabalhadores em segundo plano podem enviar mensagens
de notificação assíncronas, usando o comando NOTIFY
via SPI, ou diretamente via
Async_Notify()
.
Essas notificações serão enviadas na efetivação da transação.
Os processos trabalhadores em segundo plano não devem se registrar
para receber notificações assíncronas com o comando
LISTEN
, porque não há infraestrutura para um
processo trabalhador consumir essas notificações.
O módulo src/test/modules/worker_spi
contém um
exemplo funcional, que demonstra algumas técnicas úteis.
O número máximo de processos trabalhadores em segundo plano registrados é limitado por max_worker_processes.