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

Add Status and StatusOr classes #10550

Merged
merged 14 commits into from
Apr 13, 2020
Merged

Add Status and StatusOr classes #10550

merged 14 commits into from
Apr 13, 2020

Conversation

yanavlasov
Copy link
Contributor

@yanavlasov yanavlasov commented Mar 26, 2020

Add Status and StatusOr classes.
These classes are to be used for removing exceptions in HTTP codecs.
The Status class is for returning reach error information, similar to exception types. It is an alias of absl::Status.
The StatusOr class is a fork of not yet open source absl::StatusOr class. Once absl releases StatusOr the fork will be removed.

Risk Level: None
Testing: Unit Tests
Docs Changes: N/A
Release Notes: N/A

Signed-off-by: Yan Avlasov yavlasov@google.com

These classes are to be used for removing exceptions in HTTP codecs.

Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Copy link
Contributor

@asraa asraa left a comment

Choose a reason for hiding this comment

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

Thank you!! Some initial comments...

source/common/http/status.h Outdated Show resolved Hide resolved
source/common/http/status.h Outdated Show resolved Hide resolved
source/common/http/status.h Outdated Show resolved Hide resolved
source/common/http/status.h Outdated Show resolved Hide resolved
source/common/http/status.h Outdated Show resolved Hide resolved
Copy link
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.

Thanks for working on this. I have a high level question before digging into the details.

/wait-any

source/common/http/status.h Outdated Show resolved Hide resolved
* Constructing an error status is likely as heavy as throwing an exception due to a heap
* allocation.
*/
class ABSL_MUST_USE_RESULT Status final {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we at least attempt to re-use abseil status and use the status payload for application-specific errors?
Having multiple statuses is a real pain.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, I'd really like to understand what absl::Status is lacking and see if we can resolve that.

Copy link
Contributor

@asraa asraa Mar 30, 2020

Choose a reason for hiding this comment

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

Hm I see. We could potentially map our five (OK, CodecProtocol, BufferFlood, etc) to five of the absl canonical errors, and set up a kHttpCode payload to hold the HttpCode in the case of a PrematureResponseException.

Other things we want are:

