feat: Mutex redesign - isolate event processing for better ordering and throughput (#2137)

* fix: improve message resend logic by adding checks for message IDs

* Revert "fix: improve message resend logic by adding checks for message IDs"

This reverts commit c03f9d8e6fc6cbfbb9d1f8f67c169700e704213d.

* fix: introduce separate mutexes for message, receipt, app state patch, and notification processing

* fix: remove unused processing mutex for notifications
This commit is contained in:
Matheus Filype
2025-12-11 23:34:59 -03:00
committed by GitHub
parent 8e93934eb7
commit 829fa8d601
3 changed files with 25 additions and 10 deletions
+17 -4
View File
@@ -72,8 +72,18 @@ export const makeChatsSocket = (config: SocketConfig) => {
let privacySettings: { [_: string]: string } | undefined
let syncState: SyncState = SyncState.Connecting
/** this mutex ensures that the notifications (receipts, messages etc.) are processed in order */
const processingMutex = makeMutex()
/** this mutex ensures that messages are processed in order */
const messageMutex = makeMutex()
/** this mutex ensures that receipts are processed in order */
const receiptMutex = makeMutex()
/** this mutex ensures that app state patches are processed in order */
const appStatePatchMutex = makeMutex()
/** this mutex ensures that notifications are processed in order */
const notificationMutex = makeMutex()
// Timeout for AwaitingInitialSync state
let awaitingSyncTimeout: NodeJS.Timeout | undefined
@@ -748,7 +758,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
let initial: LTHashState
let encodeResult: { patch: proto.ISyncdPatch; state: LTHashState }
await processingMutex.mutex(async () => {
await appStatePatchMutex.mutex(async () => {
await authState.keys.transaction(async () => {
logger.debug({ patch: patchCreate }, 'applying app patch')
@@ -1180,7 +1190,10 @@ export const makeChatsSocket = (config: SocketConfig) => {
...sock,
createCallLink,
getBotListV2,
processingMutex,
messageMutex,
receiptMutex,
appStatePatchMutex,
notificationMutex,
fetchPrivacySettings,
upsertMessage,
appPatch,
+6 -4
View File
@@ -71,7 +71,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
ev,
authState,
ws,
processingMutex,
messageMutex,
notificationMutex,
receiptMutex,
signalRepository,
query,
upsertMessage,
@@ -1067,7 +1069,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
try {
await Promise.all([
processingMutex.mutex(async () => {
receiptMutex.mutex(async () => {
const status = getStatusFromReceiptType(attrs.type)
if (
typeof status !== 'undefined' &&
@@ -1141,7 +1143,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
try {
await Promise.all([
processingMutex.mutex(async () => {
notificationMutex.mutex(async () => {
const msg = await processNotification(node)
if (msg) {
const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me!.id)
@@ -1218,7 +1220,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
}
try {
await processingMutex.mutex(async () => {
await messageMutex.mutex(async () => {
await decrypt()
// message failed to decrypt
if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
+2 -2
View File
@@ -71,7 +71,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const {
ev,
authState,
processingMutex,
messageMutex,
signalRepository,
upsertMessage,
query,
@@ -1199,7 +1199,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
})
if (config.emitOwnEvents) {
process.nextTick(async () => {
await processingMutex.mutex(() => upsertMessage(fullMsg, 'append'))
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'))
})
}