-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
chore: RFC for handling discarded events #14708
base: master
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for vector-project canceled.
|
6c4cb78
to
c90126e
Compare
We will also add a new command-line option and environment variable to opt into the stricter | ||
validation for users that want the additional assurance this provides. | ||
|
||
#### Simplify Discarded Output Handling |
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 find the names used here awkward, but admittedly don't have any 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.
An alternative name for disposition
could be on_error
(or on_discard
).
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.
It could be, but only as applies to the error output. The proposed disposition applies to all named outputs, including from the datadog_agent
source and the remap
transform.
- Should the option for turning unhandled output enforcement into a hard error be written as a more | ||
generic switch to turn _all_ deprecations into errors? |
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.
Yes! 👍
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.
Yes. This feels similar to firewall/WAF products having "advisory" and "blocking" mode. As a learner, it's nice to get things up and running and be warned of dangerous configurations. Once I'm comfortable and depending on the product, I want a "strict" mode that more forcefully calls things out.
- What should be done with the existing discarded event metrics? Should they always be emitted, or | ||
only when the output isn't consumed by another component? Do we need another disposition marker to | ||
indicate discards are not to be counted as errors? |
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.
🤔 if we're forcing it to be handled, I would think they could be counted like we do with other event streams - events/bytes sent/received. Practically, that seems like a hard sell given the work we've just put into the discarded metrics and compatibility with the ui.
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.
Yeah, I agree that if we're not dropping them on the floor, then they aren't really discarded... just routed elsewhere.
Although maybe with true discarded event handling, "discarded" metrics would simply be the indication of how many discarded events were sent by that component, and then if the event made it to a component where we just dropped/rejected it, without further daisy chained error outputs... that would be when we emit an actual error metric?
Dunno, I feel like we could argue the definition of this stuff until the cows come home.
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.
Hmm, yeah, the relationship between this and the discarded event metric will require some thought.
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 agree with tobz here. In the data flow from source to sink, since all the components are sending an event to the next component, the final discard event will be thrown by the last component.
I see the importance of the discarded metrics, it gives the true number of the events. Subsequent metrics from the dlq component helps us understand the lag behind discarded and writes to dlq, and the loss in these events too. We will need indicators, for example: the difference between the number of discarded events versus the discarded events written to the dlq component. These numbers need to be close to zero in a given window.
TL;DR: we need discarded events metrics, and some more.
- Incompatible event type | ||
- Unhandled metric value type | ||
- Unwired component output | ||
- Failure to send to next component |
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 this type of failure possible in cases other than a non-graceful shutdown (i.e. panic)?
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, at the end of a graceful shutdown where components are being forcefully terminated. I think we could still send the data to the discard output in hopes it is still connected, and/or mark them as failed.
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.
If we're already forcefully terminating components then it seems unlikely that we'd be able to successfully send the events anywhere. I ask mostly because this would be a failure case that'd apply to every source and transform, even those that may not have any other way of dropping event, and I'd rather focus this feature around the components where there's a more meaningful type of failure possible.
We will introduce a new output to all components that would otherwise discard events, named | ||
`discards`. |
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.
How will this relate to the existing errors
output from the remap transform?
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'm not exactly sure. I am of the mind that it should be split between errors and discards (ie abort
), but that introduces a significant breaking change. If, on the other hand, we call the new output errors
, this works for remap but not everything that is discarded is an error.
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.
Ah, nevermind, the remap
transform already has a dropped
output that is what we need, so I think all the rest of this RFC is about errors in particular and not generic "discards".
potential of increased overhead and reduced performance if extra components have to be configured | ||
just to handle the outputs that now need to be connected somewhere. To reduce this overhead, we will | ||
add a new optional configuration setting to indicate the internal disposition of each output: | ||
`outputs.NAME.disposition`. This will have two non-default values, `"drop"` to mark all events going |
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.
Do you see this config applying to other outputs in the future? It sounds like we're only talking about a single hardcoded discards
output, so I'm not sure that we need to nest the configuration in such a way that implies it could be applied to other outputs as well.
What's the benefit of outputs.NAME.disposition
vs something like on_discard = {send,drop,reject}
?
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 have some sources, like datadog_agent
IIRC, that already have multiple outputs. If users do not want to wire up one of those outputs and we enforce that all outputs be handled, then now they need a blackhole for that output as well because we special-cased the discard handling. With the generic handling, all cases are covered.
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.
That's a good point, I didn't consider the effect on those sources. I am still a little bit hesitant to have configuration that refers to a named output that won't actually exist (when configured to drop or reject), since that could result in some confusion. I'm not sure I have a great alternative though, aside from introducing separate config on those sources that controls which events types are emitted.
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 is looking good so far, but I had a few clarifying questions to make sure I understand the scope.
/// Default behavior, send the event on to further components that name this as an input. | ||
Send, | ||
/// Discard the event and mark it is as delivered. | ||
Drop, | ||
/// Discard the event and mark it as failed. | ||
Reject, |
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.
These make sense individually, but I find it... let's call it "weird", that I could have a component which selects another component's discards output but then that upstream component could basically just disable the sending of those discarded events even though I've named that output specifically?
If that's not the intention, then maybe this is just a docs problem, but that was my initial impression seeing these three distinct options.
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.
That will be something caught by the configuration verification stage, that "handled" outputs are not named as an input. This is an issue I discussed later in the document in "Alternatives" as not having a great solution IMO.
Simplifying the configuration of previously unhandled outputs presents a bit of a Catch-22. Without | ||
any help from Vector, this would require users to explicitly route the unhandled outputs to a new | ||
`blackhole` sink. So, we want to add a shorthand to avoid the extra configuration that would | ||
require, and potentially the extra running component internally. |
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 section confuses me a little bit.
Why would a sink be needed if operators can control the output behavior at the component itself? Which is to say, if a source can configure its discarded events output to drop or reject... why would we ever set up a downstream component to collect those events unless it was actually going to do something with them?
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 "without any help from Vector" is trying to say that we are not helping sinks by allowing a source to configure its output to drop or reject. I will try to reword to clarify.
- What should be done with the existing discarded event metrics? Should they always be emitted, or | ||
only when the output isn't consumed by another component? Do we need another disposition marker to | ||
indicate discards are not to be counted as errors? |
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.
Yeah, I agree that if we're not dropping them on the floor, then they aren't really discarded... just routed elsewhere.
Although maybe with true discarded event handling, "discarded" metrics would simply be the indication of how many discarded events were sent by that component, and then if the event made it to a component where we just dropped/rejected it, without further daisy chained error outputs... that would be when we emit an actual error metric?
Dunno, I feel like we could argue the definition of this stuff until the cows come home.
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.
Nice write-up!
We will also add a new command-line option and environment variable to opt into the stricter | ||
validation for users that want the additional assurance this provides. | ||
|
||
#### Simplify Discarded Output Handling |
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.
An alternative name for disposition
could be on_error
(or on_discard
).
|
||
### Implementation | ||
|
||
#### Configuration |
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 wonder if we could have global configuration for the disposition as well? That might simplify it for simple use-cases. Like setting disposition = "drop"
at the global level and have it apply to all components.
Could also do the same at the component level for those that already have multiple outputs in addition to letting them configure the disposition per-output.
transforms, and simply modify them to write failed events to that output. This would have the | ||
benefit of higher performance, as we could likely rework most existing sinks to avoid needing the | ||
clone and would not need the extra finalizer and task to handle forwarding the events. On the other | ||
hand, this performance loss appears to be relatively minor, likely in the low single digit percent |
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 seems like another area that could benefit from having a copy-on-write data structure for events to avoid a full clone (I know we do have this optimization now for when the event is completely unchanged, which should help here too).
|
||
## Outstanding Questions | ||
|
||
- Do we want to automate the process of rewriting configurations that have missing output handlers, |
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 my suggestion above of allowing global configuration of the disposition would allow users to slowly take advantage of this new behavior component-by-component.
- What should be done with the existing discarded event metrics? Should they always be emitted, or | ||
only when the output isn't consumed by another component? Do we need another disposition marker to | ||
indicate discards are not to be counted as errors? |
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.
Hmm, yeah, the relationship between this and the discarded event metric will require some thought.
buffers would be a good approach, but that path towards that is less clear than solving the problem | ||
in isolation. | ||
|
||
## Outstanding Questions |
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.
Are there any cross-cutting concerns between these new, conditional, error outputs and the configuration schema?
|
||
### User Experience | ||
|
||
#### Add Discarded Event Output |
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 will be the format of the events flowing to the errors
output? Will we follow the precedent set by the remap
transform of wrapping the event with some additional metadata about the failure? I think it'd be good to call that out explicitly in here.
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 this should take advantage of the new metadata namespacing too? That feature will only exist for logs, but we could extend it to metrics and traces. Also will we offer any guarantees about the metadata, if we do include it? I could see users starting to route on it based on the type of failure. I'd suggest we push back on that clearly document that the metadata is opaque and just for human consumption for now.
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.
To validate your intuition, I would be tempted to route based on the metadata from an error. Hopefully a specific example will help:
Kafka sinks can throw errors when the broker sees a message that's too large. I'm finding it difficult to know the exact message size before sending, so it's hard to build a filter upstream. I could see handling this situation by routing the error. I might route to a console log or split the message and try again.
At a minimum, I could also envision using the error data to know whether dropping the message is okay or if I need to trigger alerts.
### Out of scope | ||
|
||
- Handling of discarded data in sources before it is translated into events. | ||
- Handling of discarded sink request data after it is translated from 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.
This should be entirely accounted for by end-to-end acknowledgements? Or is there a gap I'm not thinking of?
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 is making me realize that end-to-end acks is another cross-cutting concern here.
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.
Noted.
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 situation here plays around like this: For an event, i.e, after all the retries have exhausted, will be marked as discarded. For a discarded event, the following will happen:
- Push to DLQ with retries
- If the write to DLQ was successful, acknowledge to the source for this event.
- if write to DLQ failed, drop the event and acknowledge to the source for this event.
Is this a fair understanding?
We will introduce a new output to all components that would otherwise discard events, named | ||
`errors`. Note that some components already have such an named output. This proposal standardizes | ||
that output naming and provides additional support for handling it. |
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 wonder if we should lean into existing nomenclature, namely "dead letter queues", to make this feel more familiar to users.
pub enum OutputBuffer { | ||
Drop, | ||
Reject, | ||
Store(Vec<EventArray>), |
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.
Store
here is for events that are going to passed to a downstream component?
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.
Correct. I'll note that.
❌ Deploy Preview for vrl-playground failed.
|
- The proposed changes will impact emission and receipt of end-to-end acknowledgements. | ||
- The proposed configuration options should fit in the configuration schema, but may have | ||
implications for its interpretation, particularly around the topology connections. | ||
- Handling discarded events in any way will change the interpretation of several exiting internal |
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.
- Handling discarded events in any way will change the interpretation of several exiting internal | |
- Handling discarded events in any way will change the interpretation of several existing internal |
Overall I'm looking at what I assume are the two most common situations I think users would want.
|
|
||
### User Experience | ||
|
||
#### Add Discarded Event Output |
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.
It's a bit out of scope for this RFC, but very much related -- adding an event output for events that successfully went through a sink would allow customers to provide better observability into events that were successfully delivered. For example, we would like to extract metrics from delivered events so we can show our customers how many events we successfully delivered. As it is now, we can only do that before the sink.
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.
Feel free to file an issue about this, but as worded, it sounds like what you're looking for are the existing component metrics such as component_sent_events_total
, which sinks do emit.
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 this might clarify what I meant: #14708 (comment)
Note we will probably need to resolve #14969 as part of this work as it will make the incorrect type definitions much more common. |
Ideally this is also supported in |
Rendered
Ref: #1772 #12217 #13549 #16432 #16432 #17962