Skip to content

Commit c61958f

Browse files
authored
Merge pull request #1147 from Krishanx92/report
Improve perf
2 parents 6294a42 + f7c4864 commit c61958f

2 files changed

Lines changed: 84 additions & 36 deletions

File tree

gateway/gateway-runtime/docker-entrypoint.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ export ROUTER_XDS_PORT="${ROUTER_XDS_PORT:-18000}"
8080
export POLICY_ENGINE_XDS_PORT="${POLICY_ENGINE_XDS_PORT:-18001}"
8181
export LOG_LEVEL="${LOG_LEVEL:-info}"
8282

83+
# Performance tuning configuration
84+
# GOMAXPROCS limits Go's CPU usage - set to leave cores for Envoy (default: 2)
85+
# ROUTER_CONCURRENCY sets Envoy's worker thread count (default: auto-detect, 0 means use all cores)
86+
# APIP_GW_POLICY_ENGINE_METRICS_ENABLED controls metrics collection (default: true, set false for high-load)
87+
export GOMAXPROCS="${GOMAXPROCS:-2}"
88+
export ROUTER_CONCURRENCY="${ROUTER_CONCURRENCY:-0}"
89+
export APIP_GW_POLICY_ENGINE_METRICS_ENABLED="${APIP_GW_POLICY_ENGINE_METRICS_ENABLED:-true}"
90+
8391
# Derive Router (Envoy) xDS config — used by envsubst on config-override.yaml
8492
export XDS_SERVER_HOST="${GATEWAY_CONTROLLER_HOST}"
8593
export XDS_SERVER_PORT="${ROUTER_XDS_PORT}"
@@ -95,6 +103,9 @@ log " Router xDS: ${GATEWAY_CONTROLLER_HOST}:${ROUTER_XDS_PORT}"
95103
log " Policy Engine xDS: ${PE_XDS_SERVER}"
96104
log " Log Level: ${LOG_LEVEL}"
97105
log " Policy Engine Socket: ${POLICY_ENGINE_SOCKET}"
106+
log " GOMAXPROCS: ${GOMAXPROCS}"
107+
log " Router Concurrency: ${ROUTER_CONCURRENCY}"
108+
log " Policy Engine Metrics: ${APIP_GW_POLICY_ENGINE_METRICS_ENABLED}"
98109
[[ ${#ROUTER_ARGS[@]} -gt 0 ]] && log " Router extra args: ${ROUTER_ARGS[*]}"
99110
[[ ${#PE_ARGS[@]} -gt 0 ]] && log " Policy Engine extra args: ${PE_ARGS[*]}"
100111

@@ -174,6 +185,7 @@ log "Starting Envoy..."
174185
-c /etc/envoy/envoy.yaml \
175186
--config-yaml "${CONFIG_OVERRIDE}" \
176187
--log-level "${LOG_LEVEL}" \
188+
--concurrency "${ROUTER_CONCURRENCY}" \
177189
"${ROUTER_ARGS[@]}" \
178190
> >(while IFS= read -r line; do echo "[rtr] $line"; done) \
179191
2> >(while IFS= read -r line; do echo "[rtr] $line" >&2; done) &

gateway/gateway-runtime/policy-engine/internal/kernel/extproc.go

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"fmt"
2525
"io"
2626
"log/slog"
27+
"sync"
2728
"time"
2829

2930
core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
@@ -55,6 +56,11 @@ type ExternalProcessorServer struct {
5556
kernel *Kernel
5657
executor *executor.ChainExecutor
5758
tracer trace.Tracer
59+
60+
// routeMetadataCache caches parsed route metadata by raw metadata string.
61+
// This avoids expensive prototext.Unmarshal on every request (~11% CPU savings).
62+
// Key: raw metadata string from Envoy, Value: parsed RouteMetadata
63+
routeMetadataCache sync.Map
5864
}
5965

6066
// NewExternalProcessorServer creates a new ExternalProcessorServer
@@ -398,7 +404,9 @@ type RouteMetadata struct {
398404
ProjectID string
399405
}
400406

401-
// extractRouteMetadata extracts the route metadata from Envoy metadata
407+
// extractRouteMetadata extracts the route metadata from Envoy metadata.
408+
// Uses a cache keyed by the raw metadata string to avoid repeated prototext.Unmarshal
409+
// calls which account for ~11% CPU usage according to profiling.
402410
func (s *ExternalProcessorServer) extractRouteMetadata(req *extprocv3.ProcessingRequest) RouteMetadata {
403411
metadata := RouteMetadata{}
404412

@@ -421,43 +429,61 @@ func (s *ExternalProcessorServer) extractRouteMetadata(req *extprocv3.Processing
421429
// Extract API metadata from xds.route_metadata
422430
if routeMetadataValue, ok := extProcAttrs.Fields["xds.route_metadata"]; ok {
423431
if metadataStr := routeMetadataValue.GetStringValue(); metadataStr != "" {
424-
// Parse the protobuf text format string using prototext
425-
var envoyMetadata core.Metadata
426-
if err := prototext.Unmarshal([]byte(metadataStr), &envoyMetadata); err != nil {
427-
slog.Warn("Failed to unmarshal route metadata", "error", err)
432+
// Check cache first - routes with the same metadata string get cache hits
433+
if cached, ok := s.routeMetadataCache.Load(metadataStr); ok {
434+
cachedMeta := cached.(RouteMetadata)
435+
// Copy API fields from cache, keep the route name from this request
436+
metadata.APIId = cachedMeta.APIId
437+
metadata.APIName = cachedMeta.APIName
438+
metadata.APIVersion = cachedMeta.APIVersion
439+
metadata.Context = cachedMeta.Context
440+
metadata.OperationPath = cachedMeta.OperationPath
441+
metadata.Vhost = cachedMeta.Vhost
442+
metadata.APIKind = cachedMeta.APIKind
443+
metadata.TemplateHandle = cachedMeta.TemplateHandle
444+
metadata.ProviderName = cachedMeta.ProviderName
445+
metadata.ProjectID = cachedMeta.ProjectID
428446
} else {
429-
// Extract fields from "wso2.route" filter metadata
430-
if routeStruct, ok := envoyMetadata.FilterMetadata["wso2.route"]; ok && routeStruct.Fields != nil {
431-
if apiIdValue, ok := routeStruct.Fields["api_id"]; ok {
432-
metadata.APIId = apiIdValue.GetStringValue()
433-
}
434-
if apiNameValue, ok := routeStruct.Fields["api_name"]; ok {
435-
metadata.APIName = apiNameValue.GetStringValue()
436-
}
437-
if apiVersionValue, ok := routeStruct.Fields["api_version"]; ok {
438-
metadata.APIVersion = apiVersionValue.GetStringValue()
439-
}
440-
if apiContextValue, ok := routeStruct.Fields["api_context"]; ok {
441-
metadata.Context = apiContextValue.GetStringValue()
442-
}
443-
if operationPath, ok := routeStruct.Fields["path"]; ok {
444-
metadata.OperationPath = operationPath.GetStringValue()
445-
}
446-
if vhostValue, ok := routeStruct.Fields["vhost"]; ok {
447-
metadata.Vhost = vhostValue.GetStringValue()
448-
}
449-
if originalAPIKindValue, ok := routeStruct.Fields["api_kind"]; ok {
450-
metadata.APIKind = originalAPIKindValue.GetStringValue()
451-
}
452-
if templateHandleValue, ok := routeStruct.Fields["template_handle"]; ok {
453-
metadata.TemplateHandle = templateHandleValue.GetStringValue()
454-
}
455-
if providerNameValue, ok := routeStruct.Fields["provider_name"]; ok {
456-
metadata.ProviderName = providerNameValue.GetStringValue()
457-
}
458-
if projectIDValue, ok := routeStruct.Fields["project_id"]; ok {
459-
metadata.ProjectID = projectIDValue.GetStringValue()
447+
// Cache miss - parse the protobuf text format string
448+
var envoyMetadata core.Metadata
449+
if err := prototext.Unmarshal([]byte(metadataStr), &envoyMetadata); err != nil {
450+
slog.Warn("Failed to unmarshal route metadata", "error", err)
451+
} else {
452+
// Extract fields from "wso2.route" filter metadata
453+
if routeStruct, ok := envoyMetadata.FilterMetadata["wso2.route"]; ok && routeStruct.Fields != nil {
454+
if apiIdValue, ok := routeStruct.Fields["api_id"]; ok {
455+
metadata.APIId = apiIdValue.GetStringValue()
456+
}
457+
if apiNameValue, ok := routeStruct.Fields["api_name"]; ok {
458+
metadata.APIName = apiNameValue.GetStringValue()
459+
}
460+
if apiVersionValue, ok := routeStruct.Fields["api_version"]; ok {
461+
metadata.APIVersion = apiVersionValue.GetStringValue()
462+
}
463+
if apiContextValue, ok := routeStruct.Fields["api_context"]; ok {
464+
metadata.Context = apiContextValue.GetStringValue()
465+
}
466+
if operationPath, ok := routeStruct.Fields["path"]; ok {
467+
metadata.OperationPath = operationPath.GetStringValue()
468+
}
469+
if vhostValue, ok := routeStruct.Fields["vhost"]; ok {
470+
metadata.Vhost = vhostValue.GetStringValue()
471+
}
472+
if originalAPIKindValue, ok := routeStruct.Fields["api_kind"]; ok {
473+
metadata.APIKind = originalAPIKindValue.GetStringValue()
474+
}
475+
if templateHandleValue, ok := routeStruct.Fields["template_handle"]; ok {
476+
metadata.TemplateHandle = templateHandleValue.GetStringValue()
477+
}
478+
if providerNameValue, ok := routeStruct.Fields["provider_name"]; ok {
479+
metadata.ProviderName = providerNameValue.GetStringValue()
480+
}
481+
if projectIDValue, ok := routeStruct.Fields["project_id"]; ok {
482+
metadata.ProjectID = projectIDValue.GetStringValue()
483+
}
460484
}
485+
// Cache the parsed metadata for future requests
486+
s.routeMetadataCache.Store(metadataStr, metadata)
461487
}
462488
}
463489
}
@@ -471,6 +497,16 @@ func (s *ExternalProcessorServer) extractRouteMetadata(req *extprocv3.Processing
471497
return metadata
472498
}
473499

500+
// ClearRouteMetadataCache clears the route metadata cache.
501+
// Call this when routes are updated via xDS to ensure stale metadata is not used.
502+
func (s *ExternalProcessorServer) ClearRouteMetadataCache() {
503+
s.routeMetadataCache.Range(func(key, value interface{}) bool {
504+
s.routeMetadataCache.Delete(key)
505+
return true
506+
})
507+
slog.Info("Route metadata cache cleared")
508+
}
509+
474510
// generateRequestID generates a unique request identifier
475511
func (s *ExternalProcessorServer) generateRequestID() string {
476512
return uuid.New().String()

0 commit comments

Comments
 (0)