@@ -121,6 +121,10 @@ var registryLock sync.RWMutex
121121// credentialGroup ensures only one credential refresh happens per registry
122122var credentialGroup singleflight.Group
123123
124+ // Transport cache to avoid creating new transports for each request
125+ var transportCache = make (map [string ]* http.Transport )
126+ var transportCacheLock sync.RWMutex
127+
124128func AddRegistryEndpointFromConfig (epc RegistryConfiguration ) error {
125129 ep := NewRegistryEndpoint (epc .Prefix , epc .Name , epc .ApiURL , epc .Credentials , epc .DefaultNS , epc .Insecure , TagListSortFromString (epc .TagSortMode ), epc .Limit , epc .CredsExpire )
126130 return AddRegistryEndpoint (ep )
@@ -282,16 +286,65 @@ func (ep *RegistryEndpoint) DeepCopy() *RegistryEndpoint {
282286 return newEp
283287}
284288
289+ // ClearTransportCache clears the transport cache
290+ // This is useful when registry configuration changes
291+ func ClearTransportCache () {
292+ transportCacheLock .Lock ()
293+ transportCache = make (map [string ]* http.Transport )
294+ transportCacheLock .Unlock ()
295+ }
296+
285297// GetTransport returns a transport object for this endpoint
286- func (ep * RegistryEndpoint ) GetTransport () * http.Transport {
298+ // Implements connection pooling and reuse to avoid creating new transports for each request
299+ func (ep * RegistryEndpoint ) GetTransport () * http.Transport {
300+ // Check if we have a cached transport for this registry
301+ transportCacheLock .RLock ()
302+ if transport , exists := transportCache [ep .RegistryAPI ]; exists {
303+ log .Debugf ("Transport cache HIT for %s: %p" , ep .RegistryAPI , transport )
304+ transportCacheLock .RUnlock ()
305+ return transport
306+ }
307+ log .Debugf ("Transport cache MISS for %s" , ep .RegistryAPI )
308+ transportCacheLock .RUnlock ()
309+
310+ // Create a new transport with optimized connection pool settings
311+ transportCacheLock .Lock ()
312+ defer transportCacheLock .Unlock ()
313+
314+ // Double-check after acquiring write lock
315+ if transport , exists := transportCache [ep .RegistryAPI ]; exists {
316+ log .Debugf ("Transport cache double-check HIT for %s: %p" , ep .RegistryAPI , transport )
317+ return transport
318+ }
319+
320+ log .Debugf ("Creating NEW transport for %s" , ep .RegistryAPI )
321+
287322 tlsC := & tls.Config {}
288323 if ep .Insecure {
289324 tlsC .InsecureSkipVerify = true
290325 }
291- return & http.Transport {
292- Proxy : http .ProxyFromEnvironment ,
293- TLSClientConfig : tlsC ,
326+
327+ // Create transport with aggressive timeout and connection management
328+ transport := & http.Transport {
329+ Proxy : http .ProxyFromEnvironment ,
330+ TLSClientConfig : tlsC ,
331+ MaxIdleConns : 20 , // Reduced global max idle connections
332+ MaxIdleConnsPerHost : 5 , // Reduced per-host connections
333+ IdleConnTimeout : 30 * time .Second , // Reduced idle timeout
334+ TLSHandshakeTimeout : 5 * time .Second , // Reduced TLS timeout
335+ ExpectContinueTimeout : 1 * time .Second , // Expect-Continue timeout
336+ DisableKeepAlives : false , // Enable HTTP Keep-Alive
337+ ForceAttemptHTTP2 : true , // Enable HTTP/2 if available
338+ // Critical timeout settings to prevent hanging connections
339+ ResponseHeaderTimeout : 10 * time .Second , // Response header timeout
340+ MaxConnsPerHost : 10 , // Limit total connections per host
294341 }
342+
343+ // Cache the transport for reuse
344+ transportCache [ep .RegistryAPI ] = transport
345+ log .Debugf ("Cached NEW transport for %s: %p" , ep .RegistryAPI , transport )
346+
347+ return transport
295348}
296349
297350// init initializes the registry configuration
0 commit comments