Capnp: Uri format and possible error cases for capabilities

I’m trying to implement a consensus system and want to use capnp internally (I’m not going to end up using the pipe-lining features, but was looking for a well engineered, minimally buggy library).

There are two things that I can’t quite work out.

  • How to get a capability(service) for a remote server, (as in how are the uri’s specified).
  • What errors can be thrown when doing an rpc, thus allowing me to automatically retry if the network dies/server dies.

Uri: So I just noticed that there was a line later on in the example for a python client connecting

capnp://insecure@127.0.0.1:7000

However I’d imagine that the insecure part of the uri should be replaced by the id of the service?

Automatically retrying: The main issue with this is that I can’t see what errors could be thrown by the rpc, thus which are recoverable.
Specifically I need this to be resistant to node failures, so if a capability errors out, then I presume that I need to re-fetch that capability from the uri/sturdy-ref.
I’d expect the errors to be thrown in cases where the network had failed, however would those just be the errors of the underlying socket?

It has been quite cool to work with so far and I would love to use this in my project!

The server can use Capnp_rpc_unix.Cap_file.save_service to write out a .cap file with the address, which you can then give to the client. There is no way for the client to guess the URI itself, because the URI includes a secret granting the client access to the service.

See the example in https://github.com/mirage/capnp-rpc#networking, which also describes the components of the URI.

You can use Capability.problem when you get an exception to check if your connection to the service is still OK. Capnp_rpc.Exception has a type field which includes Disconnected as one of the options.

You could also use Capability.when_broken to register a callback that will be called as soon as the connection to the service fails, in case you want to reconnect immediately, rather than waiting until the next call fails.

In summary:

  1. Some service.foo() call fails, returning an exception.
  2. You can use Capability.problem service to see whether your connection to the service itself is still OK (this just checks some local state and is very fast).
  3. If there’s no problem with service, you are free to make more calls on the object (the exception you got was just for that call). Otherwise, either use your sturdy-ref to get a new connection or (if you don’t have one) propagate the exception until it reaches someone who does and can restart from there.
1 Like

I take it then that the .cap file then needs to be passed out of band to the clients? Or alternatively use the insecure keyword instead, but then I’m presuming that the service id would be unguessable?

I take it then that the .cap file then needs to be passed out of band to the clients?

Yes. If you already had a capnp connection, you could use the persistence API instead.

Or alternatively use the insecure keyword instead, but then I’m presuming that the service id would be unguessable?

insecure is only for interop with endpoints that don’t support TLS at all. It won’t work with a normal (TLS-enabled) endpoint.

1 Like