From 4aa58cf378a397e58dbdf0528adf9e5e914d348a Mon Sep 17 00:00:00 2001 From: achingbrain Date: Thu, 14 Mar 2024 08:11:34 +0100 Subject: [PATCH] fix: add retries for dnslink Hitting external services can be flaky as we see periodic failures in CI for `ipfs.io` and friends so add a retry. --- packages/ipns/src/dnslink.ts | 5 ++--- packages/ipns/src/index.ts | 24 +++++++++++++++++++++- packages/ipns/test/resolve-dnslink.spec.ts | 17 +++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/packages/ipns/src/dnslink.ts b/packages/ipns/src/dnslink.ts index 24d71a0aa..9be046054 100644 --- a/packages/ipns/src/dnslink.ts +++ b/packages/ipns/src/dnslink.ts @@ -12,7 +12,7 @@ async function recursiveResolveDnslink (domain: string, depth: number, dns: DNS, throw new Error('recursion limit exceeded') } - log('query %s for TXT and CNAME records', domain) + log('query %s for TXT records', domain) const txtRecordsResponse = await dns.query(domain, { ...options, types: [ @@ -76,9 +76,8 @@ async function recursiveResolveDnslink (domain: string, depth: number, dns: DNS, } } - // no dnslink records found, try CNAMEs + // see if there is a CNAME delegation log('no DNSLink records found for %s, falling back to CNAME', domain) - const cnameRecordsResponse = await dns.query(domain, { ...options, types: [ diff --git a/packages/ipns/src/index.ts b/packages/ipns/src/index.ts index 72f9013af..a689d893c 100644 --- a/packages/ipns/src/index.ts +++ b/packages/ipns/src/index.ts @@ -253,6 +253,7 @@ const HOUR = 60 * MINUTE const DEFAULT_LIFETIME_MS = 24 * HOUR const DEFAULT_REPUBLISH_INTERVAL_MS = 23 * HOUR +const DEFAULT_DNSLINK_RETRIES = 3 export type PublishProgressEvents = ProgressEvent<'ipns:publish:start'> | @@ -321,6 +322,13 @@ export interface ResolveDNSLinkOptions extends AbortOptions, ProgressOptions { @@ -424,7 +432,21 @@ class DefaultIPNS implements IPNS { } async resolveDNSLink (domain: string, options: ResolveDNSLinkOptions = {}): Promise { - const dnslink = await resolveDNSLink(domain, this.dns, this.log, options) + let dnslink = '' + const retries = options.retries ?? DEFAULT_DNSLINK_RETRIES + + for (let i = 0; i < retries; i++) { + try { + dnslink = await resolveDNSLink(domain, this.dns, this.log, options) + break + } catch (err) { + if (i === (retries - 1)) { + throw err + } + + log.error('error resolving %s attempt %d of %d', domain, i + 1, retries, err) + } + } return this.#resolve(dnslink, options) } diff --git a/packages/ipns/test/resolve-dnslink.spec.ts b/packages/ipns/test/resolve-dnslink.spec.ts index 40c3ceb4a..9df6576e4 100644 --- a/packages/ipns/test/resolve-dnslink.spec.ts +++ b/packages/ipns/test/resolve-dnslink.spec.ts @@ -70,6 +70,23 @@ describe('resolveDNSLink', () => { expect(result.cid.toString()).to.equal('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') }) + it('should retry on failure', async () => { + dns.query.withArgs('foobar.baz') + .rejects(new CodeError('Not found', 'ENODATA')) + + dns.query.withArgs('_dnslink.foobar.baz') + .onFirstCall().rejects(new CodeError('Not found', 'ENODATA')) + .onSecondCall().resolves(dnsResponse([{ + name: '_dnslink.foobar.baz.', + TTL: 60, + type: RecordType.TXT, + data: 'dnslink=/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn' + }])) + + const result = await name.resolveDNSLink('foobar.baz', { nocache: true, offline: true }) + expect(result.cid.toString()).to.equal('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') + }) + it('should handle bad records', async () => { dns.query.withArgs('_dnslink.foobar.baz').resolves(dnsResponse([{ name: 'foobar.baz.',