Files
insights-go/system_context.go
T
Dustin ByrneandGitHub 139d59d59a fix: dramatically reduce memory allocations in feature flag evaluation (#172)
Addresses high RAM usage and OOMs when using feature flags at scale.
Tested with 853 flags + 12 cohorts (production-like setup):

  Before: 17,463 allocs/op, 201 MB/op, 51 ms/op
  After:    139 allocs/op,  56 KB/op, 129 µs/op

Key changes:
- Cache flagsByKey in flagsState at load time (was O(N²) in GetAllFlags)
- sha1.Sum with stack buffer instead of sha1.New + hash.Write (zero heap escapes)
- Append '.' separator inside calculateHash buffer (avoid string concat alloc)
- Cache getSystemContext/getVersion with sync.Once
- Pre-compute at load time: variant lookup tables, decoded payloads, parsed cohort values
- Cache compiled regexps via sync.Map
- Eliminate double flag evaluation (single-pass GetFeatureFlagWithPayload)
- Lazy evaluationCache only for flags with dependencies
- Sentinel errors + direct type assertions replacing errors.As
- valueToString replacing fmt.Sprintf throughout matchProperty
- Defer empty map allocation in validate() to remote path only
- Pre-merge distinct_id once before GetAllFlags loop
- Embed payload/variant strings in FeatureFlagResult struct
- Skip Properties merge for flags without person property conditions
2026-03-26 09:55:28 +01:00

58 lines
1.4 KiB
Go

package posthog
import (
"runtime"
"sync"
)
type osInfo struct {
Name string `json:"$os"`
Version string `json:"$os_version,omitempty"`
Distro string `json:"$os_distro,omitempty"`
}
type sysContext struct {
osInfo
GoVersion string `json:"$go_version"`
}
// cachedSystemContext holds the cached system context, computed once per process.
// OS info and Go version don't change during runtime, so there's no need to
// re-compute (and re-exec sw_vers/uname) on every event.
var (
cachedSystemContext sysContext
cachedSystemContextOnce sync.Once
// cachedSysContextProps holds the pre-built properties from the cached system context.
// This avoids creating a new Properties map on every ToProperties() call.
cachedSysContextProps Properties
cachedSysContextPropsOnce sync.Once
)
func getSystemContext() sysContext {
cachedSystemContextOnce.Do(func() {
cachedSystemContext = sysContext{
osInfo: getOSInfo(),
GoVersion: runtime.Version(),
}
})
return cachedSystemContext
}
func (ctx sysContext) ToProperties() Properties {
cachedSysContextPropsOnce.Do(func() {
sc := getSystemContext()
props := Properties{
"$os": sc.Name,
"$go_version": sc.GoVersion,
}
if sc.Version != "" {
props["$os_version"] = sc.Version
}
if sc.Distro != "" {
props["$os_distro"] = sc.Distro
}
cachedSysContextProps = props
})
return cachedSysContextProps
}