xref: /aosp_15_r20/external/pigweed/pw_rpc/design.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_rpc-design:
2
3================
4Design & roadmap
5================
6.. pigweed-module-subpage::
7   :name: pw_rpc
8
9.. _module-pw_rpc-design-overview:
10
11--------
12Overview
13--------
14The semantics of ``pw_rpc`` are similar to `gRPC
15<https://grpc.io/docs/what-is-grpc/core-concepts/>`_.
16
17.. _module-pw_rpc-design-lifecycle:
18
19RPC call lifecycle
20==================
21In ``pw_rpc``, an RPC begins when the client sends an initial packet. The server
22receives the packet, looks up the relevant service method, then calls into the
23RPC function. The RPC is considered active until the server sends a status to
24finish the RPC. The client may terminate an ongoing RPC by cancelling it.
25Multiple concurrent RPC requests to the same method may be made simultaneously
26(Note: Concurrent requests are not yet possible using the Java client. See
27`Issue 237418397 <https://issues.pigweed.dev/issues/237418397>`_).
28
29Depending the type of RPC, the client and server exchange zero or more protobuf
30request or response payloads. There are four RPC types:
31
32* **Unary**. The client sends one request and the server sends one
33  response with a status.
34* **Server streaming**. The client sends one request and the server sends zero
35  or more responses followed by a status.
36* **Client streaming**. The client sends zero or more requests and the server
37  sends one response with a status.
38* **Bidirectional streaming**. The client sends zero or more requests and the
39  server sends zero or more responses followed by a status.
40
41.. _module-pw_rpc-design-events:
42
43Events
44------
45The key events in the RPC lifecycle are:
46
47* **Start**. The client initiates the RPC. The server's RPC body executes.
48* **Finish**. The server sends a status and completes the RPC. The client calls
49  a callback.
50* **Request**. The client sends a request protobuf. The server calls a callback
51  when it receives it. In unary and server streaming RPCs, there is only one
52  request and it is handled when the RPC starts.
53* **Response**. The server sends a response protobuf. The client calls a
54  callback when it receives it. In unary and client streaming RPCs, there is
55  only one response and it is handled when the RPC completes.
56* **Error**. The server or client terminates the RPC abnormally with a status.
57  The receiving endpoint calls a callback.
58* **Request Completion**. The client sends a message that it would like to
59  request call completion. The server calls a callback when it receives it. Some
60  servers may ignore the request completion message. In client and bidirectional
61  streaming RPCs, this also indicates that client has finished sending requests.
62
63.. _module-pw_rpc-design-services:
64
65Services
66========
67A service is a logical grouping of RPCs defined within a ``.proto`` file. ``pw_rpc``
68uses these ``.proto`` definitions to generate code for a base service, from which
69user-defined RPCs are implemented.
70
71``pw_rpc`` supports multiple protobuf libraries, and the generated code API
72depends on which is used.
73
74Services must be registered with a server in order to call their methods.
75Services may later be unregistered, which cancels calls for methods in that
76service and prevents future calls to them, until the service is re-registered.
77
78Background:
79
80* `Protocol Buffer service
81  <https://developers.google.com/protocol-buffers/docs/proto3#services>`_
82* `gRPC service definition
83  <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_
84
85.. _module-pw_rpc-design-status-codes:
86
87Status codes
88============
89``pw_rpc`` call objects (``ClientReaderWriter``, ``ServerReaderWriter``, etc.)
90use certain status codes to indicate what occurred. These codes are returned
91from functions like ``Write()`` or ``Finish()``.
92
93* ``OK`` -- The operation succeeded.
94* ``UNAVAILABLE`` -- The channel is not currently registered with the server or
95  client.
96* ``UNKNOWN`` -- Sending a packet failed due to an unrecoverable
97  :cpp:func:`pw::rpc::ChannelOutput::Send` error.
98
99.. _module-pw_rpc-design-unrequested-responses:
100
101Unrequested responses
102=====================
103``pw_rpc`` supports sending responses to RPCs that have not yet been invoked by
104a client. This is useful in testing and in situations like an RPC that triggers
105reboot. After the reboot, the device opens the writer object and sends its
106response to the client.
107
108The C++ API for opening a server reader/writer takes the generated RPC function
109as a template parameter. The server to use, channel ID, and service instance are
110passed as arguments. The API is the same for all RPC types, except the
111appropriate reader/writer class must be used.
112
113.. code-block:: c++
114
115   // Open a ServerWriter for a server streaming RPC.
116   auto writer = RawServerWriter::Open<pw_rpc::raw::ServiceName::MethodName>(
117       server, channel_id, service_instance);
118
119   // Send some responses, even though the client has not yet called this RPC.
120   CHECK_OK(writer.Write(encoded_response_1));
121   CHECK_OK(writer.Write(encoded_response_2));
122
123   // Finish the RPC.
124   CHECK_OK(writer.Finish(OkStatus()));
125
126.. _module-pw_rpc-design-errata:
127
128Errata
129------
130Prior to support for concurrent requests to a single method, no identifier was
131present to distinguish different calls to the same method. When a "call ID"
132feature was first introduced to solve this issue, existing clients and servers
133(1) set this value to zero and (2) ignored this value.
134
135When initial support for concurrent methods was added, a separate "open call ID"
136was introduced to distinguish unrequested responses. However, legacy servers
137built prior to this change continue to send unrequested responses with call ID
138zero. Prior to `this fix
139<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/192311>`_, clients
140which used "open call ID" would not accept unrequested responses from legacy
141servers. Clients built after that change will accept unrequested responses which
142use both "open call ID" and call ID zero.
143
144See `Issue 237418397 <https://issues.pigweed.dev/issues/237418397>`_ for more
145details and discussion.
146
147.. _module-pw_rpc-design-naming:
148
149------
150Naming
151------
152
153For upstream Pigweed services, this naming style is a requirement. Note that
154some services created before this was established may use non-compliant
155names. For Pigweed users, this naming style is a suggestion.
156
157Reserved names
158==============
159``pw_rpc`` reserves a few service method names so they can be used for generated
160classes. The following names cannot be used for service methods:
161
162- ``Client``
163- ``Service``
164- Any reserved words in the languages ``pw_rpc`` supports (e.g. ``class``).
165
166``pw_rpc`` does not reserve any service names, but the restriction of avoiding
167reserved words in supported languages applies.
168
169Service naming style
170====================
171``pw_rpc`` service names should use capitalized camel case and should not use
172the term "Service". Appending "Service" to a service name is redundant, similar
173to appending "Class" or "Function" to a class or function name. The
174C++ implementation class may use "Service" in its name, however.
175
176For example, a service for accessing a file system should simply be named
177``service FileSystem``, rather than ``service FileSystemService``, in the
178``.proto`` file.
179
180.. code-block:: protobuf
181
182   // file.proto
183   package pw.file;
184
185   service FileSystem {
186       rpc List(ListRequest) returns (stream ListResponse);
187   }
188
189The C++ service implementation class may append "Service" to the name.
190
191.. code-block:: cpp
192
193   // file_system_service.h
194   #include "pw_file/file.raw_rpc.pb.h"
195
196   namespace pw::file {
197
198   class FileSystemService : public pw_rpc::raw::FileSystem::Service<FileSystemService> {
199     void List(ConstByteSpan request, RawServerWriter& writer);
200   };
201
202   }  // namespace pw::file
203
204.. _module-pw_rpc-roadmap:
205
206-------
207Roadmap
208-------
209Concurrent requests were not initially supported in pw_rpc (added in `C++
210<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/109077>`_, `Python
211<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/139610>`_, and
212`TypeScript
213<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/160792>`_). As a
214result, some user-written service implementations may not expect or correctly
215support concurrent requests.
216