From 4112ecdf6d6c7c28b0958f6bc0851b7dd9690020 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 24 Nov 2025 16:31:16 +1100 Subject: [PATCH 1/2] fix: allow node 500ms to pick ipv4/6 instead of default 250 --- ts/session/apis/seed_node_api/SeedNodeAPI.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ts/session/apis/seed_node_api/SeedNodeAPI.ts b/ts/session/apis/seed_node_api/SeedNodeAPI.ts index 9bd8c294f..4d54b03dd 100644 --- a/ts/session/apis/seed_node_api/SeedNodeAPI.ts +++ b/ts/session/apis/seed_node_api/SeedNodeAPI.ts @@ -1,7 +1,8 @@ import https from 'https'; import tls from 'tls'; - +import { setDefaultAutoSelectFamilyAttemptTimeout } from 'net'; import _ from 'lodash'; + // eslint-disable-next-line import/no-named-default import { default as insecureNodeFetch } from 'node-fetch'; import pRetry from 'p-retry'; @@ -275,7 +276,10 @@ async function getSnodesFromSeedUrl(urlObj: URL): Promise> { agent: sslAgent, }; window?.log?.info(`insecureNodeFetch => plaintext for getSnodesFromSeedUrl ${url}`); - + // Note: node has a default timeout of 250ms to pick ipv4 or ipv6 address, but sometimes it times out + // Increase that duration to 500ms as it seems to be resolving our issues. + // see https://github.com/nodejs/undici/issues/2990#issuecomment-2408883876 + setDefaultAutoSelectFamilyAttemptTimeout(500); const response = await insecureNodeFetch(url, fetchOptions); if (response.status !== 200) { From 2841cb377ec0602eeabdb594f440e31edc68c1ce Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 24 Nov 2025 16:31:42 +1100 Subject: [PATCH 2/2] fix: onboarding waits for first snode to reply with messages and not all of them --- ts/session/apis/snode_api/swarmPolling.ts | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/ts/session/apis/snode_api/swarmPolling.ts b/ts/session/apis/snode_api/swarmPolling.ts index 2f31927d1..78f6bc643 100644 --- a/ts/session/apis/snode_api/swarmPolling.ts +++ b/ts/session/apis/snode_api/swarmPolling.ts @@ -977,7 +977,7 @@ export class SwarmPolling { ); } - const allResultsFromUserProfile = await Promise.allSettled( + const firstResultWithMessagesUserProfile = await Promise.any( swarmSnodes.map(async toPollFrom => { // Note: always print something so we know if the polling is hanging window.log.info( @@ -996,21 +996,25 @@ export class SwarmPolling { window.log.info( `[onboarding] pollOnceForOurDisplayName of ${ed25519Str(pubkey.key)} from snode: ${ed25519Str(toPollFrom.pubkey_ed25519)} namespaces: ${[SnodeNamespaces.UserProfile]} returned: ${retrieved?.length}` ); + if (!retrieved?.length) { + /** + * Sometimes, a snode is out of sync with its swarm but still replies with what he thinks is the swarm's content. + * When that happens, we can get a "no display name" error, as indeed, that snode didn't have a config message on user profile. + * To fix this, we've added a check over all of the snodes of our swarm, and we pick the first one that reports having a config message on user profile. + * This won't take care of the case where a snode has a message with an empty display name, but it's not the root issue that this was added for. + */ + + throw new Error( + `pollOnceForOurDisplayName of ${ed25519Str(pubkey.key)} from snode: ${ed25519Str(toPollFrom.pubkey_ed25519)} no results from user profile` + ); + } return retrieved; }) ); - const resultsFromUserProfile = flatten( - compact( - allResultsFromUserProfile - .filter(promise => promise.status === 'fulfilled') - .map(promise => promise.value) - ) - ); - // check if we just fetched the details from the config namespaces. // If yes, merge them together and exclude them from the rest of the messages. - if (!resultsFromUserProfile?.length) { + if (!firstResultWithMessagesUserProfile?.length) { throw new NotFoundError('[pollOnceForOurDisplayName] resultsFromUserProfile is empty'); } @@ -1021,7 +1025,7 @@ export class SwarmPolling { } const userConfigMessagesWithNamespace: Array> = - resultsFromUserProfile.map(r => { + firstResultWithMessagesUserProfile.map(r => { return (r.messages.messages || []).map(m => { return { ...m, namespace: SnodeNamespaces.UserProfile }; });