[bug-hunter] Non-memory OTel receiver close releases shared default slabqueue pool
- Dominant language
- Go
- Stars
- 12.7k
- Forks
- 5k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 364
Description
## Impact
When a Beat receiver using a non-memory queue (`queue.disk` path) is closed, it decrements/releases the shared default slabqueue pool (`intakeQueueID == ""`) even though it never acquired that pool. If another receiver is actively using the default mem-backed pool, this can tear down shared queue state unexpectedly and break publishing behavior for the still-running pipeline.
## Reproduction Steps
1. Create `libbeat/publisher/pipeline/output_otel_bug_repro_test.go` with the test below.
2. Run:
```bash
go test -v ./libbeat/publisher/pipeline -run '^TestRepro_NonMemCloseReleasesDefaultPool$'
```
## Expected vs Actual
**Expected:** Closing a non-memory controller should not release the shared default slabqueue pool used by a mem-backed controller.
**Actual:** The pool registration for `""` is removed; test fails:
```text
=== RUN TestRepro_NonMemCloseReleasesDefaultPool
output_otel_bug_repro_test.go:61:
Error: Should be true
Test: TestRepro_NonMemCloseReleasesDefaultPool
Messages: closing a non-memory controller must not release the shared default slabqueue pool
--- FAIL: TestRepro_NonMemCloseReleasesDefaultPool (0.00s)
FAIL
FAIL github.com/elastic/beats/v7/libbeat/publisher/pipeline 0.007s
FAIL
```
## Failing Test
```go
(go/redacted):build !nooteloutput
package pipeline
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/elastic/beats/v7/libbeat/publisher"
"github.com/elastic/beats/v7/libbeat/publisher/queue/memqueue"
)
func TestRepro_NonMemCloseReleasesDefaultPool(t *testing.T) {
type fakeNonMemqueueSettings struct{ Path string }
memController, err := newOTelOutputController(
beatInfoForTest(t),
monitorsForTest(),
nilObserver,
"",
nil,
memqueue.Settings{Events: 4},
)
require.NoError(t, err, "memory-backed controller creation should succeed")
defer memController.waitClose(cancelledContext(), false)
nonMemController, err := newOTelOutputController(
beatInfoForTest(t),
monitorsForTest(),
nilObserver,
"",
memqueue.FactoryForSettings[publisher.Event](memqueue.Settings{Events: 4}),
fakeNonMemqueueSettings{Path: "/tmp/dq"},
)
require.NoError(t, err, "non-memory queue controller creation should succeed")
require.NoError(t, nonMemController.waitClose(cancelledContext(), false), "closing non-memory controller should succeed")
allOTelPools.Lock()
_, stillRegistered := allOTelPools.lookup[""]
allOTelPools.Unlock()
assert.True(t, stillRegistered, "closing a non-memory controller must not release the shared default slabqueue pool")
}
```
## Evidence
- `libbeat/publisher/pipeline/output_otel.go:112-118` acquires shared pool only on `memqueue.Settings` path.
- `libbeat/publisher/pipeline/output_otel.go:119-131` non-memory path bypasses pool acquisition.
- `libbeat/publisher/pipeline/output_otel.go:253` unconditionally calls `releaseOTelPool(c.intakeQueueID)` during close.
- For non-memory controllers with empty intake ID, this releases key `""` despite no acquire.
- Recent introducing commit touching this path: `37398496f5` (`Implement a per-pipeline queue over a shared backing array. (slabqueue) (#51047)`).
- Duplicate checks run in repo issues/PR search for relevant keywords found no matching tracked item.
---
[What is this?](https://ela.st/github-ai-tools) | [From workflow: Bug Hunter](https://github.com/elastic/beats/actions/runs/27137748178)
Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.
> - [x] expires on Jun 15, 2026, 12:44 PM UTC
Contributor guide
Assessment
This issue has not been assessed yet.