The session garbage collector ran DELETE FROM Sessions WHERE access < ?
against an unindexed column, forcing a full table scan and taking gap
locks across the access range. With REPLACE INTO Sessions happening on
every authenticated request, this is a deadlock hotspot.
- Add Sessions_access_idx on Sessions(access) in both fresh-install
schema (zm_create.sql.in) and a migration (zm_update-1.39.10.sql).
- Rewrite ZMSessionHandler::gc to a two-phase delete: SELECT up to 100
expired ids via the new index (consistent read, no locks), then
DELETE WHERE id IN (...) by primary key. InnoDB takes record locks
only on the matched rows, not gap locks on the access range.
- Bump version to 1.39.10 so zmupdate.pl picks up the new migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>