Skip to content

[libbeat/processing] add a new shared processor wrapper - #50713

Merged
VihasMakwana merged 7 commits into
elastic:mainfrom
VihasMakwana:shared-manual
May 19, 2026
Merged

[libbeat/processing] add a new shared processor wrapper#50713
VihasMakwana merged 7 commits into
elastic:mainfrom
VihasMakwana:shared-manual

Conversation

@VihasMakwana

@VihasMakwana VihasMakwana commented May 14, 2026

Copy link
Copy Markdown
Contributor

Proposed commit message

Right now, per input processors are instantiated for every new harvester created.

return func(clientCfg beat.ClientConfig) (beat.ClientConfig, error) {
var indexProcessor beat.Processor
if !config.Index.IsEmpty() {
staticFields := fmtstr.FieldsForBeat(beatInfo.Beat, beatInfo.Version)
timestampFormat, err := fmtstr.NewTimestampFormatString(&config.Index, staticFields)
if err != nil {
return clientCfg, err
}
indexProcessor = add_formatted_index.New(timestampFormat)
}
userProcessors, err := processors.New(config.Processors, beatInfo.Logger)
if err != nil {
return clientCfg, err
}

In environments with a large number of log files, this will lead to excessive memory usage and can cause memory exhaustion/OOM issues.

Fix this by adding a shared processor wrapper, that will create one processor instance for a given config, no matter how many times it was instantiated.

Why is it important

Processors interested in using a "shared" state can opt-in by wrapping their constructors:

Before:

processors.RegisterPlugin("add_kubernetes_metadata", New)

After:

processors.RegisterPlugin("add_kubernetes_metadata", shared.New(New))

This will result in much less memory allocation over long term.

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have made corresponding change to the default configuration files
  • I have added tests that prove my fix is effective or that my feature works. Where relevant, I have used the stresstest.sh script to run them under stress conditions and race detector to verify their stability.
  • I have added an entry in ./changelog/fragments using the changelog tool.

How to test this PR locally

You can test this with following config:

filebeat.inputs:

- type: filestream
  id: my-filestream-id1
  enabled: true
  paths:
    - /path/to/logs/*.log
  processors:
    - add_kubernetes_metadata

output.console:
  enabled: true
  # path: "/tmp/filebeat"
logging.level: debug

With new files being created, the memory increase will still be there, but slow and steady. Without my changes, the usage is drastic.
It is more drastic if you're testing this in k8s environment with add_kubernetes_metadata processor.

Related issues

@botelastic botelastic Bot added the needs_team Indicates that the issue/PR needs a Team:* label label May 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 GitHub comments

Just comment with:

  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)
  • /test : Run the Buildkite pipeline.

@VihasMakwana VihasMakwana added the Team:Elastic-Agent-Data-Plane Label for the Agent Data Plane team label May 14, 2026
@botelastic botelastic Bot removed the needs_team Indicates that the issue/PR needs a Team:* label label May 14, 2026
@VihasMakwana
VihasMakwana requested review from belimawr and cmacknz May 14, 2026 20:02
@github-actions

This comment has been minimized.

@mergify

mergify Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

This pull request does not have a backport label.
If this is a bug or security fix, could you label this PR @VihasMakwana? 🙏.
For such, you'll need to label your PR with:

  • The upcoming major version of the Elastic Stack
  • The upcoming minor version of the Elastic Stack (if you're not pushing a breaking change)

To fixup this pull request, you need to add the backport labels for the needed
branches, such as:

  • backport-8./d is the label to automatically backport to the 8./d branch. /d is the digit
  • backport-active-all is the label that automatically backports to all active branches.
  • backport-active-8 is the label that automatically backports to all active minor branches for the 8 major.
  • backport-active-9 is the label that automatically backports to all active minor branches for the 9 major.

@github-actions

Copy link
Copy Markdown
Contributor

TL;DR

Libbeat: Run check/update failed because the new file libbeat/processors/shared/shared.go is missing the required Apache license header, so go-licenser stops make -C libbeat check update.

Remediation

  • Add the standard Elasticsearch/Apache-2.0 header block at the top of libbeat/processors/shared/shared.go (before package shared).
  • Re-run make -C libbeat check update and make check-no-changes to confirm header checks pass.
Investigation details

Root Cause

The header check stage (check-headers) failed on the new shared processor file. In the current PR commit (61b4346e79f1a7bf4cb65b3dd4e869963f6a2229), libbeat/processors/shared/shared.go starts directly with package shared and contains no license header, which violates the repository header policy.

Evidence

  • Build: https://buildkite.com/elastic/beats/builds/46148
  • Job/step: Libbeat: Run check/update (beats-libbeat build 29678)
  • Key log excerpt:
    • processors/shared/shared.go: is missing the license header
    • Error: running "go-licenser -d -license ASL2" failed with exit code 1
    • make: *** [scripts/Makefile:146: check-headers] Error 1

Verification

  • Not run locally in this workflow environment.

Follow-up

  • After adding the header, re-trigger CI; this failure should clear unless another new file is also missing headers.

Note

🔒 Integrity filter blocked 3 items

The following items were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

What is this? | From workflow: PR Buildkite Detective

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@orestisfl
orestisfl self-requested a review May 15, 2026 07:29

@orestisfl orestisfl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach LGTM

Comment thread libbeat/processors/shared/shared.go Outdated
Comment thread libbeat/processors/shared/shared.go Outdated
Comment thread libbeat/processors/shared/shared.go Outdated

@belimawr belimawr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far it looks great!

@VihasMakwana VihasMakwana changed the title [chore] add a new shared processor wrapper [libbeat/processing] add a new shared processor wrapper May 16, 2026
@VihasMakwana VihasMakwana added backport-8.19 Automated backport to the 8.19 branch skip-changelog backport-9.4 labels May 16, 2026
@VihasMakwana
VihasMakwana marked this pull request as ready for review May 18, 2026 05:34
@VihasMakwana
VihasMakwana requested a review from a team as a code owner May 18, 2026 05:34
@VihasMakwana
VihasMakwana requested a review from AndersonQ May 18, 2026 05:34
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

Pinging @elastic/elastic-agent-data-plane (Team:Elastic-Agent-Data-Plane)

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 383acd5b-14bc-4617-9fa7-d40b21d49693

📥 Commits

Reviewing files that changed from the base of the PR and between b56b327 and 1ba5101.

📒 Files selected for processing (2)
  • libbeat/processors/shared/shared.go
  • libbeat/processors/shared/shared_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • libbeat/processors/shared/shared_test.go

📝 Walkthrough

Walkthrough

This PR adds a shared processor wrapper that caches processor instances by a configuration hash with reference counting and lifecycle-managed Close. The kubernetes metadata processor is registered using shared.New(New) and a test helper signature was fixed. New concurrency tests for both the shared wrapper and the kubernetes annotator validate caching, reference-counted Close, and event isolation under concurrent Run/Close and concurrent cache mutations.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • 🛠️ Update Documentation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libbeat/processors/shared/shared.go`:
- Around line 59-61: The sharedProcessors map currently hands out the same
wrapper instance so duplicate Close() calls from different borrowers can
decrement p.refCount multiple times; fix by introducing a per-borrower handle
type (e.g., sharedHandle) that holds a pointer to the shared entry (e.g.,
sharedEntry with proc and refCount) plus its own closed flag and mutex, have
Borrow/lookup (the code around sharedProcessors[hash] and p.refCount) increment
only sharedEntry.refCount and return a new sharedHandle; implement
sharedHandle.Close() to be idempotent by checking its closed flag, setting it
once, then decrementing sharedEntry.refCount and when that hits zero close the
underlying processor and remove the sharedEntry from sharedProcessors (also
protect shared map and refCount with the existing lock used in the code paths
around lines 59 and 79-90).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 9b242bef-2171-4fa8-825f-a107ce0ae187

📥 Commits

Reviewing files that changed from the base of the PR and between 01f436f and 9cb291f.

📒 Files selected for processing (5)
  • libbeat/processors/add_kubernetes_metadata/kubernetes.go
  • libbeat/processors/add_kubernetes_metadata/kubernetes_concurrent_test.go
  • libbeat/processors/add_kubernetes_metadata/kubernetes_test.go
  • libbeat/processors/shared/shared.go
  • libbeat/processors/shared/shared_test.go

Comment thread libbeat/processors/shared/shared.go
Comment thread libbeat/processors/shared/shared.go
Comment thread libbeat/processors/shared/shared_test.go Outdated
Comment thread libbeat/processors/shared/shared_test.go Outdated
Comment thread libbeat/processors/shared/shared.go Outdated
Comment thread libbeat/processors/shared/shared.go
Comment thread libbeat/processors/shared/shared.go Outdated
Comment thread libbeat/processors/shared/shared.go Outdated
@VihasMakwana
VihasMakwana requested a review from AndersonQ May 19, 2026 06:30
@VihasMakwana
VihasMakwana merged commit 54f7600 into elastic:main May 19, 2026
205 checks passed
VihasMakwana added a commit that referenced this pull request May 21, 2026
…or wrapper (#50780)

* [libbeat/processing] add a new shared processor wrapper (#50713)

* chore: add a new shared wrapper for processors to use

* refcount

* simplify and add tests

* license

* lint

* double close test

* testing and review comments

(cherry picked from commit 54f7600)

# Conflicts:
#	libbeat/processors/add_kubernetes_metadata/kubernetes_test.go

* merge conflicts

---------

Co-authored-by: Vihas Makwana <121151420+VihasMakwana@users.noreply.github.com>
Co-authored-by: Vihas Makwana <vihas.makwana@elastic.co>
VihasMakwana added a commit that referenced this pull request May 21, 2026
…r wrapper (#50781)

* [libbeat/processing] add a new shared processor wrapper (#50713)

* chore: add a new shared wrapper for processors to use

* refcount

* simplify and add tests

* license

* lint

* double close test

* testing and review comments

(cherry picked from commit 54f7600)

# Conflicts:
#	libbeat/processors/add_kubernetes_metadata/kubernetes_test.go

* merge conflicts

---------

Co-authored-by: Vihas Makwana <121151420+VihasMakwana@users.noreply.github.com>
Co-authored-by: Vihas Makwana <vihas.makwana@elastic.co>
@VihasMakwana VihasMakwana added the backport-9.3 Automated backport to the 9.3 branch label May 21, 2026
@VihasMakwana

Copy link
Copy Markdown
Contributor Author

I'm also backporting this to 9.3.

VihasMakwana added a commit that referenced this pull request Jun 4, 2026
…r wrapper (#50830)

* [libbeat/processing] add a new shared processor wrapper (#50713)

* chore: add a new shared wrapper for processors to use

* refcount

* simplify and add tests

* license

* lint

* double close test

* testing and review comments

(cherry picked from commit 54f7600)

# Conflicts:
#	libbeat/processors/add_kubernetes_metadata/kubernetes_test.go

* merge conflicts

---------

Co-authored-by: Vihas Makwana <121151420+VihasMakwana@users.noreply.github.com>
Co-authored-by: Vihas Makwana <vihas.makwana@elastic.co>
orestisfl added a commit to orestisfl/beats that referenced this pull request Jul 27, 2026
orestisfl pushed a commit to orestisfl/beats that referenced this pull request Jul 27, 2026
Cherry-pick of elastic#50586 (squashed). Replaces the approach from elastic#50713
(reverted in the previous commit): instead of a dedicated shared
processor wrapper opted into per-processor, SafeWrap now dedupes
processor instances globally by (name, config hash) with reference
counting, so per-input processors are no longer instantiated per
harvester.

Fixes memory/goroutine blow-up with many harvesters (elastic#50376).

Note: the lifecycle contract tests introduced in the first commit of
this PR fail at this commit (TestContractConcurrentPipelines,
TestContractInputConcurrentHarvesters): closing a group twice releases
another owner's reference and the shared instance is closed under a
live holder. The next commit restores the contract.
orestisfl added a commit to orestisfl/beats that referenced this pull request Jul 29, 2026
orestisfl pushed a commit to orestisfl/beats that referenced this pull request Jul 29, 2026
Cherry-pick of elastic#50586 (squashed). Replaces the approach from elastic#50713
(reverted in the previous commit): instead of a dedicated shared
processor wrapper opted into per-processor, SafeWrap now dedupes
processor instances globally by (name, config hash) with reference
counting, so per-input processors are no longer instantiated per
harvester.

Fixes memory/goroutine blow-up with many harvesters (elastic#50376).

Note: the lifecycle contract tests introduced in the first commit of
this PR fail at this commit (TestContractConcurrentPipelines,
TestContractInputConcurrentHarvesters): closing a group twice releases
another owner's reference and the shared instance is closed under a
live holder. The next commit restores the contract.
orestisfl added a commit that referenced this pull request Jul 30, 2026
Deduplicate processor instances in SafeWrap using (name, hash).
Constructor calls with an identical configuration return handles to one
shared, reference-counted instance.

Beat-level global processors were already shared before this PR so most
processors should already work with that level of concurrency.

Design points:

- Exceptions:
  1. PathSetter implementations
  2. The rate_limit processor: a shared token bucket would divide the
     configured per-input limit by the number of owners.
- Each constructor call returns a handle; Close is idempotent per
  handle.
- Concurrent constructors sharing the same key wait for a single
  instance to initialize. Construction is done outside the global lock
  to avoid one processor blocking all constructors.

This reverts #50713, which shared only add_kubernetes_metadata through a
dedicated wrapper package, cherry-picks and hardens the approach from
#50586.

Processors that need to opt-out of being shared can implement the
Unshareable interface.

Closes #50376

Co-authored-by: Vihas Makwana <vihas.makwana@elastic.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-8.19 Automated backport to the 8.19 branch backport-9.3 Automated backport to the 9.3 branch backport-9.4 skip-changelog Team:Elastic-Agent-Data-Plane Label for the Agent Data Plane team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants