Skip to main content

Overview

This page discusses methods on your durable state data types. To learn more about how to declare your methods in a .proto file see the Schemas and interfaces section.

Safety guarantees are a core feature of Reboot. Based on an RPC’s method kind, Reboot can guarantee answers to questions such as:

  • Will an RPC perform any mutations of a state?
  • Will an RPC perform any mutations on other states?
  • If this RPC performs mutations on other states, when do those effects become visible?
  • What other RPCs can run concurrently with this RPC?

Enforcing these guarantees naturally creates a safety hierarchy, in which methods can only call other methods that enforce similar or stronger guarantees. This allows Reboot to:

  • Safely maximize the number of operations running in parallel on a state, while still preserving safety guarantees.
  • Take over complicated safety semantics for you, like locking and transactionality across states.
  • Monitor the call graph, to guarantee that transitive calls don't have unintended side-effects, even as code changes.

Kinds

The four kinds of methods in Reboot are:

Kind
readerreader methods are allowed to read from but not mutate state. Every reader is given a read-only snapshot of the state allowing multiple readers to execute concurrently on a given state instance. readers can only call other readers.
writerwriter methods can both read from and mutate state. writer execution happens serially on a given state, allowing each update to happen atomically. To enforce write atomicity, writers can call any readers, but they cannot call other writers.
transactiontransactions combine multiple read/write operations into a single atomic action, often across multiple states. All of the reads and writes included in the transaction will happen atomically: once a state is involved in a transaction, no other RPCs may mutate its state until that transaction is complete. If any part of the transaction fails, the entire transaction will be rolled back. transactions may call readers, writers, and other transactions.
workflowComing soon!

Calling a method

Usually you'll call methods via generated Python, TypeScript, or React client code (or libraries that wrap the generated code).

In order to call a method on an instance of your state data types you need to first get a reference to it, e.g., on the backend by calling .lookup() and in React by using one of the generated hooks.

Once you have a reference you can call methods on it. Calling a method requires passing a context as the first argument.

Depending on whether you're trying to call a method "inside" or "outside" a Reboot application, it will be either a Context or an ExternalContext.

From "inside" a Reboot application

By "inside" a Reboot application we specifically mean within one of the methods that you've implemented for your servicers. Each of those methods takes a context argument that you can use to make calls:

  • ReaderContext - Passed to a reader.
  • WriterContext - Passed to a writer.
  • TransactionContext - Passed to a transaction.
  • WorkflowContext - Passed to a workflow.

The types of these contexts allow Reboot (as well as static type checkers like mypy, Pyright, or tsc) to enforce its safety guarantees throughout the call graph.

From "outside" a Reboot application

By "outside" a Reboot application we mean from a (micro)service not built with Reboot (e.g., a gRPC or HTTP based service), or a lambda running in some serverless environment, or your even your laptop! Your tests are another example of "outside" a Reboot application.

In order to call a method on an instance of your state from "outside" a Reboot application you'll need to use an ExternalContext, for example:

context = ExternalContext(
name="send message",
url="http://localhost:9991"
)

hello = Hello.lookup(id)

response = await hello.Send(context, message="Hello, World!")

The url argument specifies where your Reboot application is running, e.g., on your laptop via rbt dev, in a container on Kubernetes via rbt serve (where url will likely route to your Kubernetes gateway), or in the cloud via rbt cloud.

Because an ExternalContext is used outside of Reboot, a series of Reboot methods called using an ExternalContext don't have any atomicity guarantees with regard to one another (although the individual methods executing within Reboot will of course still have their own atomicity). If you are calling multiple Reboot methods that you would like to happen atomically, you can move those calls into a transaction, and then call that method from your client.

Reactivity

Using an ExternalContext you can also call reader methods reactively, for example:

fig = Fig.lookup(fig_id)
async for response in fig.reactively().GetPosition(context):
print(f"{fig_id}: {response}")

HTTP

Reboot can also be accessed directly via HTTP when you don't have access to Reboot generated code (e.g., we don't generate code in the language you're using). See here for more information about Reboot's additional required headers when compared to gRPC.

For example, to call the reader method called Messages on the Hello state type (declared in the hello.v1 package):

curl -XPOST http://localhost:9991/hello.v1.HelloMethods/Messages \
-H "x-reboot-state-ref:hello.v1.Hello:reboot-hello"

Or to call the writer method called Send:

curl -XPOST "http://localhost:9991/hello.v1.HelloMethods/Send" \
-H "x-reboot-state-ref:hello.v1.Hello:reboot-hello" \
-d '{"message":"Hello, World!"}'