@@ -14,6 +14,7 @@ import (
1414 "github.com/argoproj-labs/argocd-image-updater/registry-scanner/pkg/image"
1515 "github.com/argoproj-labs/argocd-image-updater/registry-scanner/pkg/log"
1616
17+ memcache "github.com/patrickmn/go-cache"
1718 "go.uber.org/ratelimit"
1819 "golang.org/x/sync/singleflight"
1920)
@@ -123,6 +124,10 @@ var registryLock sync.RWMutex
123124// credentialGroup ensures only one credential refresh happens per registry
124125var credentialGroup singleflight.Group
125126
127+ // Transport cache to avoid creating new transports for each request
128+ // Using go-cache with 30 minute expiration and 10 minute cleanup interval
129+ var transportCache = memcache .New (30 * time .Minute , 10 * time .Minute )
130+
126131func AddRegistryEndpointFromConfig (ctx context.Context , epc RegistryConfiguration ) error {
127132 ep := NewRegistryEndpoint (epc .Prefix , epc .Name , epc .ApiURL , epc .Credentials , epc .DefaultNS , epc .Insecure , TagListSortFromString (epc .TagSortMode ), epc .Limit , epc .CredsExpire )
128133 return AddRegistryEndpoint (ctx , ep )
@@ -311,16 +316,76 @@ func (ep *RegistryEndpoint) DeepCopy() *RegistryEndpoint {
311316 return newEp
312317}
313318
319+ // ClearTransportCache clears the transport cache
320+ // This is useful when registry configuration changes
321+ func ClearTransportCache () {
322+ transportCache .Flush ()
323+ }
324+
314325// GetTransport returns a transport object for this endpoint
326+ // Implements connection pooling and reuse to avoid creating new transports for each request
315327func (ep * RegistryEndpoint ) GetTransport () * http.Transport {
328+ // Check if we have a cached transport for this registry
329+ if cachedTransport , found := transportCache .Get (ep .RegistryAPI ); found {
330+ transport := cachedTransport .(* http.Transport )
331+ log .Debugf ("Transport cache HIT for %s: %p" , ep .RegistryAPI , transport )
332+
333+ // Validate that the transport is still usable
334+ if isTransportValid (transport ) {
335+ return transport
336+ }
337+
338+ // Transport is stale, remove it from cache
339+ log .Debugf ("Transport for %s is stale, removing from cache" , ep .RegistryAPI )
340+ transportCache .Delete (ep .RegistryAPI )
341+ }
342+
343+ log .Debugf ("Transport cache MISS for %s" , ep .RegistryAPI )
344+
345+ // Create a new transport with optimized connection pool settings
316346 tlsC := & tls.Config {}
317347 if ep .Insecure {
318348 tlsC .InsecureSkipVerify = true
319349 }
320- return & http.Transport {
321- Proxy : http .ProxyFromEnvironment ,
322- TLSClientConfig : tlsC ,
350+
351+ // Create transport with aggressive timeout and connection management
352+ transport := & http.Transport {
353+ Proxy : http .ProxyFromEnvironment ,
354+ TLSClientConfig : tlsC ,
355+ MaxIdleConns : 20 , // Reduced global max idle connections
356+ MaxIdleConnsPerHost : 5 , // Reduced per-host connections
357+ IdleConnTimeout : 90 * time .Second , // Reduced idle timeout
358+ TLSHandshakeTimeout : 10 * time .Second , // Reduced TLS timeout
359+ ExpectContinueTimeout : 1 * time .Second , // Expect-Continue timeout
360+ DisableKeepAlives : false , // Enable HTTP Keep-Alive
361+ ForceAttemptHTTP2 : true , // Enable HTTP/2 if available
362+ // Critical timeout settings to prevent hanging connections
363+ ResponseHeaderTimeout : 10 * time .Second , // Response header timeout
364+ MaxConnsPerHost : 10 , // Limit total connections per host
323365 }
366+
367+ // Cache the transport for reuse with default expiration (30 minutes)
368+ transportCache .Set (ep .RegistryAPI , transport , memcache .DefaultExpiration )
369+ log .Debugf ("Cached NEW transport for %s: %p" , ep .RegistryAPI , transport )
370+
371+ return transport
372+ }
373+
374+ // isTransportValid checks if a cached transport is still valid and usable
375+ func isTransportValid (transport * http.Transport ) bool {
376+ // Basic validation - check if transport is not nil and has valid configuration
377+ if transport == nil {
378+ return false
379+ }
380+
381+ // Check if the transport's connection settings are reasonable
382+ // This is a simple validation, more sophisticated checks could be added
383+ if transport .MaxIdleConns < 0 || transport .MaxIdleConnsPerHost < 0 {
384+ return false
385+ }
386+
387+ // Transport appears to be valid
388+ return true
324389}
325390
326391// init initializes the registry configuration
0 commit comments