Skip to content

fix(otel): widen the span queue when flush_at exceeds its default - #2086

Open
passionworkeer wants to merge 2 commits into
langfuse:mainfrom
passionworkeer:oss/langfuse-python-flushat-20261031T215101-nightly-14dfce43
Open

passionworkeer wants to merge 2 commits into
langfuse:mainfrom
passionworkeer:oss/langfuse-python-flushat-20261031T215101-nightly-14dfce43

Conversation

@passionworkeer

@passionworkeer passionworkeer commented Nov 1, 2026 •

Copy link
Copy Markdown

What does this PR do?

LangfuseSpanProcessor forwards flush_at to BatchSpanProcessor as
max_export_batch_size and never sets max_queue_size. OpenTelemetry keeps its own
default queue size and rejects any larger batch, so a flush_at above the queue size
failed client construction with

ValueError: max_export_batch_size must be less than or equal to max_queue_size.

The message never mentions flush_at, which makes the failure hard to attribute to a
Langfuse setting.

This passes max_queue_size=max(flush_at, default_queue_size), where default_queue_size
is OpenTelemetry's own resolved default (BatchSpanProcessor._default_max_queue_size(),
which honours OTEL_BSP_MAX_QUEUE_SIZE). A batch that already fits leaves the queue
exactly as it is today; only a batch that does not fit widens it.

Fixes langfuse/langfuse#19539

Type of change

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

Verification

Regression tests in tests/unit/test_flush_at_batch_size.py. On the unpatched main
4 of the 5 fail or error with the ValueError above; all 5 pass with the fix:

case before after
flush_at=5000 constructs and flushes ERROR ×2 PASSED ×2
finished spans still exported with flush_at=5000 FAILED PASSED
OTEL_BSP_MAX_QUEUE_SIZE=1000 + flush_at=1500 FAILED PASSED
OTEL_BSP_MAX_QUEUE_SIZE=5000 + flush_at=3000 keeps queue at 5000 PASSED (guard) PASSED

Queue capacity sweep (probe script, not committed — it reads OpenTelemetry internals):

OTEL_BSP_MAX_QUEUE_SIZE flush_at before after
unset 512 2048 2048
unset 2048 2048 2048
unset 3000 ValueError 3000
1000 1500 ValueError 1500
5000 3000 5000 5000 (regression guard, unchanged)
1000 5000 ValueError 5000
1000 5000 ValueError 5000

Other checks:

  • tests/unit/test_span_processor.py + test_resource_manager.py + test_otel.py:
    145 passed, 2 skipped.
  • Full tests/unit: 988 passed, 2 skipped. The remaining 2 failures and 18 errors are all
    in tests/unit/test_prompt.py / test_prompt_atexit.py and reproduce identically on the
    unpatched main in this environment (no LANGFUSE_PUBLIC_KEY, no literal python on
    PATH); they are unrelated to this change.
  • ruff check . clean, ruff format --check clean on both changed files,
    mypy langfuse clean.

Note for reviewers

_default_max_queue_size() is an OpenTelemetry internal. It is used here because it is
what determines the constraint being fixed, and because the queue default is configurable
through OTEL_BSP_MAX_QUEUE_SIZE — hard-coding 2048 would both miss smaller configured
queues and narrow larger ones. The declared range is opentelemetry-sdk>=1.45.0,<2; the
alternative would be duplicating OpenTelemetry's environment parsing.

An alternative fix would be to validate flush_at and raise a clear error naming the
setting instead of widening the queue. Happy to switch if you prefer that — this PR picks
widening because it keeps every currently working configuration working.

Checklist

  • I have self-reviewed the diff
  • I have added or updated tests
  • I have not hand-edited generated files under langfuse/api/
  • No new dependencies, no public API change

RetriggerView in GreptileConfidence Score: 4/5

The PR appears safe to merge, with a non-blocking test isolation issue.

Summary

Widens the span queue when flush_at exceeds OpenTelemetry's configured queue size.

  • Large flush_at settings fit in the span queue.

Reviews (1) · Last reviewed commit: "fix(otel): widen the span queue when flu..." · Reviewed by Greptile

Copilot AI balanced review requested due to automatic review settings November 1, 2026 02:29

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

Comment thread tests/unit/test_flush_at_batch_size.py Outdated
Comment on lines +58 to +66
for processor in provider._active_span_processor._span_processors:
queue = getattr(processor, "_queue", None) or getattr(
processor, "_batch_processor", None
)
target = getattr(queue, "_queue", queue)
capacity = getattr(target, "maxlen", None) or getattr(target, "capacity", None)

if isinstance(capacity, int):
return capacity

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.

P2 Test reads an older queue

_queue_capacity() returns the first queued processor on the shared provider, not the processor created by this test. LangfuseResourceManager.reset() clears clients but leaves the global provider and its processors in place. If the smaller-queue test runs first, this test reads its capacity of 1500 and fails despite correctly creating a queue of 5000. Give these tests a fresh TracerProvider, or select the processor belonging to the current client.

Knowledge Base Used: Client initialization and resource management

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/unit/test_flush_at_batch_size.py
Line: 58-66

Comment:
**Test reads an older queue**

`_queue_capacity()` returns the first queued processor on the shared provider, not the processor created by this test. `LangfuseResourceManager.reset()` clears clients but leaves the global provider and its processors in place. If the smaller-queue test runs first, this test reads its capacity of 1500 and fails despite correctly creating a queue of 5000. Give these tests a fresh `TracerProvider`, or select the processor belonging to the current client.

**Knowledge Base Used:** [Client initialization and resource management](https://app.greptile.com/-/switch-tenant?tenant=personal-org-4986&next=%2Fcustom-context%2Fknowledge-base%2Flangfuse%2Flangfuse-python%2F-%2Fdocs%2Fclient-initialization-and-resources.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

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.

Fixed in 68499cb — each test now builds its client with its own TracerProvider, so the capacity it reads belongs to that client instead of whichever processor was registered first on the shared provider.

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

None yet

Development

Successfully merging this pull request may close these issues.

bug(sdk-python): flush_at larger than the span queue raises ValueError at client construction

2 participants