Estou escrevendo um aplicativo que processa trabalhos. Um trabalho faz duas coisas que não são transacionais: ParteA e ParteB. Ambos são idempotentes.
Um trabalho tem três status:
- Criada
- ParteA_Concluído
- ParteB_Concluído
A lógica é mais ou menos assim:
BEGIN;
let jobId = randomUUID
INSERT INTO jobs (id, status) VALUES (jobId, 'Created');
COMMIT;
BEGIN;
do partA in application code (make HTTP request)
UPDATE jobs SET status = 'PartA_Done' WHERE id=?
COMMIT;
BEGIN;
do partB in application code (make HTTP request)
UPDATE jobs SET status = 'PartB_Done' WHERE id=?
COMMIT;
A ParteA pode ter êxito, mas não consegue atualizar a jobs
tabela. Uma tarefa cron de nova tentativa tentará novamente a conclusão, PartB_Done
.
Se eu adicionasse mais de um cron job, ou seja, executando simultaneamente, há o risco de os cron jobs realizarem trabalho duplicado. Em particular, ambos os cron jobs poderiam ser refeitos PartA
, PartB
ou ambos para o mesmo trabalho. Eu quero evitar isso.
Existe alguma maneira de executar o COMMIT
, mantendo um FOR UPDATE
bloqueio nas transações subsequentes na nova jobs
linha que adicionei?
Achei que COMMIT AND CHAIN
funcionaria, mas não tenho certeza do que fazer.
Você não pode manter um bloqueio de linha em um commit. Eu tenho duas ideias:
Adicione uma coluna adicional
processing_since
do tipotimestamp with time zone
. A coluna é NULL quando ninguém trabalha no trabalho. Sempre que um processo começa a trabalhar no trabalho, ele define a coluna comocurrent_timestamp
, e quando completa uma parte, não apenas muda,status
mas também defineprocessing_since
como NULL.Um trabalho está disponível se
processing_since
for NULL ou anterior a um determinado intervalo de tempo que seja maior do que o necessário para concluir uma parte do trabalho. Dessa forma, o “bloqueio” expira depois de um tempo.Use bloqueios consultivos . Esses bloqueios podem durar mais que uma transação e não causam problemas com o autovacuum. Infelizmente você está usando UUIDs, caso contrário você poderia ter usado o
jobid
bloqueio de aviso. Você poderia adicionar outrabigint
coluna à tabela apenas para esse propósito.