feat: Add Task support for BatchExportProcessor and PeriodicExportingMetricReader for single-threaded WebAssembly#6379
Conversation
|
|
c3eaac6 to
eb3c0b6
Compare
b1e1d58 to
02b4838
Compare
|
To whoever approved the workflows run, thank you :) I've made adjustments to avoid parameters reordering breaking changes which should get the build go further. |
|
The tests revealed a possible race condition in the disposing/shutdown ordering of the processor resulting in missing exports, for the task implementation. |
|
Fixed an additional race condition for slower machines where the exporter may not have fully started while being shut down, preventing it to still export what was in queue. |
|
Thanks for the run, again! Looks like there's a reported test failure, but without any failed tests. Feels like a fluke, but it may be something known to this project. Would a rerun help? Thanks! |
|
Thanks @cijothomas, it helped :) |
b028326 to
2b6d37c
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR adds optional Task support for BatchExportProcessor and PeriodicExportingMetricReader to enable OpenTelemetry to work in .NET's WebAssembly single-threaded mode (Blazor, Uno Platform, etc.). The implementation maintains backward compatibility by defaulting to the existing Thread-based approach while allowing configuration to use Task-based workers.
Key changes:
- Added new
UseThreadsparameter to control whether to use Thread or Task-based implementations - Refactored existing code to use abstract worker classes with Thread and Task implementations
- WebAssembly runtime automatically uses Task-based workers when detected
Reviewed Changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| BatchExportProcessor.cs | Refactored to use configurable worker pattern with Thread/Task implementations |
| PeriodicExportingMetricReader.cs | Added options-based constructor and worker abstraction |
| BatchExportProcessorOptions.cs | Added UseThreads property |
| PeriodicExportingMetricReaderOptions.cs | Added UseThreads property |
| BatchActivityExportProcessor.cs | Added options-based constructor overload |
| BatchLogRecordExportProcessor.cs | Added options-based constructor overload |
| Multiple worker classes | New abstract base classes and Thread/Task implementations |
| Test files | Updated tests to cover both Thread and Task scenarios |
Comments suppressed due to low confidence (4)
test/OpenTelemetry.Tests/Logs/BatchLogRecordExportProcessorTests.cs:16
- Parameter name 'useThread' should be 'useThreads' to be consistent with the property name 'UseThreads' used throughout the codebase.
public void StateValuesAndScopeBufferingTest(bool useThread)
test/OpenTelemetry.Tests/Logs/BatchLogRecordExportProcessorTests.cs:33
- Variable name 'useThread' should be 'useThreads' to match the property name and maintain consistency.
UseThreads = useThread,
test/OpenTelemetry.Tests/Logs/BatchLogRecordExportProcessorTests.cs:165
- Parameter name 'useThread' should be 'useThreads' for consistency with the property name used throughout the codebase.
public void DisposeWithoutShutdown(bool useThread)
test/OpenTelemetry.Tests/Logs/BatchLogRecordExportProcessorTests.cs:182
- Variable name 'useThread' should be 'useThreads' to match the property name and maintain consistency.
UseThreads = useThread,
052771d to
24bec97
Compare
src/OpenTelemetry/Metrics/Reader/PeriodicExportingMetricReader.cs
Outdated
Show resolved
Hide resolved
src/OpenTelemetry/Metrics/Reader/PeriodicExportingMetricReader.cs
Outdated
Show resolved
Hide resolved
src/OpenTelemetry/Metrics/Reader/PeriodicExportingMetricReader.cs
Outdated
Show resolved
Hide resolved
src/OpenTelemetry/Metrics/Reader/PeriodicExportingMetricReaderOptions.cs
Outdated
Show resolved
Hide resolved
|
Thanks for the reviews @martincostello! |
| this.workerTask = Task.Factory.StartNew( | ||
| this.ExporterProcAsync, | ||
| this.cancellationTokenSource.Token, | ||
| TaskCreationOptions.LongRunning, |
There was a problem hiding this comment.
This option means creating a dedicated thread, defeating the purpose of this implementation. Though, since ExporterProcAsync can yield the execution, the continuation will be schedule on the thread pool and the thread will probably die.
A simple Task.Run would be safer here.
There was a problem hiding this comment.
TaskCreationOptions.LongRunning hints the scheduler to create a dedicated thread, which is exactly what this PR is trying to avoid on WebAssembly. As @verdie-g pointed out in the PR comments, a simple Task.Run(this.ExporterProcAsync) would be safer. On WASM where threads aren't available, LongRunning may either be ignored or throw.
| await Task.WhenAny( | ||
| this.dataExportedNotification.Task, | ||
| this.shutdownCompletionSource.Task, | ||
| Task.Delay(timeout, combinedTokenSource.Token)).ConfigureAwait(false); |
There was a problem hiding this comment.
Since the Task.Delay is used as a timeout mechanism, the delay result will be discarded most of the time since the other would already have completed. Yet, we let the timer fire every time here which can cause important contention (see dotnet/runtime#83890 (comment)). The fix would be to cancel the Task.Delay when it did not complete.
There was a problem hiding this comment.
While combinedTokenSource exists, it only cancels on overall shutdown/timeout, not when dataExportedNotification completes. Each loop iteration leaks a live timer until it fires. This was also flagged by @verdie-g. The fix: cancel the CancellationTokenSource after WhenAny returns, or restructure to reuse a single timer.
Same issue exists in ExporterProcAsync:
await Task.WhenAny(
this.exportTrigger.WaitAsync(cancellationToken),
Task.Delay(this.ScheduledDelayMilliseconds, cancellationToken)).ConfigureAwait(false);
The Task.Delay is only cancelled on full shutdown (cancellationToken), not when the semaphore fires, so timers accumulate.
Fixes #2816
Design discussion issue: Partially based on #5778 and #5838
Changes
The change introduces updates to
BatchExportProcessorandPeriodicExportingMetricReaderin order to switch between Thread and Task implementations to support .NET's WebAssembly single threaded mode (used by Blazor, Uno Platform and others).Based on #5838 (review), the threads mode is automatically enabled when multi-threading is available. Tasks are enabled when WebAssembly is detected, based on the
ThreadingHelperclass.Note that there is no public API to change between thread and tasks.
Merge requirement checklist
CHANGELOG.mdfiles updated for non-trivial changes