Repository navigation
feat: return a promise from flush() - #32
Merged
Merged
Conversation
flush() started an export and returned nothing, so code had no way to wait for its spans to be sent. Shutdown handlers and serverless functions need to, since explicit exits and frozen processes skip the beforeExit flush. flush() now returns a promise that resolves once the spans it took from the buffer and every export already in flight are done. It never rejects. Failed exports are already reported as OTelExportWarning warnings, and a rejection would be unhandled in every caller that ignores the result. An export that fails to send destroys its request, so neither its socket nor its deadline outlives it. The export benchmark awaits its flushes instead of counting requests at the collector, and several tests await flush() instead of waiting a fixed delay for an export that might not come. Signed-off-by: Bryan English <bryan@bryanenglish.com>
bengl
marked this pull request as ready for review
November 2, 2026 21:54
AbhiPrasad
reviewed
Nov 2, 2026
AbhiPrasad
left a comment
Member
There was a problem hiding this comment.
if we do it async should we let folks pass in a AbortController?
also maybe we should have a max timeout (allow for it to be configured?)
Member
Author
|
@AbhiPrasad wanna do those in a separate PR? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
flush()used to start an export and return nothing, so there was no way to wait for spans to reach the collector. Explicit exits skip thebeforeExitflush, and so does a serverless platform that freezes the process after each invocation. Nowflush()returns a promise:What the promise covers. It resolves once the spans
flush()took from the buffer, and every export already in flight, are done, whether they reached the collector or not. Aflush()called while an export is in flight waits for that export too, so awaiting the latest call is enough. Spans that end after the call belong to a later flush.It never rejects. Failed exports are already reported as
OTelExportWarningwarnings. A rejection would be unhandled in every caller that ignores the result, including microtel's own timer, buffer-full, andbeforeExitflushes.How an export settles. Each export gets a promise, kept in a set while its request is open. It settles on whichever comes first: the response ending, a response or request error, or the
exportTimeoutdeadline, which destroys the request. An export that throws while sending destroys its request, so neither its socket nor its deadline outlives it.Using it.
benchmark/export.bench.jsawaits its flushes instead of counting requests at the collector, and drops its watchdog. A shortexportTimeoutkeeps a stalled export from hanging a sample. Each sample now ends when the last response does, rather than when the collector finishes reading the last request, so its numbers are a little lower than main's.delay(100)afterflush()to see whether an export happened. They now awaitflush()itself.Not taken.
flush().exportTimeoutalready bounds every export.Tests.
test/test-flush-promise.jscovers:init()flush()waiting for an export still in flightexportTimeoutEach of these breaks fails the file: not waiting for exports in flight, settling by rejecting, resolving before the export ends, a response or request error that never settles, and a failed send that leaves its request open.