fix(messages-recv): wrap session deletions in transactions
CRITICAL FIX: Wraps session deletion operations in transactions to prevent
race conditions with concurrent session operations.
Changes:
- Wrap session deletion at line 468 (incoming retry) in transaction
- Wrap session deletion at line 1026 (outgoing retry) in transaction
- Use transaction key format: delete-session-${sessionId}
Problem before fix:
Session deletions happened OUTSIDE transactions while other operations
INSIDE transactions could be reading/writing the same session key.
Timeline of race condition:
T0: Message A arrives → processingMutex.mutex()
T1: Transaction started → reads session X
T2: Message B (retry) → shouldRecreateSession()
T3: Message B deletes session X ← OUTSIDE transaction
T4: Message A tries to use session X in transaction
T5: Session doesn't exist → decryption failure
T6: Message A lost
After fix:
All session operations (read/write/delete) are serialized via transactions,
preventing concurrent access and data corruption.
https://claude.ai/code/session_VMxqX
This commit is contained in:
@@ -465,7 +465,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
if (shouldRecreateSession) {
|
||||
logger.debug({ fromJid, retryCount, reason: recreateReason, errorCode }, 'recreating session for retry')
|
||||
// Delete existing session to force recreation
|
||||
await authState.keys.set({ session: { [sessionId]: null } })
|
||||
// CRITICAL: Wrap in transaction to prevent race with other session operations
|
||||
await authState.keys.transaction(async () => {
|
||||
await authState.keys.set({ session: { [sessionId]: null } })
|
||||
}, `delete-session-${sessionId}`)
|
||||
forceIncludeKeys = true
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1023,7 +1026,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
|
||||
if (shouldRecreateSession) {
|
||||
logger.debug({ participant, retryCount, reason: recreateReason, errorCode }, 'recreating session for outgoing retry')
|
||||
await authState.keys.set({ session: { [sessionId]: null } })
|
||||
// CRITICAL: Wrap in transaction to prevent race with other session operations
|
||||
await authState.keys.transaction(async () => {
|
||||
await authState.keys.set({ session: { [sessionId]: null } })
|
||||
}, `delete-session-${sessionId}`)
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry')
|
||||
|
||||
Reference in New Issue
Block a user