Skip to content

filter state: add a mark for sharing with the upstream#21700

Merged
kyessenov merged 17 commits intoenvoyproxy:mainfrom
kyessenov:shared_filter_state
Jul 6, 2022
Merged

filter state: add a mark for sharing with the upstream#21700
kyessenov merged 17 commits intoenvoyproxy:mainfrom
kyessenov:shared_filter_state

Conversation

@kyessenov
Copy link
Contributor

@kyessenov kyessenov commented Jun 14, 2022

Commit Message: Adds a new flag for filter state objects that indicates the intent to share with the upstream.
Additional Description: Follow-up to #19809. There have been multiple reports of unexpected lifecycle changes for the filter state objects because they are stored in the transport socket options. This PR addresses this issue by introducing a new mark for filter state that explicitly changes the usage of filter state objects:

Risk Level: medium, revert to the original behavior prior to #19809
Testing: yes
Docs Changes: yes
Release Notes: yes

Signed-off-by: Kuat Yessenov <kuat@google.com>
Signed-off-by: Kuat Yessenov <kuat@google.com>
@repokitteh-read-only
Copy link

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #21700 was opened by kyessenov.

see: more, trace.

Signed-off-by: Kuat Yessenov <kuat@google.com>
Copy link
Member

@ggreenway ggreenway left a comment

Choose a reason for hiding this comment

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

This approach seems good to me.

/wait

class FilterState {
public:
enum class StateType { ReadOnly, Mutable };
enum StateType {
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 it's simpler to just leave this as the previous enum, and add another bool for SharedWithUpstreamConnection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When the upstream L7 filters get done, we'll also need to share with the upstream request I think. I could add another enum for sharing configuration I suppose?

Signed-off-by: Kuat Yessenov <kuat@google.com>
Signed-off-by: Kuat Yessenov <kuat@google.com>
@repokitteh-read-only
Copy link

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to (api/envoy/|docs/root/api-docs/).
envoyproxy/api-shepherds assignee is @lizan
CC @envoyproxy/api-watchers: FYI only for changes made to (api/envoy/|docs/root/api-docs/).

🐱

Caused by: #21700 was synchronize by kyessenov.

see: more, trace.

Signed-off-by: Kuat Yessenov <kuat@google.com>
Signed-off-by: Kuat Yessenov <kuat@google.com>
@kyessenov kyessenov marked this pull request as ready for review June 28, 2022 18:37
@kyessenov
Copy link
Contributor Author

@ggreenway This should be ready now. I could simplify the recently merged extension, too, which was the only client in-tree for filter state within transport socket options.

Signed-off-by: Kuat Yessenov <kuat@google.com>
Copy link
Member

@lizan lizan left a comment

Choose a reason for hiding this comment

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

/lgtm api

breaking API changes approved per exception:
Changes made within 14 days of the introduction of a new API field or message, provided the new field or message has not been included in an Envoy release.

Network::Address::InstanceConstSharedPtr source_address,
Network::TransportSocketPtr&& transport_socket,
const Network::ConnectionSocket::OptionsSharedPtr& options,
const Network::TransportSocketOptionsConstSharedPtr& transport_options = nullptr) PURE;
Copy link
Member

Choose a reason for hiding this comment

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

no default argument for virtual methods per style guide.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Signed-off-by: Kuat Yessenov <kuat@google.com>
Signed-off-by: Kuat Yessenov <kuat@google.com>
@ggreenway ggreenway self-assigned this Jun 29, 2022
Copy link
Member

@ggreenway ggreenway left a comment

Choose a reason for hiding this comment

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

/wait

* that are marked as shared with the upstream connection.
*/
virtual const StreamInfo::FilterStateSharedPtr& filterState() const PURE;
virtual const StreamInfo::FilterState::Objects& filterStateObjects() const PURE;
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 this function name should be more descriptive, maybe something like downstreamSharedFilterStateObjects().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed.

// state.
//
// As a special case, objects that are marked as shared with the upstream
// become bound to the upstream connection life span, regardless of the
Copy link
Member

Choose a reason for hiding this comment

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

clarify: the object will live as long as either the downstream or upstream connection is open. The current wording implies that the lifetime is strictly tied to the upstream connection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Clarified, LMK if it can be improved further.

std::shared_ptr<Object> data_;
StateType state_type_;
StreamSharing stream_sharing_{StreamSharing::None};
std::string name_{""};
Copy link
Member

Choose a reason for hiding this comment

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

The initialization can just be {} instead of {""}, but empty is the default so it can be omitted entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

if (!filter_state) {
return nullptr;
for (const auto& object : options->filterStateObjects()) {
if (auto hashable = dynamic_cast<const Hashable*>(object.data_.get()); hashable != nullptr) {
Copy link
Member

Choose a reason for hiding this comment

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

What happens if an object isn't Hashable? Can we end up with bugs due to matching transport socket options hash even though it contains different filter state? Should FilterState::Object always inherit from Hashable? Or maybe make it required when the object opts in to sharing, via an accessor like getHashable()? We mostly try to avoid dynamic_cast in envoy for this type of feature detection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hashable is only needed for L7->L4 stream transition. For tcp_proxy, it's already 1-1 so hashing is not necessary. I followed https://www.envoyproxy.io/docs/envoy/latest/api-v3/type/v3/hash_policy.proto#envoy-v3-api-msg-type-v3-hashpolicy-filterstate which does the same dynamic cast test.

filter_state.setData(object.name_, object.data_, object.state_type_,
StreamInfo::FilterState::LifeSpan::Connection, object.stream_sharing_);
} catch (const EnvoyException& e) {
ENVOY_LOG(trace, "Failed to set data for '{}': {}", object.name_, e.what());
Copy link
Member

Choose a reason for hiding this comment

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

Ignoring this error seems unsafe; something important may have been discarded. Are there cases where this is expected to happen? Should this have an ENVOY_BUG()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this never throws because the stream info is new and objects have distinct names. Removed try/catch.

Signed-off-by: Kuat Yessenov <kuat@google.com>

struct FilterObject {
std::shared_ptr<Object> data_;
StateType state_type_;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
StateType state_type_;
StateType state_type_{};

default initialize this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@kyessenov
Copy link
Contributor Author

/retest

@repokitteh-read-only
Copy link

Retrying Azure Pipelines:
Check envoy-presubmit isn't fully completed, but will still attempt retrying.
Retried failed jobs in: envoy-presubmit

🐱

Caused by: a #21700 (comment) was created by @kyessenov.

see: more, trace.

Copy link
Member

@lizan lizan left a comment

Choose a reason for hiding this comment

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

LGTM, can you resolve conflicts?

kyessenov added 2 commits July 1, 2022 14:25
Signed-off-by: Kuat Yessenov <kuat@google.com>
Signed-off-by: Kuat Yessenov <kuat@google.com>
@ggreenway
Copy link
Member

/retest

@repokitteh-read-only
Copy link

Retrying Azure Pipelines:
Retried failed jobs in: envoy-presubmit

🐱

Caused by: a #21700 (comment) was created by @ggreenway.

see: more, trace.

Copy link
Member

@ggreenway ggreenway left a comment

Choose a reason for hiding this comment

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

LGTM

@kyessenov kyessenov merged commit 18212bb into envoyproxy:main Jul 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants