diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..51239789 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-23 - Flag Parsing Performance +**Learning:** `kingpin` flags are global variables but accessing and processing their values (e.g. `strings.Split` on a comma-separated list) inside a hot path (like HTTP request parsing) can cause significant overhead due to repeated allocations. +**Action:** Cache the processed value of configuration flags, especially for high-frequency operations. Use `sync.RWMutex` or `atomic.Value` to ensure thread-safe access if the flag value can change (even if only in tests). diff --git a/ebpftracer/l7/http.go b/ebpftracer/l7/http.go index 0fe69098..9adbad49 100644 --- a/ebpftracer/l7/http.go +++ b/ebpftracer/l7/http.go @@ -9,11 +9,50 @@ import ( "net/http" "net/url" "strings" + "sync" "unicode/utf8" "github.com/coroot/coroot-node-agent/flags" ) +var ( + sensitiveHeadersCache map[string]bool + sensitiveHeadersMu sync.RWMutex + lastSensitiveHeader string +) + +func getSensitiveHeaders() map[string]bool { + currentHeader := "" + if flags.SensitiveHeader != nil { + currentHeader = *flags.SensitiveHeader + } + + sensitiveHeadersMu.RLock() + if sensitiveHeadersCache != nil && lastSensitiveHeader == currentHeader { + m := sensitiveHeadersCache + sensitiveHeadersMu.RUnlock() + return m + } + sensitiveHeadersMu.RUnlock() + + sensitiveHeadersMu.Lock() + defer sensitiveHeadersMu.Unlock() + + // Double check + if sensitiveHeadersCache != nil && lastSensitiveHeader == currentHeader { + return sensitiveHeadersCache + } + + m := make(map[string]bool) + sensitiveKeysList := strings.Split(currentHeader, ",") + for _, key := range sensitiveKeysList { + m[strings.ToLower(strings.TrimSpace(key))] = true + } + sensitiveHeadersCache = m + lastSensitiveHeader = currentHeader + return m +} + // safeString converts bytes to string with UTF-8 validation // If bytes contain invalid UTF-8, returns base64 encoded version as fallback func safeString(data []byte) string { @@ -100,11 +139,7 @@ func ParseHTTPRequest(data []byte) (*http.Request, error) { var header = http.Header{} var host = "" headerLines := bytes.Split(headers, []byte("\n")) - sensitiveHeaders := make(map[string]bool) - sensitiveKeysList := strings.Split(*flags.SensitiveHeader, ",") - for _, key := range sensitiveKeysList { - sensitiveHeaders[strings.ToLower(strings.TrimSpace(key))] = true - } + sensitiveHeaders := getSensitiveHeaders() for _, line := range headerLines { // Skip empty lines if len(line) == 0 { @@ -252,11 +287,7 @@ func ParseHTTPResponse(data []byte) (*http.Response, error) { // Parse headers var header = http.Header{} - sensitiveHeaders := make(map[string]bool) - sensitiveKeysList := strings.Split(*flags.SensitiveHeader, ",") - for _, key := range sensitiveKeysList { - sensitiveHeaders[strings.ToLower(strings.TrimSpace(key))] = true - } + sensitiveHeaders := getSensitiveHeaders() headerLines := bytes.Split(headerData, []byte("\r\n")) if len(headerLines) == 1 {