From 9c1adfd05f5946855531e4dc20de91aaceee2277 Mon Sep 17 00:00:00 2001 From: Renato Alcara Date: Thu, 26 Feb 2026 18:54:19 -0300 Subject: [PATCH] perf(reconnect): eliminate race condition between connection:open and offline message delivery perf(reconnect): eliminate race condition between connection:open and offline message delivery --- src/Socket/socket.ts | 37 +++++++++++++++++++++++-------------- src/Types/Message.ts | 3 +-- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Socket/socket.ts b/src/Socket/socket.ts index 4ea30e8d..405140ff 100644 --- a/src/Socket/socket.ts +++ b/src/Socket/socket.ts @@ -1499,27 +1499,36 @@ export const makeSocket = (config: SocketConfig) => { }) // login complete ws.on('CB:success', async (node: BinaryNode) => { - try { - await uploadPreKeysToServerIfRequired() - await sendPassiveIq('active') - - // After successful login, validate our key-bundle against server - try { - await digestKeyBundle() - } catch (e) { - logger.warn({ e }, 'failed to run digest after login') - } - } catch (err) { - logger.warn({ err }, 'failed to send initial passive iq') - } - logger.info('opened connection to WA') clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try ev.emit('creds.update', { me: { ...authState.creds.me!, lid: node.attrs.lid } }) + // Emit connection:open immediately so the application layer begins processing + // incoming messages without waiting for any WA round-trips. ev.emit('connection.update', { connection: 'open' }) + // ─── Background: sendPassiveIq + pre-key validation + key-bundle digest ── + // sendPassiveIq('active') tells the WA edge server to start routing live + // messages to this connection. All three operations involve WA round-trips + // (100–500 ms each) but none need to complete before the app can handle + // messages. Run them in parallel, fire-and-forget, non-blocking. + Promise.allSettled([sendPassiveIq('active'), uploadPreKeysToServerIfRequired(), digestKeyBundle()]) + .then(results => { + const [passiveResult] = results + if (passiveResult.status === 'rejected') { + logger.warn({ err: passiveResult.reason }, 'failed to send initial passive iq') + } + for (const result of results.slice(1)) { + if (result.status === 'rejected') { + logger.warn({ err: result.reason }, 'background key operation failed after login (non-critical)') + } + } + }) + .catch(err => { + logger.error({ err }, 'unexpected error in background post-login handler') + }) + // Record successful connection metrics recordConnectionAttempt('success') incrementActiveConnections() diff --git a/src/Types/Message.ts b/src/Types/Message.ts index 8cc6192b..e78e8b45 100644 --- a/src/Types/Message.ts +++ b/src/Types/Message.ts @@ -1,4 +1,3 @@ -import Long from 'long' import type { Readable } from 'stream' import type { URL } from 'url' import { proto } from '../../WAProto/index.js' @@ -30,7 +29,7 @@ export type WAMessageKey = proto.IMessageKey & { export type PlaceholderMessageData = { key: WAMessageKey pushName?: string | null - messageTimestamp?: number | Long | null + messageTimestamp?: WAMessage['messageTimestamp'] participant?: string | null participantAlt?: string | null }