Skip to content

Make drop_table idempotent so concurrent retention runs don't crash (#3595) - #3616

Open
youdie006 wants to merge 1 commit into
postalserver:mainfrom
youdie006:fix/drop-table-if-exists
Open

Make drop_table idempotent so concurrent retention runs don't crash (#3595)#3616
youdie006 wants to merge 1 commit into
postalserver:mainfrom
youdie006:fix/drop-table-if-exists

Conversation

@youdie006

Copy link
Copy Markdown

Problem

ProcessMessageRetentionScheduledTask crashes with Mysql2::Error: Unknown table when two workers run retention concurrently (#3595). The task's lock is stealable after a few minutes, so two workers can call Provisioner#drop_table for the same raw-<date> table; the second one runs a bare DROP TABLE on an already-dropped table and raises.

In lib/postal/message_db/provisioner.rb, drop_table was the one un-guarded destructive query. Its sibling create_raw_table already tolerates the same concurrent-run race with rescue Mysql2::Error ... raise unless ... already exists.

Fix

Use DROP TABLE IF EXISTS, so dropping an already-removed table is a no-op instead of an error. This mirrors the concurrent-run tolerance create_raw_table has, but at the SQL level so it doesn't swallow any unrelated Mysql2::Error.

Adds a spec asserting drop_table does not raise when the table no longer exists (it fails on the old bare DROP TABLE and passes with IF EXISTS).

Scope

This is the safe, idempotent guard for the crash itself. @willpower232 noted on the issue that the underlying lock-renewal behaviour (two workers holding the retention lock at once) may also be worth revisiting — that's a separate change, and IF EXISTS stops the crash regardless of whether the lock overlap is later tightened.


AI disclosure: prepared with AI assistance (Claude Code); the fix was traced against the code and syntax-checked with ruby -c before submitting. The message_db specs run against MySQL, so the added spec is exercised by CI.

ProcessMessageRetentionScheduledTask's lock is stealable after a few minutes, so
two workers can call Provisioner#drop_table for the same table. The bare DROP
TABLE then raises Mysql2::Error (Unknown table) on the second worker and crashes
the task (postalserver#3595). Use DROP TABLE IF EXISTS, mirroring the concurrent-run
tolerance create_raw_table already has via its rescue.

Adds a spec asserting drop_table does not raise when the table is gone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants