perf(messages): enable parallel message processing
perf(messages): enable parallel message processing with KeyedMutex + …
This commit is contained in:
+3
-3
@@ -41,7 +41,7 @@ import {
|
||||
newLTHashState,
|
||||
processSyncAction
|
||||
} from '../Utils'
|
||||
import { makeMutex } from '../Utils/make-mutex'
|
||||
import { makeMutex, makeKeyedMutex } from '../Utils/make-mutex'
|
||||
import processMessage from '../Utils/process-message'
|
||||
import { buildTcTokenFromJid } from '../Utils/tc-token-utils'
|
||||
import {
|
||||
@@ -74,8 +74,8 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
||||
|
||||
let syncState: SyncState = SyncState.Connecting
|
||||
|
||||
/** this mutex ensures that messages are processed in order */
|
||||
const messageMutex = makeMutex()
|
||||
/** this mutex ensures that messages from the same chat are processed in order, while allowing parallel processing of messages from different chats */
|
||||
const messageMutex = makeKeyedMutex()
|
||||
|
||||
/** this mutex ensures that receipts are processed in order */
|
||||
const receiptMutex = makeMutex()
|
||||
|
||||
@@ -1265,8 +1265,24 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
)
|
||||
}
|
||||
|
||||
// CRITICAL: Normalize JIDs BEFORE acquiring mutex to ensure messages from the same
|
||||
// chat (arriving with different JID formats - LID vs PN) use the SAME mutex key.
|
||||
// This prevents parallel processing of messages from the same conversation which
|
||||
// would break message ordering guarantees.
|
||||
// Addresses Copilot/Codex PR #75 critical review: JID normalization vulnerability
|
||||
await normalizeMessageJids(msg, signalRepository, logger)
|
||||
|
||||
try {
|
||||
await messageMutex.mutex(async () => {
|
||||
// Use KeyedMutex with NORMALIZED remoteJid for parallel processing across different chats
|
||||
// while maintaining sequential order within the same chat
|
||||
// Fallback chain: remoteJid (normalized) > msg.key.id (unique) > 'unknown' (serializes all)
|
||||
let mutexKey = msg.key.remoteJid
|
||||
if (!mutexKey) {
|
||||
logger.warn({ msgId: msg.key.id, fromMe: msg.key.fromMe }, 'Missing remoteJid after normalization, using msg.key.id as fallback')
|
||||
mutexKey = msg.key.id || 'unknown'
|
||||
}
|
||||
|
||||
await messageMutex.mutex(mutexKey, async () => {
|
||||
await decrypt()
|
||||
// message failed to decrypt
|
||||
if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
|
||||
@@ -1480,7 +1496,8 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
}
|
||||
}
|
||||
|
||||
await normalizeMessageJids(msg, signalRepository, logger)
|
||||
// JID normalization moved BEFORE mutex acquisition (line 1273) to prevent race conditions
|
||||
// cleanMessage still runs inside mutex to ensure atomic message processing
|
||||
cleanMessage(msg, authState.creds.me!.id, authState.creds.me!.lid!)
|
||||
|
||||
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify')
|
||||
|
||||
@@ -1639,7 +1639,12 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
// Emit own event for album root if configured
|
||||
if (config.emitOwnEvents) {
|
||||
process.nextTick(async () => {
|
||||
await messageMutex.mutex(() => upsertMessage(albumRootMsg, 'append'))
|
||||
let mutexKey = albumRootMsg.key.remoteJid
|
||||
if (!mutexKey) {
|
||||
logger.warn({ msgId: albumRootMsg.key.id }, 'Missing remoteJid in albumRootMsg, using msg.key.id as fallback')
|
||||
mutexKey = albumRootMsg.key.id || 'unknown'
|
||||
}
|
||||
await messageMutex.mutex(mutexKey, () => upsertMessage(albumRootMsg, 'append'))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1732,7 +1737,12 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
// Emit own event if configured
|
||||
if (config.emitOwnEvents) {
|
||||
process.nextTick(async () => {
|
||||
await messageMutex.mutex(() => upsertMessage(mediaMsg, 'append'))
|
||||
let mutexKey = mediaMsg.key.remoteJid
|
||||
if (!mutexKey) {
|
||||
logger.warn({ msgId: mediaMsg.key.id }, 'Missing remoteJid in mediaMsg, using msg.key.id as fallback')
|
||||
mutexKey = mediaMsg.key.id || 'unknown'
|
||||
}
|
||||
await messageMutex.mutex(mutexKey, () => upsertMessage(mediaMsg, 'append'))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1931,7 +1941,12 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
})
|
||||
if (config.emitOwnEvents) {
|
||||
process.nextTick(async () => {
|
||||
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'))
|
||||
let mutexKey = fullMsg.key.remoteJid
|
||||
if (!mutexKey) {
|
||||
logger.warn({ msgId: fullMsg.key.id }, 'Missing remoteJid in fullMsg, using msg.key.id as fallback')
|
||||
mutexKey = fullMsg.key.id || 'unknown'
|
||||
}
|
||||
await messageMutex.mutex(mutexKey, () => upsertMessage(fullMsg, 'append'))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user