  • a StatusOr -- maybe we need in-house absl::variant holding absl::Status that can be a drop in when absl::StatusOr is released?
  • the potential to prevent overwriting a bad status (we catch and set a codec error, ASSERT we don't try to set it again).

Copy link
Member

Choose a reason for hiding this comment

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

See my comment above also? Would all of this be solved via a template parameter/struct (which could be empty) added to the underlying status class?

Copy link
Contributor

Choose a reason for hiding this comment

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

re: StatusOr -- it is about to be open sourced (probably weeks, not months). For now, the recommendation is to fork it (e.g. https://github.com/google/cel-cpp/tree/master/base), and let the abseil team know. Using abseil standard status should provide better experience with testing macros, proto/grpc status alignment, etc down the line I'd think.

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've spent some time talking to the absl engineer about how to make absl::Status work with custom error spaces and here are the takeaways.

@ahedberg The only question is how to reconsile absl::Status error space with the error space of Envoy dataplane.

  • @mattklein123 It is highly unlikely that there will be ability to change error codes of absl::Status by any means. It is tied to the "canonical Google error space" and flexibility of changing it is outside of the scope of this class. This was actually considered before and discarded as it was adding unneeded (internally to Google) complexity.
  • Recommended and the only way to modify error codes is to use absl::Status payloads which is a set of key/value pairs. Value is an opaque byte buffer. As suggested by @kyessenov

So the API to the error facility might look something like this if we go with the absl::Status

namespace Envoy {
using Status = absl::Status;

enum class StatusCode : int {
  CodecProtocolError = 0,
  BufferFloodError = 1,
  PrematureResponseError = 2
};

// Methods for creating error codes
Status MakeCodecProtocolError(absl::string_view message);
Status MakeBufferFloodError(absl::string_view message);
Status MakePrematureResponseError(absl::string_view message, Http::Code http_code);

// Methods for checking and extracting error information
StatusCode GetStatusCode(Status);
bool IsCodecProtocolError(Status);
// ...

} // namespace Envoy

Internally these methods will create absl::Status with e.g. "Internal" error code and a payload with Envoy's error code and optionally the http_code. The extractors will get the error details from the payload. We could also do error code mapping as @asraa suggested. This would make allocation cost go up for one class of error only.

Downsides are:

  1. Creating a payload needs two allocations
  2. Extracting may need one too, unless we want to deal with the absl::Cord iterators and reconstruct payload byte by byte.
  3. absl::Status can not be constructed directly and its code() method always return the same error code. It may be a gotcha for someone who does not know that there is additional API to be used for constructing and checking error codes.

The upside is that:

  1. We do not need to maintain absl::Status. Although we will need to maintain the payload serializer/deserializer code.
  2. absl::StatusOr comes for free as well as it will use absl::Status

Let me know your thoughts. It seems like people want to avoid forking these two classes and I agree this is not ideal.

Copy link
Member

Choose a reason for hiding this comment

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

IMO the upsides of reusing the existing classes outweigh the negatives, especially because in the happy path there would be no extra allocations, so it seems fine to me.

Conceptually though, I still don't understand why the payload mechanism couldn't be templated in such a way where the default implementation is the key/value pair map, and then we could have our own implementation with our own well specified struct. This would force StatusOr to also be templated on our templated Status type. This seems like the optimal solution, albeit a bunch more work, so I suppose it probably would not happen.

Signed-off-by: Yan Avlasov <yavlasov@google.com>
@yanavlasov yanavlasov changed the title Add Status and StatusOr classes. Add Status class Apr 6, 2020
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
@yanavlasov yanavlasov changed the title Add Status class Add Status and StatusOr classes Apr 6, 2020
Copy link
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.

Thanks this looks like it's on the right track. Thanks for iterating on not forking more code than we need to. A few high level comments/questions.

/wait

source/common/common/status.cc Outdated Show resolved Hide resolved
source/common/common/status.cc Outdated Show resolved Hide resolved
}
}

StatusCode ToEnvoyStatusCode(absl::StatusCode code) {
Copy link
Member

Choose a reason for hiding this comment

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

The below mappings seem kind of strange. Do we need these mappings?

Edit: I see that this is a mapping to attempt not using internal error in all cases, is that right? If so maybe add a bunch more comments? This seems fragile and I wonder if we should just always take the perf hit to use the embeded payload to describe the status? wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

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

I personally think this is OK, can we add a table at the top of the header file, and comments in the enum definition for StatusCode? I think people do generally map their codes on to the canonical codes, eg (https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto).

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've changed it to use payloads in all cases, just for comparison. Check if it is better.

source/common/common/status.h Outdated Show resolved Hide resolved
source/common/common/statusor.cc Outdated Show resolved Hide resolved
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Exclude third_party from check_format

Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
source/common/common/status.cc Outdated Show resolved Hide resolved

namespace Envoy {

using absl::StatusOr; // NOLINT: disable misc-unused-using-decls clang-tidy check
Copy link
Member

Choose a reason for hiding this comment

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

use NOLINT(misc-unused-using-decls)

Copy link
Member

Choose a reason for hiding this comment

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

Do we need this file at all? Can't we temporarily just include the third party file directly until we get it from absl?

Copy link
Contributor Author

@yanavlasov yanavlasov Apr 10, 2020

Choose a reason for hiding this comment

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

I was thinking that it would make transition from fork to released version easier. Otherwise a bunch files would need to have include paths and BUILD dependencies changed. absl does not have a timeline for releasing StatusOr it can be awhile.

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

Thanks generally looks good to me. A few small comments and will defer to @asraa and others for further review.

/wait

source/common/common/status.cc Outdated Show resolved Hide resolved
source/common/common/status.cc Outdated Show resolved Hide resolved
source/common/common/status.h Outdated Show resolved Hide resolved

namespace Envoy {

using absl::StatusOr; // NOLINT: disable misc-unused-using-decls clang-tidy check
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this file at all? Can't we temporarily just include the third party file directly until we get it from absl?

@@ -0,0 +1,441 @@
/**
Copy link
Member

Choose a reason for hiding this comment

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

I doubt this is going to get run so is it worth copying?

Copy link
Contributor Author

@yanavlasov yanavlasov Apr 10, 2020

Choose a reason for hiding this comment

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

I've renamed some methods of the forked StatusOr public API after talking to absl engineer to match what is planned to be open sourced. The file is forked from tensorflow and API names were outdated. So I needed the test to make sure I did not screw something up. I mean it does not hurt to keep it for now.

Copy link
Member

Choose a reason for hiding this comment

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

Sure sounds good.

Copy link
Contributor

@asraa asraa 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 awesome, thank you for all your work! LGTM, non-blocking comment addition

source/common/common/status.h Outdated Show resolved Hide resolved
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Copy link
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!

@asraa asraa merged commit 44eedc7 into envoyproxy:master Apr 13, 2020
return "PrematureResponseError";
case StatusCode::CodecClientError:
return "CodecClientError";
}
Copy link
Member

Choose a reason for hiding this comment

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

we're getting compilation issues on Windows as this helper function does not return a value in all cases, we're going to make a PR to fix this and add a RELEASE_ASSERT in the default case (and change all all the below ASSERTs to RELEASE_ASSERTs), if an exception is more appropriate, please let us know

Copy link
Member

Choose a reason for hiding this comment

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

The documentation of getStatusCode mentions a RELEASE_ASSERT but is using ASSERT

Copy link
Member

Choose a reason for hiding this comment

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

Disregard the part about ASSERT -> RELEASE_ASSERT as I see the comments now asking to do the opposite

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.

7 participants