mirror of https://github.com/grpc/proposal.git
173 lines
6.3 KiB
Markdown
173 lines
6.3 KiB
Markdown
Separate internal-only and public sections of the C++ wrapping
|
|
----
|
|
* Author(s): vjpai
|
|
* Approver: ctiller
|
|
* Status: Approved
|
|
* Implemented in: C++
|
|
* Last updated: July 6, 2017
|
|
* Discussion at: https://groups.google.com/forum/#!topic/grpc-io/Z8S9P4NzGHs
|
|
|
|
## Abstract
|
|
|
|
Maintain separation of C++ classes and functions that are intended
|
|
to be directly used by application developers from those that are
|
|
supposed to be used only through codegen or for that exist for gRPC's
|
|
internal implementation (e.g., interactions with the core API).
|
|
|
|
## Background
|
|
|
|
Essentially all of gRPC C++ lives in `namespace grpc`, giving the
|
|
appearance that all portions are equally intended as part of the
|
|
publicly-exposed API. This gRFC aims to clarify this by categorizing
|
|
the gRPC C++ wrapping, moving some components to `namespace
|
|
grpc::internal`, and moving some class constructors to `private` (with
|
|
friends or appropriately-named `static` factories for internalized generation).
|
|
|
|
### Related Proposals:
|
|
|
|
N/A
|
|
|
|
## Proposal
|
|
|
|
The C++ gRPC wrapping includes numerous classes and functions, but
|
|
they fall into five separate categories:
|
|
|
|
1. Documented for public use by end-user client/server applications
|
|
1. Intended for interfacing with serialization layers such as protobuf
|
|
1. Intended for use through a code-generation layer
|
|
1. Intended for use in the internal implementation of gRPC C++
|
|
1. Intended for interfacing with gRPC core
|
|
|
|
This gRFC aims to clarify that the first two are intended for public
|
|
use (the second one to support custom serializers), but that the last
|
|
three should not be used as such. This will be achieved by
|
|
moving classes and functions in the latter three categories to `namespace
|
|
grpc::internal`.
|
|
|
|
* Accessed through codegen
|
|
- `RpcMethod`
|
|
- `BlockingUnaryCall`
|
|
|
|
* gRPC C++ implementation
|
|
- `CallHook`
|
|
- `CallOpSetInterface`
|
|
- `CallOpSet`
|
|
- `SneakyCallOpSet`
|
|
- `CallNoOp`
|
|
- `RpcMethodHandler`
|
|
- `UnknownMethodHandler`
|
|
- `ClientStreamingHandler`
|
|
- `ServerStreamingHandler`
|
|
- `TemplatedBidiStreamingHandler`
|
|
- `BidiStreamingHandler`
|
|
- `StreamedUnaryHandler`
|
|
- `SplitServerStreamingHandler`
|
|
|
|
* gRPC C++ interface with core
|
|
- `CompletionQueueTag`
|
|
- `Call`
|
|
- `CallOpSendInitialMetadata`
|
|
- `CallOpSendMessage`
|
|
- `CallOpClientSendClose`
|
|
- `CallOpServerSendStatus`
|
|
- `CallOpClientRecvStatus`
|
|
- `CallOpRecvInitialMetadata`
|
|
- `CallOpRecvMessage`
|
|
- `CallOpGenericRecvMessage`
|
|
- `MetadataMap`
|
|
|
|
Additionally, some classes in the first category are only supposed to
|
|
be created through the code generator or method handlers. To support
|
|
this, we will privatize their constructors and allow them to be
|
|
created only by internalized `friend` classes or through `static`
|
|
factories invoked by the code generator (which are themselves moved to
|
|
a new member `struct` called `struct internal` in each of the classes).
|
|
|
|
- `ClientReader`
|
|
- `ClientWriter`
|
|
- `ClientReaderWriter`
|
|
- `ServerReader`
|
|
- `ServerWriter`
|
|
- `ServerReaderWriter`
|
|
|
|
Whereas the code generator could previously use `new
|
|
::grpc::ClientReader<R>`, it will now use
|
|
`::grpc::ClientReader<R>::internal::Create` as a result of this
|
|
proposal. This is slightly bulkier but it makes it clear that public
|
|
code is not supposed to directly construct or `new` an object of type
|
|
`ClientReader`.
|
|
|
|
In some cases, the `static` factory already existed, but will now be
|
|
moved to a `struct internal` member class within the class.
|
|
|
|
- `ClientAsyncResponseReader`
|
|
- `ClientAsyncReader`
|
|
- `ClientAsyncWriter`
|
|
- `ClientAsyncReaderWriter`
|
|
|
|
The effect on the above classes is that the code generator will call
|
|
`::grpc::ClientAsyncResponseReader::internal::Create` instead of
|
|
`::grpc::ClientAsyncResponseReader::Create`. This change is much less
|
|
significant than the above.
|
|
|
|
Interface-only classes that relate to the first category will also be
|
|
moved to the `grpc::internal` namespace, except for the immediate
|
|
parents of final user-exposed classes (since those interfaces are
|
|
useful for creating mock versions).
|
|
|
|
- `AsyncReaderInterface`
|
|
- `AsyncWriterInterface`
|
|
- `ClientAsyncStreamingInterface`
|
|
- `ClientStreamingInterface`
|
|
- `ReaderInterface`
|
|
- `ServerAsyncStreamingInterface`
|
|
- `ServerStreamingInterface`
|
|
- `WriterInterface`
|
|
|
|
## Rationale
|
|
|
|
Despite the categories described above, the language does not provide
|
|
a direct mechanism for specifying this difference, so public users can
|
|
actually use any part of the wrapping that they choose. For example,
|
|
[Tensorflow](https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/core/distributed_runtime/rpc/grpc_worker_service_impl.h)
|
|
bypasses the gRPC code generator and directly uses gRPC components in
|
|
each category except for core-interactions.
|
|
|
|
Although circumventing apparent abstraction layers can be a bid to
|
|
gain performance, it can also hurt achieved application performance by
|
|
locking applications into deprecated gRPC implementation mechanisms
|
|
instead of allowing the latest performance enhancements in gRPC C++.
|
|
|
|
The second category is remaining untouched because we have advertised
|
|
gRPC as accepting pluggable serializers, and this should remain
|
|
untouched. These include `SerializationTraits`, `Slice`, and other
|
|
related classes. Even though these are not used as a common part of
|
|
the documented public API, there are external users that reasonably
|
|
want these and we can continue supporting them.
|
|
|
|
## Implementation
|
|
|
|
This is implemented in C++ in pull-request
|
|
https://github.com/grpc/grpc/pull/11572. It is intended as a safe
|
|
build-only change. In fact, only two places in our own `test` suite
|
|
directly used internal classes and they have been resolved as follows:
|
|
|
|
1. `bm_cq`: completion queue processing benchmark. Since this is a
|
|
microbenchmark, allow it to access `internal::CompletionQueueTag`
|
|
1. QPS worker: this benchmark was directly using
|
|
`ReaderInterface` and `WriterInterface` knowing that they were the
|
|
parent-classes of various sub-classes that could be used in
|
|
the test. Instead, switch this to a `template` and use duck-typing
|
|
instead of inheritance.
|
|
|
|
Ongoing work will be to maintain discipline in deciding whether a
|
|
class or function belongs in `grpc` or `grpc::internal` as well as
|
|
deciding whether a class truly needs a public constructor.
|
|
|
|
## Open issues (if applicable)
|
|
|
|
This PR will cause problems for gRPC users that are using the
|
|
interfaces that are intended for internal use. This should not be a
|
|
large number of gRPC users since these APIs were not documented for
|
|
public use.
|