Skip to content
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

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

bruceg
Copy link
Member

@bruceg bruceg commented Oct 3, 2022

@bruceg bruceg added type: task Generic non-code related tasks domain: core Anything related to core crates i.e. vector-core, core-common, etc domain: rfc labels Oct 3, 2022
@netlify
Copy link

netlify bot commented Oct 3, 2022

Deploy Preview for vector-project canceled.

Name Link
🔨 Latest commit 861bf04
🔍 Latest deploy log https://app.netlify.com/sites/vector-project/deploys/63634bbc418f2500081eb788

@github-actions github-actions bot removed the domain: core Anything related to core crates i.e. vector-core, core-common, etc label Oct 3, 2022
@bruceg bruceg force-pushed the bruceg/discarded-events-rfc branch from 6c4cb78 to c90126e Compare October 3, 2022 23:04
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
Copy link
Contributor

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.

Copy link
Member

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).

Copy link
Member Author

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.

rfcs/2022-08-25-12217-handling-discarded-events.md Outdated Show resolved Hide resolved
Comment on lines 335 to 336
- 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?
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes! 👍

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.

Comment on lines +343 to +345
- 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?
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Member

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.

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.

rfcs/2022-08-25-12217-handling-discarded-events.md Outdated Show resolved Hide resolved
- Incompatible event type
- Unhandled metric value type
- Unwired component output
- Failure to send to next component
Copy link
Member

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)?

Copy link
Member Author

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.

Copy link
Member

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.

rfcs/2022-08-25-12217-handling-discarded-events.md Outdated Show resolved Hide resolved
Comment on lines 56 to 57
We will introduce a new output to all components that would otherwise discard events, named
`discards`.
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member Author

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
Copy link
Member

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}?

Copy link
Member Author

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.

Copy link
Member

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.

rfcs/2022-08-25-12217-handling-discarded-events.md Outdated Show resolved Hide resolved
Copy link
Contributor

@tobz tobz left a 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.

rfcs/2022-08-25-12217-handling-discarded-events.md Outdated Show resolved Hide resolved
Comment on lines +128 to +133
/// 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,
Copy link
Contributor

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.

Copy link
Member Author

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.

Comment on lines 262 to 265
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.
Copy link
Contributor

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?

Copy link
Member Author

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.

rfcs/2022-08-25-12217-handling-discarded-events.md Outdated Show resolved Hide resolved
rfcs/2022-08-25-12217-handling-discarded-events.md Outdated Show resolved Hide resolved
rfcs/2022-08-25-12217-handling-discarded-events.md Outdated Show resolved Hide resolved
Comment on lines +343 to +345
- 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?
Copy link
Contributor

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.

Copy link
Member

@jszwedko jszwedko left a 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
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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,
Copy link
Member

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.

Comment on lines +343 to +345
- 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?
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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.

Copy link
Member

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.

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.
Copy link
Contributor

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?

Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

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

Noted.

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:

  1. Push to DLQ with retries
  2. If the write to DLQ was successful, acknowledge to the source for this event.
  3. if write to DLQ failed, drop the event and acknowledge to the source for this event.
    Is this a fair understanding?

Comment on lines +52 to +54
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.
Copy link
Contributor

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>),
Copy link
Contributor

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?

Copy link
Member Author

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.

@netlify
Copy link

netlify bot commented Nov 3, 2022

Deploy Preview for vrl-playground failed.

Name Link
🔨 Latest commit 861bf04
🔍 Latest deploy log https://app.netlify.com/sites/vrl-playground/deploys/63634bbc94e212000abde16c

- 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
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- 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

@fuchsnj
Copy link
Member

fuchsnj commented Nov 3, 2022

Overall I'm looking at what I assume are the two most common situations I think users would want.

  1. A new user trying out things, who just wants to see Vector work. They don't care about errors and want them to just drop. Forcing them to create sinks to consume all errors is cumbersome / annoying. There should be an easy way to disable errors.

  2. A more enterprise user that is concerned about events being dropped due to errors and wants to have a single sink that stores all the errors somewhere (s3 / kafka / etc).

    • What does the config look like here. If there are 50 components in a config, do they have to individually list out every single error output in the one sink? That seems very tedious (even if Vector enforces all the error outputs are used).
    • Once Vector is running and starts getting errors collected, the first question I have is, what failed? Do we have information on which error occurred, which component it failed in?
    • Will the events be in a format where I can re-ingest the events. Lets say there was an outage / config error which caused millions of logs to be dead-lettered. The issue was fixed, and I now want to re-process all the dead-lettered events. (I assume this is out of scope, but it's good to at least think about)


### User Experience

#### Add Discarded Event Output
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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)

@jszwedko
Copy link
Member

Note we will probably need to resolve #14969 as part of this work as it will make the incorrect type definitions much more common.

@inftl
Copy link

inftl commented Sep 27, 2023

Ideally this is also supported in sinks. Since a sink can fail inserting, it would be nice to have a backup sink.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain: rfc type: task Generic non-code related tasks
Projects
None yet
Development

Successfully merging this pull request may close these issues.