Skip to content

ratelimit: convert to Grpc::AsyncClient.#1267

Merged
htuch merged 11 commits intoenvoyproxy:masterfrom
htuch:ratelimit-async-client
Jul 18, 2017
Merged

ratelimit: convert to Grpc::AsyncClient.#1267
htuch merged 11 commits intoenvoyproxy:masterfrom
htuch:ratelimit-async-client

Conversation

@htuch
Copy link
Copy Markdown
Member

@htuch htuch commented Jul 14, 2017

No description provided.

@htuch
Copy link
Copy Markdown
Member Author

htuch commented Jul 14, 2017

@mattklein123 This is WiP - I still need to add some more unit tests for coverage and the actual integration test that motivated this, but thought I would throw it out for comments early.

Copy link
Copy Markdown
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

A few comments but at a high level LGTM.


option go_package = "ratelimit";

option cc_generic_services = true;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It might be out of scope for this change, but if possible can we pull this proto from envoy-api and delete this file also? There is a GH open on that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, I think we want to cut to the v2 API when we switch to envoy-api, and there we have a different package namespace etc., so that's best left as future work.

ASSERT(callbacks_);
channel_->cancel();
ASSERT(callbacks_ != nullptr);
ASSERT(stream_ != nullptr);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: will crash on the next line in obvious way

callbacks_->complete(status);
callbacks_ = nullptr;
}
LimitStatus limit_status = LimitStatus::OK;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Question: It seems kind of odd to do this logic in the context of onRemoteClose(). Why not onReceiveMessage() ? I guess the larger question is for unary APIs (which are very common) can we make coding against this easier vs. having to remember to close the stream, etc.?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yep, this is a good idea, I was planning to provide a unary RPC interface that didn't require users to keep track of the minutiae of stream state before finalizing the PR.

@htuch
Copy link
Copy Markdown
Member Author

htuch commented Jul 17, 2017

@mattklein123 This now works and is ready for review. It's pretty big - if you are OK reviewing as is that's great, otherwise I can try and tease apart stuff into smaller PRs.

@msample I'm hoping this resolves your issue when using Grpc::AsyncClient in your custom filter.

@mattklein123
Copy link
Copy Markdown
Member

I'm working on fixing #1254 then I can review.

Copy link
Copy Markdown
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

very nice. few comments.

AsyncClientCallbacks<ResponseType>& callbacks,
const Optional<std::chrono::milliseconds>& timeout) PURE;
virtual AsyncStream<RequestType>* start(const Protobuf::MethodDescriptor& service_method,
AsyncStreamCallbacks<ResponseType>& callbacks,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since we are now splitting streaming and unary, can we kill the timeout in the streaming case? IMO it doesn't make any sense. (I know the underlying AsyncClient stuff still has this problem, but we might as well fix it here?)


grpc_stream->set_stream(http_stream);
grpc_stream->moveIntoList(std::move(grpc_stream), active_streams_);
return dynamic_cast<AsyncRequestImpl<RequestType, ResponseType>*>(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

perf nit: This has cost. Typically I would just hold a local reference/pointer to the unique_ptr, then return it after you add to the list.

LinkedObject<AsyncClientStreamImpl<RequestType, ResponseType>>::removeFromList(
parent_.active_streams_);
if (LinkedObject<AsyncStreamImpl<RequestType, ResponseType>>::inserted()) {
parent_.dispatcher_.deferredDelete(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You probably already looked at this, but just double check that the stream/request does not reference client in its destructor, or there might be ordering issues depending on how client is destructed. (I haven't looked at code but just pointing this out from past experience).

// the immediate failure case.
if (inserted()) {
removeFromList(parent_.active_streams_);
dispatcher().deferredDelete(removeFromList(parent_.active_streams_));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same comment here about referencing client in destructor.


typedef Grpc::AsyncRequestCallbacks<pb::lyft::ratelimit::RateLimitResponse> RateLimitAsyncCallbacks;

class GrpcClientImpl : public Client, public RateLimitAsyncCallbacks {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In looking at this code again, I realize that it's kind of dumb that we make a new "client" for every filter/request. Can you just drop a TODO in here while you are in here to optimize this at some point? (We should have thread local client per filter, and track outstanding requests).

mattklein123
mattklein123 previously approved these changes Jul 18, 2017
Copy link
Copy Markdown
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

nice


AsyncStream<RequestType>* start(const Protobuf::MethodDescriptor& service_method,
AsyncStreamCallbacks<ResponseType>& callbacks) override {
const Optional<std::chrono::milliseconds> no_timeout;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why was this change needed to fix ASAN? This still feels kind of busted to me. Aren't we storing a reference to the timeout inside the AsyncStreamImpl? If so should this be at client scope?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I checked and it does get copied inside the underlying Http::AsyncClient::Stream, in the owned RouteEntryImpl https://github.com/lyft/envoy/blob/0efa18c36d6f789562690b5053c2c4b00987979e/source/common/http/async_client_impl.h#L123.

More generally, this got me wondering about whether we should have a better convention for handling reference ownership semantics in Envoy - it's hard to tell from the type signature of a method alone whether it will store a reference for later use or copy. I think there's a fair bit of mixed use of this in Envoy.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The Grpc::AsyncStreamImpl was not owning the timeout in period between object construction and passing to the Http::AsyncClient::Stream in initialize(), which is what triggered the ASAN fail.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see, it gets used in the context of initialize(). Sure open to suggestions on naming. Same problem applies to pointers or references. Not sure if there is a good naming scheme.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

A first thought is FooBar& for the case when a reference is not retained past the unwinding of the call stack, FooBarRetainedRef for when it is. But, that's super verbose. @dnoe for thoughts as well.

@htuch htuch merged commit 8554be4 into envoyproxy:master Jul 18, 2017
@htuch htuch deleted the ratelimit-async-client branch July 18, 2017 22:27
htuch added a commit to htuch/envoy that referenced this pull request Jul 19, 2017
.

Turns out that a sendLocalReply() will unconditionally send a body after errors such as 504, gRPC
client performs a reset before this and had self-destructed already.
mattklein123 pushed a commit that referenced this pull request Jul 19, 2017
Turns out that a sendLocalReply() will unconditionally send a body after errors such as 504, gRPC
client performs a reset before this and had self-destructed already.
rshriram pushed a commit to rshriram/envoy that referenced this pull request Oct 30, 2018
Automatic merge from submit-queue.

Fix quota cache status assignment.

**What this PR does / why we need it**:Fix a bug in quota amount check in MixerClientImpl::Check().

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes envoyproxy#1181 

**Special notes for your reviewer**:

**Release note**:

```release-note
NONE
```
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.

2 participants