@@ -31,6 +31,7 @@ import (
3131 "net/url"
3232 "runtime"
3333 "runtime/debug"
34+ "sync"
3435 "sync/atomic"
3536 "time"
3637
@@ -66,22 +67,23 @@ type Service struct {
6667 * core.BaseProxyComponents
6768
6869 // Object pools for zero-allocation operations
69- bufferPool * pool.Pool [* []byte ]
70- requestPool * pool.Pool [* requestContext ]
71- responsePool * pool.Pool [[]byte ]
72- errorPool * pool.Pool [* errorContext ]
70+ bufferPool * pool.Pool [* []byte ]
71+ requestPool * pool.Pool [* requestContext ]
72+ errorPool * pool.Pool [* errorContext ]
7373
7474 transport * http.Transport
7575 configuration * Configuration
7676 retryHandler * core.RetryHandler
7777
78- // Cleanup management
7978 cleanupTicker * time.Ticker
8079 cleanupStop chan struct {}
8180
8281 // Per-endpoint connection pools and circuit breakers
8382 endpointPools xsync.Map [string , * connectionPool ]
8483 circuitBreakers xsync.Map [string , * circuitBreaker ]
84+
85+ // Cleanup management
86+ cleanupOnce sync.Once
8587}
8688
8789// connectionPool isolates HTTP transport instances per endpoint
@@ -177,13 +179,6 @@ func NewService(
177179 return nil , fmt .Errorf ("failed to create request pool: %w" , err )
178180 }
179181
180- responsePool , err := pool .NewLitePool (func () []byte {
181- return make ([]byte , 32 * 1024 ) // 32KB for response bodies
182- })
183- if err != nil {
184- return nil , fmt .Errorf ("failed to create response pool: %w" , err )
185- }
186-
187182 errorPool , err := pool .NewLitePool (func () * errorContext {
188183 return & errorContext {}
189184 })
@@ -197,7 +192,6 @@ func NewService(
197192 BaseProxyComponents : base ,
198193 bufferPool : bufferPool ,
199194 requestPool : requestPool ,
200- responsePool : responsePool ,
201195 errorPool : errorPool ,
202196 transport : transport ,
203197 configuration : configuration ,
@@ -328,104 +322,6 @@ func (s *Service) ProxyRequestToEndpoints(ctx context.Context, w http.ResponseWr
328322 return s .ProxyRequestToEndpointsWithRetry (ctx , w , r , endpoints , stats , rlog )
329323}
330324
331- // proxyToSingleEndpointLegacy retained for reference during migration
332- // TODO: Remove after retry logic stability confirmed
333- func (s * Service ) proxyToSingleEndpointLegacy (ctx context.Context , w http.ResponseWriter , r * http.Request , endpoints []* domain.Endpoint , stats * ports.RequestStats , rlog logger.StyledLogger ) (err error ) {
334- // Get request context from pool
335- reqCtx := s .requestPool .Get ()
336- defer s .requestPool .Put (reqCtx )
337-
338- reqCtx .requestID = stats .RequestID
339- reqCtx .startTime = stats .StartTime
340-
341- // Panic recovery
342- defer func () {
343- if rec := recover (); rec != nil {
344- s .handlePanic (ctx , w , r , stats , rlog , rec , & err )
345- }
346- }()
347-
348- s .IncrementRequests ()
349-
350- // Use context logger if available, fallback to provided logger
351- ctxLogger := middleware .GetLogger (ctx )
352- if ctxLogger != nil {
353- ctxLogger .Debug ("Olla proxy request started" ,
354- "method" , r .Method ,
355- "url" , r .URL .String (),
356- "endpoint_count" , len (endpoints ))
357- } else {
358- rlog .Debug ("proxy request started" , "method" , r .Method , "url" , r .URL .String ())
359- }
360-
361- if len (endpoints ) == 0 {
362- if ctxLogger != nil {
363- ctxLogger .Error ("No healthy endpoints available for request" )
364- } else {
365- rlog .Error ("no healthy endpoints available" )
366- }
367- s .RecordFailure (ctx , nil , time .Since (stats .StartTime ), common .ErrNoHealthyEndpoints )
368- return common .ErrNoHealthyEndpoints
369- }
370-
371- if ctxLogger != nil {
372- ctxLogger .Debug ("Using provided endpoints" , "count" , len (endpoints ))
373- } else {
374- rlog .Debug ("using provided endpoints" , "count" , len (endpoints ))
375- }
376-
377- // Select endpoint with circuit breaker check
378- endpoint , cb := s .selectEndpointWithCircuitBreaker (endpoints , rlog )
379- if endpoint == nil {
380- s .RecordFailure (ctx , nil , time .Since (stats .StartTime ), errors .New ("all endpoints circuit breakers open" ))
381- return errors .New ("all endpoints unavailable due to circuit breakers" )
382- }
383-
384- stats .EndpointName = endpoint .Name
385- reqCtx .endpoint = endpoint .Name
386-
387- // Track connections
388- s .Selector .IncrementConnections (endpoint )
389- defer s .Selector .DecrementConnections (endpoint )
390-
391- // Build target URL
392- targetURL := s .buildTargetURL (r , endpoint )
393- stats .TargetUrl = targetURL .String ()
394- reqCtx .targetURL = targetURL .String ()
395-
396- if ctxLogger != nil {
397- ctxLogger .Info ("Request dispatching" ,
398- "endpoint" , endpoint .Name ,
399- "target" , stats .TargetUrl ,
400- "model" , stats .Model )
401- } else {
402- rlog .Info ("Request dispatching" , "endpoint" , endpoint .Name , "target" , stats .TargetUrl , "model" , stats .Model )
403- }
404-
405- // Create and prepare proxy request
406- // Rewrite model name in request body if this is an alias-resolved request
407- core .RewriteModelForAlias (ctx , r , endpoint )
408-
409- proxyReq , err := s .prepareProxyRequest (ctx , r , targetURL , stats )
410- if err != nil {
411- cb .RecordFailure ()
412- s .RecordFailure (ctx , endpoint , time .Since (stats .StartTime ), err )
413- return fmt .Errorf ("failed to create proxy request: %w" , err )
414- }
415-
416- rlog .Debug ("created proxy request" )
417-
418- // Execute backend request
419- resp , err := s .executeBackendRequest (ctx , endpoint , proxyReq , cb , stats , rlog )
420- if err != nil {
421- return err
422- }
423- defer resp .Body .Close ()
424-
425- // Handle successful response
426- return s .handleSuccessfulResponse (ctx , w , r , resp , endpoint , cb , stats , rlog )
427- }
428-
429325// handlePanic handles panic recovery in proxy requests
430326func (s * Service ) handlePanic (ctx context.Context , w http.ResponseWriter , r * http.Request , stats * ports.RequestStats , rlog logger.StyledLogger , rec interface {}, err * error ) {
431327 s .RecordFailure (ctx , nil , time .Since (stats .StartTime ), fmt .Errorf ("panic: %v" , rec ))
@@ -483,7 +379,7 @@ func (s *Service) prepareProxyRequest(ctx context.Context, r *http.Request, targ
483379 stats .HeaderProcessingMs = time .Since (headerStart ).Milliseconds ()
484380
485381 // Add model header
486- if model , ok := ctx .Value ("model" ).(string ); ok && model != "" {
382+ if model , ok := ctx .Value (constants . ContextModelKey ).(string ); ok && model != "" {
487383 proxyReq .Header .Set ("X-Model" , model )
488384 stats .Model = model
489385 }
@@ -777,29 +673,31 @@ func (s *Service) cleanupUnusedResources() {
777673 }
778674}
779675
780- // Cleanup cleans up resources
676+ // Cleanup cleans up resources. Safe to call more than once.
781677func (s * Service ) Cleanup () {
782- // Stop cleanup goroutine
783- if s .cleanupStop != nil {
784- close (s .cleanupStop )
785- }
786- if s .cleanupTicker != nil {
787- s .cleanupTicker .Stop ()
788- }
678+ s .cleanupOnce .Do (func () {
679+ // Stop cleanup goroutine
680+ if s .cleanupStop != nil {
681+ close (s .cleanupStop )
682+ }
683+ if s .cleanupTicker != nil {
684+ s .cleanupTicker .Stop ()
685+ }
789686
790- // Close all endpoint pools
791- s .endpointPools .Range (func (key string , pool * connectionPool ) bool {
792- pool .transport .CloseIdleConnections ()
793- return true
794- })
687+ // Close all endpoint pools
688+ s .endpointPools .Range (func (key string , pool * connectionPool ) bool {
689+ pool .transport .CloseIdleConnections ()
690+ return true
691+ })
795692
796- s .endpointPools .Clear ()
797- s .circuitBreakers .Clear ()
693+ s .endpointPools .Clear ()
694+ s .circuitBreakers .Clear ()
798695
799- s .BaseProxyComponents .Shutdown ()
696+ s .BaseProxyComponents .Shutdown ()
800697
801- // force GC to clean up
802- runtime .GC ()
698+ // force GC to clean up
699+ runtime .GC ()
803700
804- s .Logger .Debug ("Olla proxy service cleaned up" )
701+ s .Logger .Debug ("Olla proxy service cleaned up" )
702+ })
805703}
0 commit comments