Findings
1) Process-global config resolver is overwritten per receiver construction
Location
x-pack/libbeat/cmd/instance/beat.go:132-146
Evidence
- Constructor rewrites resolver options via
config.OverwriteConfigOpts(...):
x-pack/libbeat/cmd/instance/beat.go:133-136
x-pack/libbeat/cmd/instance/beat.go:140-145
OverwriteConfigOpts in elastic-agent-libs/config updates a package-global configOpts guarded by mutex (module file: .../elastic-agent-libs@v0.33.3/config/config.go, lines 44-77, 153-156).
What is wrong
Receiver-specific keystore/env resolver settings are written to global process state during NewBeatForReceiver.
Why it matters
In one OTel Collector process, concurrent or sequential construction of multiple receivers can make one receiver parse/unpack config with another receiver’s resolver settings (cross-instance config/secret resolution behavior).
Suggested fix
Make resolver options instance-scoped in receiver construction (pass options directly to local parse/unpack flow), instead of calling process-global OverwriteConfigOpts.
2) Metricbeat receiver construction uses and mutates global paths.Paths
Location
x-pack/libbeat/cmd/instance/beat.go:118-123
libbeat/cmd/instance/beat.go:1496-1511
Evidence
- Metricbeat branch does:
instance.InitPaths(cfg) then b.Paths = paths.Paths (x-pack/libbeat/cmd/instance/beat.go:119-123)
InitPaths calls paths.InitPaths(...) (libbeat/cmd/instance/beat.go:1509-1510), which mutates singleton paths.Paths in elastic-agent-libs/paths (var Paths = New(), InitPaths, *paths = *cfg; module file .../elastic-agent-libs@v0.33.3/paths/paths.go, lines 71, 99, 106).
- Filebeat receiver branch already avoids this by using
p := paths.New(); p.InitPaths(...); b.Paths = p (x-pack/libbeat/cmd/instance/beat.go:113-117).
What is wrong
Receiver construction in the metricbeat path still binds to process-global paths state.
Why it matters
Multiple receivers in the same process can overwrite each other’s path.* values, causing incorrect data/log/meta path usage and potential racey behavior during concurrent construction.
Suggested fix
Mirror filebeat branch behavior for metricbeat receiver construction: allocate per-instance paths.New() and initialize that object, never assign b.Paths = paths.Paths.
3) Receiver construction mutates global timestamp formatter
Location
x-pack/libbeat/cmd/instance/beat.go:180-181
libbeat/common/datetime.go:105-122 and readers at 137, 173
Evidence
- Constructor calls
common.SetTimestampPrecision(b.Config.TimestampPrecision) (x-pack/libbeat/cmd/instance/beat.go:180-181).
SetTimestampPrecision comment explicitly states it changes format globally and mutates package vars tsFmt / timeFormatter (libbeat/common/datetime.go:105-108, 118-120).
What is wrong
Per-receiver config controls process-global timestamp formatting state.
Why it matters
One receiver’s precision setting can silently affect all receivers in-process; concurrent construction with runtime formatting reads can produce inconsistent formatting and race-risk in -race runs.
Suggested fix
Make timestamp precision instance-local for receiver pipelines/encoders; avoid changing package-global formatter in receiver constructor path.
Safe/Already Good
management.SetManagerFactory(...) path is mutex-protected (libbeat/management/management.go:114-118) and does not by itself encode receiver-specific state.
x-pack/libbeat/cmd/instance/beat.go:42-52 FQDN caching uses sync.OnceValues and is not per-receiver mutable config.
Suggested Actions
What is this? | From workflow: Sweeper: OTel BeatReceiver Global State Isolation
Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.
Findings
1) Process-global config resolver is overwritten per receiver construction
Location
x-pack/libbeat/cmd/instance/beat.go:132-146Evidence
config.OverwriteConfigOpts(...):x-pack/libbeat/cmd/instance/beat.go:133-136x-pack/libbeat/cmd/instance/beat.go:140-145OverwriteConfigOptsinelastic-agent-libs/configupdates a package-globalconfigOptsguarded by mutex (module file:.../elastic-agent-libs@v0.33.3/config/config.go, lines44-77,153-156).What is wrong
Receiver-specific keystore/env resolver settings are written to global process state during
NewBeatForReceiver.Why it matters
In one OTel Collector process, concurrent or sequential construction of multiple receivers can make one receiver parse/unpack config with another receiver’s resolver settings (cross-instance config/secret resolution behavior).
Suggested fix
Make resolver options instance-scoped in receiver construction (pass options directly to local parse/unpack flow), instead of calling process-global
OverwriteConfigOpts.2) Metricbeat receiver construction uses and mutates global
paths.PathsLocation
x-pack/libbeat/cmd/instance/beat.go:118-123libbeat/cmd/instance/beat.go:1496-1511Evidence
instance.InitPaths(cfg)thenb.Paths = paths.Paths(x-pack/libbeat/cmd/instance/beat.go:119-123)InitPathscallspaths.InitPaths(...)(libbeat/cmd/instance/beat.go:1509-1510), which mutates singletonpaths.Pathsinelastic-agent-libs/paths(var Paths = New(),InitPaths,*paths = *cfg; module file.../elastic-agent-libs@v0.33.3/paths/paths.go, lines71,99,106).p := paths.New(); p.InitPaths(...); b.Paths = p(x-pack/libbeat/cmd/instance/beat.go:113-117).What is wrong
Receiver construction in the metricbeat path still binds to process-global paths state.
Why it matters
Multiple receivers in the same process can overwrite each other’s
path.*values, causing incorrect data/log/meta path usage and potential racey behavior during concurrent construction.Suggested fix
Mirror filebeat branch behavior for metricbeat receiver construction: allocate per-instance
paths.New()and initialize that object, never assignb.Paths = paths.Paths.3) Receiver construction mutates global timestamp formatter
Location
x-pack/libbeat/cmd/instance/beat.go:180-181libbeat/common/datetime.go:105-122and readers at137,173Evidence
common.SetTimestampPrecision(b.Config.TimestampPrecision)(x-pack/libbeat/cmd/instance/beat.go:180-181).SetTimestampPrecisioncomment explicitly states it changes format globally and mutates package varstsFmt/timeFormatter(libbeat/common/datetime.go:105-108,118-120).What is wrong
Per-receiver config controls process-global timestamp formatting state.
Why it matters
One receiver’s precision setting can silently affect all receivers in-process; concurrent construction with runtime formatting reads can produce inconsistent formatting and race-risk in
-raceruns.Suggested fix
Make timestamp precision instance-local for receiver pipelines/encoders; avoid changing package-global formatter in receiver constructor path.
Safe/Already Good
management.SetManagerFactory(...)path is mutex-protected (libbeat/management/management.go:114-118) and does not by itself encode receiver-specific state.x-pack/libbeat/cmd/instance/beat.go:42-52FQDN caching usessync.OnceValuesand is not per-receiver mutable config.Suggested Actions
paths.Pathobjects.go test -race ./x-pack/filebeat/fbreceiver/... ./x-pack/metricbeat/mbreceiver/....What is this? | From workflow: Sweeper: OTel BeatReceiver Global State Isolation
Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.