Lever RPCs

This page describes Lever's builtin RPC system

👍

Key takeaways

  • Lever has a builtin RPC system with batteries included.
  • It allows communication either between two Lever services or between a client and a Lever service.
  • Two types: regular (request-reply) and streaming (opens a bidirectional communication channel).
  • Can send either as an efficient JSON-like encoding or just raw bytes.

The Lever RPC system is a builtin way of communicating with Lever services. It is used for invoking Lever methods either from within a Lever environment (service-to-service invokation) or from outside Lever altogether (client-to-service invokation).

The Lever RPC system provides a standardized way of calling Lever methods and has a series of features built into itself.

  • Load-balancing invokations between the Lever instances in a service.
  • Auto-discovery of said instances.
  • Routing invokations when resources are used.
  • A universal addressing system (see Lever URLs).
  • Efficient JSON-like encoding for the arguments and the result of the method invokation.
  • Streaming capabilities (see below).
1642

Multiple Lever services interconnected via the builtin RPC system

As you can see below, there are two types of Lever RPCs: regular and streaming.

Regular RPCs

Regular RPCs are the most common type of RPCs. They represent a simple request-reply type of call. The caller specifies the method and a list of arguments and receives a single reply as a result of the invokation.

RPC invokation arguments and the replies are typically small in size (well under 1MB). This is because the entirety of the arguments and the reply need to fit in the memory of the Lever instance. If that is not the case, you should consider using a streaming RPC.

Streaming RPCs

A streaming RPC, instead of causing a one-off request-reply, it creates a communication channel between the caller and the callee. The channel can remain open as long as necessary and multiple messages can be exchanged between the two parties.

The methods invoked via streaming RPCs are called streaming methods. Streaming methods names must end with Chan or _chan to be recognized as such.

Invoking a streaming RPC still requires a list of invokation arguments to start the conversation. When the invokation reaches a Lever instance, a two-way channel is created through which both the caller and the callee can send and receive messages. The two directions of the conversation are independent from each other, leaving the developer with the freedom of designing their own set of rules to communicate over that channel.

Once the streaming channel is created, the messages that flow through it are guaranteed to reach the same Lever instance. Note however, that your application should be resilient to network failure and in such cases, the channel may be closed. The caller would then need to re-establish communication with the service (perhaps by invoking the same streaming method again). When doing so, there is no guarantee that the new streaming RPC will reach the same instance within that service (unless using resources).

Example use cases for streaming RPCs include

  • Uploading / downloading content without loading it in memory in its entirety.
  • Negotiating parameters between caller and callee.
  • Push notifications and real-time synchronization.

🚧

Coming soon

Note that long-lived streaming RPC calls over the HTTP interface are not supported. The use-case will be available in a future version as a WebSocket-based gateway along with a browser client library that makes use of it.

Using long-lived streaming RPC calls from the Node client or the Go client is fine.

Encoding

Lever's RPC system uses an optional JSON-like encoding. It has the same data types as JSON, but is encoded in binary behind the scenes, for efficiency.

If you don't want to use the JSON-like encoding, you can also send raw bytes. The two data types can be used interchangeably when working with Lever API libraries. However, the caller and the implementation of the method must agree on the data type used (or in some cases, detect the type at runtime). The following data support both the JSON-like encoding and raw bytes.

  • Invokation args - JSON array or bytes (note that a single chunk of bytes is used instead of the whole array - you cannot use an array with mixed JSON and byte arguments).
  • Invokation result - JSON value or bytes.
  • Invokation error - JSON value or bytes.
  • Individual stream messages - JSON value or bytes.
  • Stream error - JSON value or bytes.

Errors

When invoking Lever methods over RPC, there are situations where an error might be returned. This could be due to any of the following factors:

  • The caller used invalid arguments for the method invokation.
  • The method implementation returned an error.
  • There was a problem with the network connection.
  • There was an internal error within Lever itself (either a bug or Lever is not available).

Note that in some of these cases, receiving an error does not mean that the invokation has had no effect. You should be aware of this when designing your methods. Also note that the official client libraries retry the call automatically in some cases). If you need to be strict about once-and-only-once semantics, you might want to include additional tokens / identifiers for the invokations and verify in the uniqueness as part of the method implementation.