UPDATE outbox_events
SET status = 1, locked_at = NOW()
WHERE id = (
SELECT id
FROM outbox_events
WHERE status = 0
ORDER BY id ASC
FOR UPDATE SKIP LOCKED
LIMIT 1
)
RETURNING *;
class OutboxClaimer
SQL = <<~SQL
UPDATE outbox_events
SET status = 1, locked_at = NOW()
WHERE id = (
SELECT id
FROM outbox_events
WHERE status = 0
ORDER BY id ASC
FOR UPDATE SKIP LOCKED
LIMIT 1
)
RETURNING *;
SQL
def claim
row = ApplicationRecord.connection.exec_query(SQL).first
row && OutboxEvent.instantiate(row)
end
end
If you have a queue table, avoid races by selecting and updating in one statement. Postgres UPDATE … RETURNING is the simplest building block for a correct custom queue / maintenance pipeline.