Allocate message sequence numbers in the DB, not in memory
save() numbered new messages from Session.db_next_sequence, a counter read
when the session was loaded. Any second writer of the same session handed
out the numbers it still believed were free, and the two inserts collided on
UNIQUE(session_id, sequence_number) — in production, when switch_profile
loaded the session mid-run and saved it back. The turn died with
"Internal error: duplicate key value violates unique constraint".

The range is now claimed inside save()'s transaction with a single
UPDATE ... RETURNING, so concurrent writers serialize on the session row,
and GREATEST() seeds the pre-counter sessions whose next_sequence is still 0
instead of relying on a racy max()+1 fallback in memory.

switch_profile itself no longer saves a session at all: it repoints the row
through a narrow set_profile() UPDATE, which keeps it out of the running
turn's way. It runs mid-turn on a session the turn still holds, so saving a
second copy from there was the collision in the first place.
1 parent 8bc0258 commit bbd5248ad846db208f393e4697bc4cc95e00475d
@Eugene Sukhodolskiy Eugene Sukhodolskiy authored 10 hours ago
Showing 6 changed files
View
navi/core/pg_session_store.py
View
navi/core/session.py
View
navi/tools/switch_profile.py
View
tests/conftest_factory.py
View
tests/unit/core/test_pg_session_store.py
View
tests/unit/tools/test_switch_profile.py 0 → 100644