Skip to content

fix: respect timeout_millis in BatchProcessor.force_flush (#4568)#4982

Open
chimchim89 wants to merge 3 commits intoopen-telemetry:mainfrom
chimchim89:fix/force-flush-timeout
Open

fix: respect timeout_millis in BatchProcessor.force_flush (#4568)#4982
chimchim89 wants to merge 3 commits intoopen-telemetry:mainfrom
chimchim89:fix/force-flush-timeout

Conversation

@chimchim89
Copy link
Contributor

Description

force_flush in BatchProcessor accepted a timeout_millis parameter but completely ignored it, blocking until all telemetry was exported regardless of the timeout passed by the caller.

The fix has two parts:

  1. _export() now accepts a flush_should_end parameter (an absolute time.time() deadline). Before each batch export, it
    checks whether the deadline has been reached and returns False early if so. Returns True when all batches are exported
    normally.

  2. force_flush() now computes a deadline from timeout_millis and passes it to _export(), then propagates the result back to
    the caller.

Note: the timeout is checked between batches, not during a single exporter.export() call. Passing the timeout into exporter.export() itself is a separate issue tracked in #4555.

Fixes #4568

Type of change

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

How Has This Been Tested?

Three new unit tests were added to opentelemetry-sdk/tests/shared_internal/test_batch_processor.py. Each test runs for both BatchLogRecordProcessor and BatchSpanProcessor via the existing @pytest.mark.parametrize decorator (6 test runs total):

  • test_force_flush_returns_true_when_all_exported — verifies that force_flush returns True when all telemetry is exported
    within the timeout.
  • test_force_flush_returns_false_when_timeout_exceeded — verifies that force_flush returns False when the deadline is
    exceeded before the queue is fully drained. Uses a slow exporter (time.sleep(0.2)) with a 100ms timeout to reliably trigger the
    timeout between batches.
  • test_force_flush_returns_false_when_shutdown — verifies that force_flush returns False immediately when the processor is
    already shut down and nothing is exported.

Ran new tests only:

pytest opentelemetry-sdk/tests/shared_internal/test_batch_processor.py -k force_flush -v

Ran full test file to verify no regressions:

pytest opentelemetry-sdk/tests/shared_internal/test_batch_processor.py -v

Ran ruff linter to verify style:

ruff check opentelemetry-sdk --fix

Ran tox to verify full lint and type check pipeline:

tox -e py

Does This PR Require a Contrib Repo Change?

  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

…ut_exceeded to avoid flakiness on fast CI runners

Fixes open-telemetry#4568.
@chimchim89 chimchim89 force-pushed the fix/force-flush-timeout branch 3 times, most recently from 7a97df8 to e7f7b93 Compare March 15, 2026 17:08
@xrmx xrmx moved this to Ready for review in Python PR digest Mar 16, 2026
@xrmx
Copy link
Contributor

xrmx commented Mar 16, 2026

Please add a changelog entry

@chimchim89 chimchim89 force-pushed the fix/force-flush-timeout branch from e7f7b93 to 3e1e396 Compare March 16, 2026 11:30
@chimchim89
Copy link
Contributor Author

Please add a changelog entry

yes. I added a changelog entry under ## Unreleased.

def _export(
self,
batch_strategy: BatchExportStrategy,
flush_should_end: Optional[float] = None,
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we could go with something like

Suggested change
flush_should_end: Optional[float] = None,
deadline: float = math.inf,

instead?

Comment on lines +253 to +259
flush_should_end = (
time.time() + (timeout_millis / 1000)
if timeout_millis is not None
else None
)
# Blocking call to export.
self._export(BatchExportStrategy.EXPORT_ALL)
return True
return self._export(BatchExportStrategy.EXPORT_ALL, flush_should_end)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, it might be easier to just default to math.inf

Suggested change
flush_should_end = (
time.time() + (timeout_millis / 1000)
if timeout_millis is not None
else None
)
# Blocking call to export.
self._export(BatchExportStrategy.EXPORT_ALL)
return True
return self._export(BatchExportStrategy.EXPORT_ALL, flush_should_end)
deadline = (
time.time() + (timeout_millis / 1000)
if timeout_millis is not None
else math.inf
)
# Blocking call to export.
return self._export(BatchExportStrategy.EXPORT_ALL, deadline)

batch_processor.shutdown()

# pylint: disable=no-self-use
def test_force_flush_returns_false_when_timeout_exceeded(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we just add some mocks here to test this behavior?

@chimchim89 chimchim89 force-pushed the fix/force-flush-timeout branch from 3e1e396 to fa68592 Compare March 17, 2026 04:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready for review

Development

Successfully merging this pull request may close these issues.

Make timeout of Exporter.export configurable and decide how to update Exporter interfaces

3 participants