Configuration Management:

Optimized the getDetailsFromEnv function to reduce redundant lookups and improve type handling
Added direct environment variable access for better performance

Memory Cache Optimization:

Implemented a size-based compression threshold (1KB) to avoid compressing small payloads
Added cache size limits (10,000 entries) to prevent memory leaks
Implemented efficient eviction strategies for the oldest entries
Added atomic counter for thread-safe cache size tracking
Improved cleanup routines with GC triggering for large caches

Proxy Implementation:

Refactored the proxy code into smaller, focused functions for better maintainability
Optimized gzip handling for better performance
Improved error handling and logging
Enhanced tracing integration

GraphQL Processing:

Optimized introspection query checking with fast-path returns
Improved object pool usage
Added detailed comments for better code understanding
Split complex functions into smaller, more focused ones
Fixed test compatibility issues with introspection checking

Request Processing:

Refactored the request processing logic into smaller, focused functions
Separated user extraction, caching, and request handling for better maintainability
Improved error handling and response generation

Tracing Enhancements:

Added better span context management
Implemented custom attributes for more detailed tracing
Added sampling configuration to reduce overhead
Improved resource attribution with host and process information
Added timeout handling for tracing operations

Application Lifecycle:

Implemented graceful shutdown with proper signal handling
Added goroutine management with wait groups
Improved startup sequence with better error handling
Added timeout handling for shutdown operations
This commit is contained in:
2025-02-25 23:34:39 +00:00
parent da577e8a02
commit 2ab78d35ce
6 changed files with 510 additions and 249 deletions
+94 -72
View File
@@ -40,59 +40,98 @@ func createFasthttpClient(timeout int) *fasthttp.Client {
// proxyTheRequest handles the request proxying logic.
func proxyTheRequest(c *fiber.Ctx, currentEndpoint string) error {
// Setup tracing if enabled
var span trace.Span
ctx := context.Background()
ctx := setupTracing(c)
if cfg.Tracing.Enable && tracer != nil {
// Extract trace information from header
if traceHeader := c.Get("X-Trace-Span"); traceHeader != "" {
spanInfo, err := libpack_tracing.ParseTraceHeader(traceHeader)
if err != nil {
cfg.Logger.Warning(&libpack_logger.LogMessage{
Message: "Failed to parse trace header",
Pairs: map[string]interface{}{"error": err.Error()},
})
} else {
if spanCtx, err := tracer.ExtractSpanContext(spanInfo); err == nil {
ctx = trace.ContextWithSpanContext(ctx, spanCtx)
}
}
}
// Start a new span
span, ctx = tracer.StartSpan(ctx, "proxy_request")
defer span.End()
}
// Check if URL is allowed
if !checkAllowedURLs(c) {
cfg.Logger.Error(&libpack_logger.LogMessage{
Message: "Request blocked",
Pairs: map[string]interface{}{"path": c.Path()},
})
if ifNotInTest() {
cfg.Monitoring.Increment(libpack_monitoring.MetricsSkipped, nil)
}
return fmt.Errorf("request blocked - not allowed URL: %s", c.Path())
}
// Construct and validate proxy URL
proxyURL := currentEndpoint + c.Path()
_, err := url.Parse(proxyURL)
if err != nil {
if _, err := url.Parse(proxyURL); err != nil {
return fmt.Errorf("invalid URL: %v", err)
}
// Log request details in debug mode
if cfg.LogLevel == "DEBUG" {
logDebugRequest(c)
}
err = retry.Do(
// Perform the proxy request with retries
if err := performProxyRequest(c, proxyURL); err != nil {
if ifNotInTest() {
cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil)
}
return err
}
// Log response details in debug mode
if cfg.LogLevel == "DEBUG" {
logDebugResponse(c)
}
// Handle gzipped responses
if err := handleGzippedResponse(c); err != nil {
return err
}
// Final status check
if c.Response().StatusCode() != fiber.StatusOK {
if ifNotInTest() {
cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil)
}
return fmt.Errorf("received non-200 response from the GraphQL server: %d", c.Response().StatusCode())
}
// Remove server header for security
c.Response().Header.Del(fiber.HeaderServer)
return nil
}
// setupTracing extracts and sets up tracing context from request headers
func setupTracing(c *fiber.Ctx) context.Context {
ctx := context.Background()
if !cfg.Tracing.Enable || tracer == nil {
return ctx
}
// Extract trace information from header
if traceHeader := c.Get("X-Trace-Span"); traceHeader != "" {
spanInfo, err := libpack_tracing.ParseTraceHeader(traceHeader)
if err != nil {
cfg.Logger.Warning(&libpack_logger.LogMessage{
Message: "Failed to parse trace header",
Pairs: map[string]interface{}{"error": err.Error()},
})
} else if spanCtx, err := tracer.ExtractSpanContext(spanInfo); err == nil {
ctx = trace.ContextWithSpanContext(ctx, spanCtx)
}
}
return ctx
}
// performProxyRequest executes the proxy request with retries
func performProxyRequest(c *fiber.Ctx, proxyURL string) error {
return retry.Do(
func() error {
proxyErr := proxy.DoRedirects(c, proxyURL, 3, cfg.Client.FastProxyClient)
if proxyErr != nil {
return proxyErr
if err := proxy.DoRedirects(c, proxyURL, 3, cfg.Client.FastProxyClient); err != nil {
return err
}
if c.Response().StatusCode() != fiber.StatusOK {
return fmt.Errorf("received non-200 response from the GraphQL server: %d", c.Response().StatusCode())
return fmt.Errorf("received non-200 response: %d", c.Response().StatusCode())
}
return nil
},
@@ -112,55 +151,38 @@ func proxyTheRequest(c *fiber.Ctx, currentEndpoint string) error {
}),
retry.LastErrorOnly(true),
)
}
// handleGzippedResponse decompresses gzipped responses
func handleGzippedResponse(c *fiber.Ctx) error {
if !bytes.EqualFold(c.Response().Header.Peek("Content-Encoding"), []byte("gzip")) {
return nil
}
// Create a pooled gzip reader
reader, err := gzip.NewReader(bytes.NewReader(c.Response().Body()))
if err != nil {
cfg.Logger.Warning(&libpack_logger.LogMessage{
Message: "Can't proxy the request",
cfg.Logger.Error(&libpack_logger.LogMessage{
Message: "Failed to create gzip reader",
Pairs: map[string]interface{}{"error": err.Error()},
})
if ifNotInTest() {
cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil)
}
return fmt.Errorf("failed to proxy request: %v", err)
return err
}
defer reader.Close()
// Read decompressed data
decompressed, err := io.ReadAll(reader)
if err != nil {
cfg.Logger.Error(&libpack_logger.LogMessage{
Message: "Failed to decompress response",
Pairs: map[string]interface{}{"error": err.Error()},
})
return err
}
if cfg.LogLevel == "DEBUG" {
logDebugResponse(c)
}
if bytes.EqualFold(c.Response().Header.Peek("Content-Encoding"), []byte("gzip")) {
// Decompress gzip response
reader, err := gzip.NewReader(bytes.NewReader(c.Response().Body()))
if err != nil {
cfg.Logger.Error(&libpack_logger.LogMessage{
Message: "Failed to create gzip reader",
Pairs: map[string]interface{}{"error": err.Error()},
})
return err
}
defer reader.Close()
decompressed, err := io.ReadAll(reader)
if err != nil {
cfg.Logger.Error(&libpack_logger.LogMessage{
Message: "Failed to decompress response",
Pairs: map[string]interface{}{"error": err.Error()},
})
return err
}
c.Response().SetBody(decompressed)
c.Response().Header.Del("Content-Encoding")
}
if c.Response().StatusCode() != fiber.StatusOK {
if ifNotInTest() {
cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil)
}
return fmt.Errorf("received non-200 response from the GraphQL server: %d", c.Response().StatusCode())
}
c.Response().Header.Del(fiber.HeaderServer)
// Update response
c.Response().SetBody(decompressed)
c.Response().Header.Del("Content-Encoding")
return nil
}