@@ -33,6 +33,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
33
33
private readonly peerRouting : PeerRouting
34
34
private readonly filterAddrs ?: string [ ]
35
35
private readonly filterProtocols ?: string [ ]
36
+ private readonly inFlightRequests : Map < string , Promise < Response > >
36
37
37
38
/**
38
39
* Create a new DelegatedContentRouting instance
@@ -44,6 +45,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
44
45
this . httpQueue = new PQueue ( {
45
46
concurrency : init . concurrentRequests ?? defaultValues . concurrentRequests
46
47
} )
48
+ this . inFlightRequests = new Map ( ) // Tracks in-flight requests to avoid duplicate requests
47
49
this . clientUrl = url instanceof URL ? url : new URL ( url )
48
50
this . timeout = init . timeout ?? defaultValues . timeout
49
51
this . filterAddrs = init . filterAddrs
@@ -95,7 +97,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
95
97
const url = new URL ( `${ this . clientUrl } routing/v1/providers/${ cid . toString ( ) } ` )
96
98
this . #addFilterParams( url , options . filterAddrs , options . filterProtocols )
97
99
const getOptions = { headers : { Accept : 'application/x-ndjson' } , signal }
98
- const res = await fetch ( url , getOptions )
100
+ const res = await this . #makeRequest ( url . toString ( ) , getOptions )
99
101
100
102
if ( res . status === 404 ) {
101
103
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
@@ -162,7 +164,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
162
164
this . #addFilterParams( url , options . filterAddrs , options . filterProtocols )
163
165
164
166
const getOptions = { headers : { Accept : 'application/x-ndjson' } , signal }
165
- const res = await fetch ( url , getOptions )
167
+ const res = await this . #makeRequest ( url . toString ( ) , getOptions )
166
168
167
169
if ( res . status === 404 ) {
168
170
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
@@ -228,7 +230,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
228
230
await onStart . promise
229
231
230
232
const getOptions = { headers : { Accept : 'application/vnd.ipfs.ipns-record' } , signal }
231
- const res = await fetch ( resource , getOptions )
233
+ const res = await this . #makeRequest ( resource , getOptions )
232
234
233
235
log ( 'getIPNS GET %s %d' , resource , res . status )
234
236
@@ -290,7 +292,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
290
292
const body = marshalIPNSRecord ( record )
291
293
292
294
const getOptions = { method : 'PUT' , headers : { 'Content-Type' : 'application/vnd.ipfs.ipns-record' } , body, signal }
293
- const res = await fetch ( resource , getOptions )
295
+ const res = await this . #makeRequest ( resource , getOptions )
294
296
295
297
log ( 'putIPNS PUT %s %d' , resource , res . status )
296
298
@@ -349,4 +351,24 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
349
351
}
350
352
}
351
353
}
354
+
355
+ // Ensures that only one concurrent request is made for the same URL with the same method
356
+ async #makeRequest ( url : string , options : RequestInit ) : Promise < Response > {
357
+ const key = `${ options . method ?? 'GET' } -${ url } `
358
+
359
+ // Check if there's already an in-flight request for this URL
360
+ const existingRequest = this . inFlightRequests . get ( key )
361
+ if ( existingRequest != null ) {
362
+ return existingRequest
363
+ }
364
+
365
+ // Create new request and track it
366
+ const requestPromise = fetch ( url , options ) . finally ( ( ) => {
367
+ // Clean up the tracked request when it completes
368
+ this . inFlightRequests . delete ( key )
369
+ } )
370
+
371
+ this . inFlightRequests . set ( key , requestPromise )
372
+ return requestPromise
373
+ }
352
374
}
0 commit comments