Skip to content

Reset fork reset once on BatchProcessor _at_fork_reinit - #5850

Open
saiyam-jain-dev wants to merge 2 commits into
open-telemetry:mainfrom
saiyam-jain-dev:fix-batch-processor-fork-fallback
Open

saiyam-jain-dev wants to merge 2 commits into
open-telemetry:mainfrom
saiyam-jain-dev:fix-batch-processor-fork-fallback

Conversation

@saiyam-jain-dev

@saiyam-jain-dev saiyam-jain-dev commented Oct 28, 2026 •

Copy link
Copy Markdown

Description

BatchProcessor has a fallback for forks that bypass os.register_at_fork (e.g. native libraries calling fork() directly):

if self._pid != os.getpid():
    self._bsp_reset_once.do_once(self._at_fork_reinit)

Because self._bsp_reset_once is an instance of Once, it can only execute its target once over the lifetime of the object. When _at_fork_reinit() runs, it previously did not reset self._bsp_reset_once. A second bypassed fork in the same process lifetime would find self._bsp_reset_once._done == True, leaving self._pid stale, failing to spawn a new worker thread, and dropping subsequent exports.

This PR re-initializes self._bsp_reset_once = Once() inside _at_fork_reinit(), allowing subsequent fork fallbacks to trigger re-initialization as expected. A regression unit test is added to test_batch_processor.py.

Fixes #5843

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • I am a human and not a bot
  • I have run the relevant tests and linting tox environments

@saiyam-jain-dev
saiyam-jain-dev requested a review from a team as a code owner October 28, 2026 01:45
@linux-foundation-easycla

linux-foundation-easycla Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: saiyam-jain-dev / name: Saiyam Jain (15e441f)

@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Pull request dashboard status

Waiting on maintainers · refreshed 2026-11-01 11:15 UTC

Merge when ready.

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

Copilot AI 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.

🟡 Changes recommended

A concurrent fork-recovery race can discard queued telemetry and start duplicate workers, while PID-mocking tests can leave earlier worker threads running.

2 open issues
What changed in this PR

This PR addresses #5843 by resetting the shared BatchProcessor fork-recovery guard so repeated forks that bypass Python’s fork hooks can recover. This affects both batch span and log record processors.

Changes:

  • Create a fresh Once guard after fork reinitialization.
  • Add a regression test that simulates two consecutive fork fallbacks.
File Description
opentelemetry-sdk/​tests/​shared_internal/​test_batch_processor.py Tests repeated fallback recovery using simulated PID changes.
opentelemetry-sdk/​src/​opentelemetry/​sdk/​_shared_internal/​__init__.py Resets the recovery guard during fork reinitialization.

Review effort: Balanced

)
self._worker_thread.start()
self._pid = os.getpid()
self._bsp_reset_once = Once()

@saiyam-jain-dev saiyam-jain-dev Oct 29, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Captured reset_once = self._bsp_reset_once before the PID check so concurrent callers seeing the PID mismatch execute on the same generation guard and don't re-trigger recovery. Also added test_fork_fallback_concurrent_emit_race_does_not_duplicate_reinit to cover this interleaving.

Comment on lines +336 to +358
exporter = Mock()
processor = batch_processor_class(
exporter,
max_queue_size=200,
max_export_batch_size=10,
schedule_delay_millis=30000,
export_timeout_millis=500,
)
bp = processor._batch_processor
initial_pid = bp._pid

with unittest.mock.patch("os.getpid") as mock_getpid:
# First fork fallback
mock_getpid.return_value = initial_pid + 1
bp.emit(telemetry)
assert bp._pid == initial_pid + 1

# Second fork fallback
mock_getpid.return_value = initial_pid + 2
bp.emit(telemetry)
assert bp._pid == initial_pid + 2

processor.shutdown()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Updated the fallback tests to patch threading.Thread so no real worker threads are stranded waiting during mocked PID changes, wrapped the execution in try...finally to guarantee shutdown() runs, and added assert mock_thread.call_count == 3 to verify worker lifecycle.

@saiyam-jain-dev

Copy link
Copy Markdown
Author

/dashboard route:reviewers

@opentelemetry-pr-dashboard

Copy link
Copy Markdown

@saiyam-jain-dev, your reviewer-routing request was recorded; this pull request has the approvals it needs and is now waiting on maintainers. The handoff remains active across pushes until newer actionable human feedback arrives. Top-level feedback through this request will not return; unresolved review threads remain open.

@saiyam-jain-dev
saiyam-jain-dev force-pushed the fix-batch-processor-fork-fallback branch from 7d98362 to da2b162 Compare November 1, 2026 11:14

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Approved PRs

Development

Successfully merging this pull request may close these issues.

BatchProcessor's fork recovery fallback only recovers once; a second missed fork silently stops export forever

3 participants