fix(messages-recv): use consistent transaction key for session operations
CRITICAL FIX: Addresses Codex Bot comment on PR #79 about transaction key mismatch. Problem: Session delete operations used `delete-session-${sessionId}` as transaction key, while encrypt/decrypt operations in sendMessage() use `meId` as key. Different keys = different mutexes = operations can run concurrently = race condition. Timeline before fix: T0: sendMessage() → transaction(meId) → mutex_meId acquired T1: Encrypt uses session X T2: shouldRecreateSession() → transaction(delete-session-X) → mutex_delete acquired T3: Delete session X ← CONCURRENT! T4: sendMessage() tries to use session X → CRASH Changes: - Line 472: Change key from `delete-session-${sessionId}` to `authState.creds.me?.id` - Line 1034: Same change for outgoing retry deletion - Now all session operations (read/write/delete/encrypt) share same mutex - Operations are properly serialized, preventing concurrent access After fix: T0: sendMessage() → transaction(meId) → mutex_meId acquired T1: shouldRecreateSession() → transaction(meId) → waits for mutex T2: sendMessage() completes → mutex released T3: shouldRecreateSession() acquires mutex → deletes session safely Validation: - ✅ Both session deletions now use same key as encrypt operations - ✅ Cross-file contract respected (messages-send.ts:1385 uses meId) - ✅ Race condition eliminated via mutex serialization https://claude.ai/code/session_VMxqX
This commit is contained in:
@@ -465,10 +465,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
if (shouldRecreateSession) {
|
||||
logger.debug({ fromJid, retryCount, reason: recreateReason, errorCode }, 'recreating session for retry')
|
||||
// Delete existing session to force recreation
|
||||
// CRITICAL: Wrap in transaction to prevent race with other session operations
|
||||
// CRITICAL: Use same transaction key as encrypt/decrypt operations to prevent race
|
||||
// Using meId ensures this delete serializes with sendMessage() and other session operations
|
||||
await authState.keys.transaction(async () => {
|
||||
await authState.keys.set({ session: { [sessionId]: null } })
|
||||
}, `delete-session-${sessionId}`)
|
||||
}, authState.creds.me?.id || 'session-operation')
|
||||
forceIncludeKeys = true
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1026,10 +1027,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
|
||||
if (shouldRecreateSession) {
|
||||
logger.debug({ participant, retryCount, reason: recreateReason, errorCode }, 'recreating session for outgoing retry')
|
||||
// CRITICAL: Wrap in transaction to prevent race with other session operations
|
||||
// CRITICAL: Use same transaction key as encrypt/decrypt operations to prevent race
|
||||
// Using meId ensures this delete serializes with sendMessage() and other session operations
|
||||
await authState.keys.transaction(async () => {
|
||||
await authState.keys.set({ session: { [sessionId]: null } })
|
||||
}, `delete-session-${sessionId}`)
|
||||
}, authState.creds.me?.id || 'session-operation')
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry')
|
||||
|
||||
Reference in New Issue
Block a user