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