Repository navigation
Conversation
…umer start startConsumer() called enable_notify_insert(), which drops and recreates the trigger on the queue table. That takes a table lock: a (re)starting worker waits for every open transaction that inserted into the queue, blocks new inserts meanwhile, and two workers of one queue starting together can deadlock. The trigger is now created once with the queue; the consumer only listens.
Uses thesis/pgmq ensureNotifyInsert(): it reads the catalog when notifications are in place (no table lock, no DDL), restores them when lost (crash recovery empties the UNLOGGED throttle table), and serializes concurrent starts under the queue lock.
Recent PHPStan 2.2.x infers the nested property assignment correctly, so the ignore is unmatched and phpstan fails on every cell of the CI matrix (8.4/8.5 x lowest/highest). (cherry picked from commit ec45573)
|
Reworked and turned into a draft. The first version (notifications only in It needs a |
|
CI status, for clarity: tests are green. phpstan reports Suggested merge order: thesis-php/pgmq#21, thesis-php/pgmq#22 → |
Fixes #3. Draft: needs a
thesis/pgmqrelease with thesis-php/pgmq#22 (it callsQueue::ensureNotifyInsert()). Until then phpstan reportsCall to an undefined method Thesis\Pgmq\Queue::ensureNotifyInsert(); once released, thethesis/pgmqconstraint incomposer.jsonshould be raised to that version.Problem
startConsumer()calledenableNotifyInsert(), which drops and recreates the trigger on the queue table on every start. That takes a table lock: a (re)starting worker waits for every open transaction that inserted into the queue, blocks new inserts meanwhile, and workers starting together race into the DDL. Observed:deadlock detectedafter two workers were recycled in the same tick.Change
createQueue()andstartConsumer()call$queue->ensureNotifyInsert()fromthesis/pgmq. In one transaction underpgmq.acquire_queue_lock()it:update_notify_insert()for another interval;enable_notify_insert()when anything is missing, disabled or foreign: crash recovery, a queue created before, concurrent cold starts (they pass one by one).thesis/pgmq; the transport only calls it.Verified
Integration tests in #8 (red on
0.5.x, green with #6 + this PR + thesis-php/pgmq#22, verified locally against that pgmq branch):consumerStartDoesNotWaitForProducerTransactions;concurrentConsumerStartsCreateTheTriggerOnce(event trigger countsCREATE TRIGGER; 4 workers with separate pools);consumerStartRestoresNotificationsLostInCrashRecovery;consumerStartRestoresDisabledTrigger;createdQueueWakesConsumerOnInsert.Stress harness (real Postgres, pgmq 1.11.1 and 1.13.0, real crashes via
kill -9):0.5.xalready exists,deadlock detected