@@ -14,6 +14,7 @@ import (
14
14
15
15
"go.uber.org/ratelimit"
16
16
"golang.org/x/sync/singleflight"
17
+ memcache "github.com/patrickmn/go-cache"
17
18
)
18
19
19
20
// TagListSort defines how the registry returns the list of tags
@@ -121,6 +122,10 @@ var registryLock sync.RWMutex
121
122
// credentialGroup ensures only one credential refresh happens per registry
122
123
var credentialGroup singleflight.Group
123
124
125
+ // Transport cache to avoid creating new transports for each request
126
+ // Using go-cache with 30 minute expiration and 10 minute cleanup interval
127
+ var transportCache = memcache .New (30 * time .Minute , 10 * time .Minute )
128
+
124
129
func AddRegistryEndpointFromConfig (epc RegistryConfiguration ) error {
125
130
ep := NewRegistryEndpoint (epc .Prefix , epc .Name , epc .ApiURL , epc .Credentials , epc .DefaultNS , epc .Insecure , TagListSortFromString (epc .TagSortMode ), epc .Limit , epc .CredsExpire )
126
131
return AddRegistryEndpoint (ep )
@@ -282,16 +287,76 @@ func (ep *RegistryEndpoint) DeepCopy() *RegistryEndpoint {
282
287
return newEp
283
288
}
284
289
290
+ // ClearTransportCache clears the transport cache
291
+ // This is useful when registry configuration changes
292
+ func ClearTransportCache () {
293
+ transportCache .Flush ()
294
+ }
295
+
285
296
// GetTransport returns a transport object for this endpoint
297
+ // Implements connection pooling and reuse to avoid creating new transports for each request
286
298
func (ep * RegistryEndpoint ) GetTransport () * http.Transport {
299
+ // Check if we have a cached transport for this registry
300
+ if cachedTransport , found := transportCache .Get (ep .RegistryAPI ); found {
301
+ transport := cachedTransport .(* http.Transport )
302
+ log .Debugf ("Transport cache HIT for %s: %p" , ep .RegistryAPI , transport )
303
+
304
+ // Validate that the transport is still usable
305
+ if isTransportValid (transport ) {
306
+ return transport
307
+ }
308
+
309
+ // Transport is stale, remove it from cache
310
+ log .Debugf ("Transport for %s is stale, removing from cache" , ep .RegistryAPI )
311
+ transportCache .Delete (ep .RegistryAPI )
312
+ }
313
+
314
+ log .Debugf ("Transport cache MISS for %s" , ep .RegistryAPI )
315
+
316
+ // Create a new transport with optimized connection pool settings
287
317
tlsC := & tls.Config {}
288
318
if ep .Insecure {
289
319
tlsC .InsecureSkipVerify = true
290
320
}
291
- return & http.Transport {
292
- Proxy : http .ProxyFromEnvironment ,
293
- TLSClientConfig : tlsC ,
321
+
322
+ // Create transport with aggressive timeout and connection management
323
+ transport := & http.Transport {
324
+ Proxy : http .ProxyFromEnvironment ,
325
+ TLSClientConfig : tlsC ,
326
+ MaxIdleConns : 20 , // Reduced global max idle connections
327
+ MaxIdleConnsPerHost : 5 , // Reduced per-host connections
328
+ IdleConnTimeout : 30 * time .Second , // Reduced idle timeout
329
+ TLSHandshakeTimeout : 5 * time .Second , // Reduced TLS timeout
330
+ ExpectContinueTimeout : 1 * time .Second , // Expect-Continue timeout
331
+ DisableKeepAlives : false , // Enable HTTP Keep-Alive
332
+ ForceAttemptHTTP2 : true , // Enable HTTP/2 if available
333
+ // Critical timeout settings to prevent hanging connections
334
+ ResponseHeaderTimeout : 10 * time .Second , // Response header timeout
335
+ MaxConnsPerHost : 10 , // Limit total connections per host
294
336
}
337
+
338
+ // Cache the transport for reuse with default expiration (30 minutes)
339
+ transportCache .Set (ep .RegistryAPI , transport , memcache .DefaultExpiration )
340
+ log .Debugf ("Cached NEW transport for %s: %p" , ep .RegistryAPI , transport )
341
+
342
+ return transport
343
+ }
344
+
345
+ // isTransportValid checks if a cached transport is still valid and usable
346
+ func isTransportValid (transport * http.Transport ) bool {
347
+ // Basic validation - check if transport is not nil and has valid configuration
348
+ if transport == nil {
349
+ return false
350
+ }
351
+
352
+ // Check if the transport's connection settings are reasonable
353
+ // This is a simple validation, more sophisticated checks could be added
354
+ if transport .MaxIdleConns < 0 || transport .MaxIdleConnsPerHost < 0 {
355
+ return false
356
+ }
357
+
358
+ // Transport appears to be valid
359
+ return true
295
360
}
296
361
297
362
// init initializes the registry configuration
0 commit comments