|
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. |
|---|
|
|
| navi/core/pg_session_store.py |
|---|
| navi/core/session.py |
|---|
| navi/tools/switch_profile.py |
|---|
| tests/conftest_factory.py |
|---|
| tests/unit/core/test_pg_session_store.py |
|---|
| tests/unit/tools/test_switch_profile.py 0 → 100644 |
|---|