-
Notifications
You must be signed in to change notification settings - Fork 889
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add SimpleConcurrentProcessor for Logs, clarify existing export concurrency #4163
Conversation
@@ -427,6 +429,21 @@ the configured `processor`. | |||
|
|||
* `processor` - processor to be isolated. | |||
|
|||
#### Simple Concurrent processor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I strongly suggest NOT using the term "concurrent" as usually it conveys that there is some of synchronization behind. E.g. "concurrent collections" are collections that can be used in multithreaded code. In some languages there is an idiom that if the type has Concurrent
in its name, the method calls are synchronized.
My naming proposal is "Passthrough processor"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This resonates with me too -- the use of "concurrent" suggests some kind of attention to concurrency, a limit of some sort in addition to offering concurrency. Maybe the name could be AsynchronousProcessor -- starts the export and returns success immediately.
I admit this worries me a bit -- is the exporter expected to implement some kind of limit, to avoid running out of memory? Can this be composed with the BatchSpanProcessor? What will control whether the BatchSpanProcessor drops spans vs. this processor consuming unlimited amounts of data?
Here, we have an OTel Collector processor that offers unlimited concurrency, but subject to a limit on the total amount of pending data, I consider it a potential solution to the problems posed above: https://github.com/open-telemetry/otel-arrow/tree/main/collector/processor/concurrentbatchprocessor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the name could be AsynchronousProcessor -- starts the export and returns success immediately.
That is not the intend. The intended exporters here not just 'starts' the export. It does everything inline (i.e serialization, writing to destination.), and then return success/failure. (from what I know, such exporters are typically writing to ETW, user-events)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the exporter expected to implement some kind of limit, to avoid running out of memory?
No. Such exporters (etw/user-events) does not buffer anything. The logRecord is serialized and handed over to destination inline. ETW/user-events system is backed by OperatingSystem kernel memory, and they have ample mechanisms to keep memory in control, but those are outside the scope of the exporter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cijothomas any thoughts about my naming proposal?
My other proposals are
-
"Direct processor" meaning that it directly calls the exporter.
-
"Adapter processor" meaning that it is only an adapter and has no logic/ synchronization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
naming is hard 🤣 I am not sure if any of the alternate suggestions are significantly better. I'll keep thinking and hope we get more suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
specification/logs/sdk.md
Outdated
Depending on the implementation the result of the export may be returned to the | ||
`Export` MAY be called concurrently for the same exporter instance if paired | ||
with [Simple Concurrent Processor](#simple-concurrent-processor). Each exporter | ||
implementation MUST document whether it supports concurrent `Export` calls, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- We cannot force custom implementation on how they document their implementations
- It is not only about exporting but also ForceFlush and Shutdown
implementation MUST document whether it supports concurrent `Export` calls, and | |
implementation provided by the SDK MUST document whether it is concurrent safe, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We cannot enforce, but the spec wording is to make sure any person authoring own exporter should follow this spec. If they don't follow, then there could be undesired behavior.
Alternatively, we can word it in such a way that "It must be documented to the exporter authors that they should document the exporters' concurrency characteristics....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this was part of why it was went with that the processor should called all exporters the same and not have to worry about concurrency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tsloughter, the users could still use processors that does not require the exporters to be concurrent safe. But we would give a more performant option for cases where exporter are concurrent safe and can be used synchronously.
@cijothomas, the alternative could be that such exporter packages (like ETW or user_events exporters) would provide their own processors which are "tailored" to their exporters. It can give more flexibility and not increase the SDK functionality surface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the alternative could be that such exporter packages (like ETW or user_events exporters) would provide their own processors which are "tailored" to their exporters
You are right! That is exactly what we have been doing for years. But that does not mean spec shouldn't support them.
The increase in SDK functionality surface is necessary, to support such scenarios. The spec already has wording that state implementations must have simple and batch (meaning others are optional), so additional processors don't put extra burden on implementations, unless they chose to support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is more (I have forgotten tot call it out explictly) I think that e.g. for ETW we can simply provide a single EtwProcessor
which does the exporting. There is no need for batching or synchronization so there is no need to provide an Exporter
interface implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that e.g. for ETW we can simply provide a single EtwProcessor which does the exporting
Yes that is also possible. (In certain ways, OTel Rust does that. Its etwexporter is not really an exporter, but a ~processor).
But I think it is best to model exporters (the thing which does serialization, export telemetry to outside the process) as exporters itself consistently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I think it is best to model exporters
I am not really sure as these exporters are emitting batches. And for use cases like ETW and user_events you probably prefer to operate on "single" log record.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are exporters! (They do the job of serializing and transferring the telemetry to an external entity.) Whether it gets a batch of 1 item or multiple is purely based on choice of processor used. (when used with SimpleProcessor, even OTLPExporter gets a batch of single item only).
Deleted my last comment because I had misread what was and wasn't in the current spec for Logs and don't want to confuse anyone reading these later. Exporting can be done concurrently, what is the reason for needing a "ConcurrentProcessor"? |
I don't think it is possible to achieve what we want today. We need exporter.export() method called synchronously from the processor. More specifically, when |
@cijothomas right, and it is called synchronously. It is the exporter that handles concurrency, or lack there of. By default SimpleProcessor and builtin exporter must not return from export for spans and logs in order to support cases like you describe and it is relied on by users of like aws lambda, but it remains the exporter where concurrency is handled, not the processor. |
Talking on Slack I now see what The issue arises when you have N processes/threads each calling To repeat here what I said on slack, I would not do this with a |
OTel .NET: OTel Rust: |
I am not sure what is SimpleProcessorPool. |
Relates to #3616 |
My own previous suggestion:
I double-checked and this would be a a breaking change in the specification. Right now, it is allowed to call I created #4173 based on this PR to separate clarification of the export concurrency from proposing a new exporting processor. |
This PR was marked stale due to lack of activity. It will be closed in 7 days. |
Closed as inactive. Feel free to reopen if this PR is still being worked on. |
Fixes most of #4134 (Tracing signal not addressed in this PR)
Changes
.NET, Rust, C++ have their SimpleProcessor already matching this.
Java, Go, Python does not. I believe they can fix the SimpleProcessor implementation to match this. This should be treated as a bug fix. Since SimpleProcessors were meant to be used in test/debug scenarios (stdout exporters), the extra perf hit should not be a concern. If it is indeed a concern, then users can be advised to switch to SimpleConcurrentProcessor.
(I also considered the possibility of adding extra capability to existing SimpleProcessor, but I believe it is better to have a dedicated one to avoid any confusion about "Simple" being in used for high-perf, prod scenarios. Also, adding more capabilities may make SimpleProcessor more complex without much gains.)
Additional background/context:
.NET, Rust, C++ had the equivalent of SimpleConcurrentProcessors for quite a while and was used when exporting to OS native tracing systems like ETW (Windows), Linux user_events. Exporting to these systems are done for scenarios requiring very high performance, and the existing Simple/Batch processors does not allow such high performance, necessitating the need of an official exporting processor.
(Note: A similar PR can be done for tracing sdk as well, but want to first try out in Log SDK. Once this is done, will make a follow up PR to trace sdk as well.)
CHANGELOG.md
file updated for non-trivial changesspec-compliance-matrix.md
updated if necessary