This is something I've wanted to do for a while (built into the `capnp`
tool).
I agree that RPC types should be introspectable. I'd design the interface
like this:
getSchema @0 () -> (typeId :UInt64, nodes :List(Schema.Node))
`nodes` would be a list of schema nodes describing the transitive closure
of dependencies. (Each `Node` describes one type, like an interface type, a
struct type, etc. See schema.capnp for details.)
CodeGeneratorRequest isn't quite the right thing to use here, because:
- It's specifically designed to provide information needed to generate
source code, which is a superset of what you need at runtime. Schema.Node
is intended to contain specifically the info that's useful at runtime.
- The CodeGeneratorRequest may include a lot of types that aren't actually
used by the specific interface.
- We don't actually embed the full CodeGeneratorReequest inside the
generated code. We embed each Node separately. I wouldn't want to store a
second copy of all that data, and trying to rebuild the CGR from the
components seems a little weird.
Next thing: I don't think the generated code would implement the
getSchema() RPC directly for each type. Instead I think I'd add a new
method to the `Capability::Server` and `ClientHook` like:
virtual kj::Promise<capnp::InterfaceSchema> getSchema() = 0;
The generated code for each Server type would implement this virtual
method, which would be a one-liner (`return
capnp::Schema::from<ThisType>();`).
It would be the RPC system's responsibility to actually turn these into RPC
calls, and to maintain a capnp::SchemaLoader used to translate
I think the RPC system would have to be responsible for maintaining a
capnp::SchemaLoader containing all schemas that had been loaded from the
remote server, in order to implement the local version of `getSchema()`.
I'm a bit worried about redundantly transmitting the same schema nodes over
the wire many times, especially when dealing with many different interfaces
that implement the same underlying types. I wonder if we need a more
complicated mechanism to deal with this. Maybe the RPC interface would let
the client specify which schemas they already know about. Or maybe this
would be tracked within the RPC system itself on a per-connection basis?
There's also the problem that two different servers could have conflicting
versions of a schema, but could both end up proxying through the same third
server, meaning you might get different versions of the same schema for
different capabilities on the same connection. Maybe it's not actually
feasible to share schemas between capabilities at all? Ugh.
So... yeah, I really want to see this happen, but there's kind of a lot of
design questions to work through. Big project.
-Kenton
On Tue, Sep 15, 2020 at 6:52 AM Ryan Patterson <[email protected]>
wrote:
> I'm in the process of writing an interactive debugger for capnp. It's easy
> enough to manually provide a schema when connecting to another peer, but I
> think it would be good long-term to support dynamic introspection as a
> feature of the RPC system.
>
> I'm quite new to capnp in general, but after reading over the
> documentation, I think the best way to do this is for capnp to dictate a
> standardized optional interface which returns the original
> CodeGeneratorRequest that was used to implement the peer. Ideally, the code
> generator could automatically implement this interface for every generated
> interface, or perhaps the capnp library could automatically implement for
> on Bootstrap and Accept capabilities. When interacting with services which
> don't support introspection (possibly because of space, security, or other
> concerns), the method call simply fails because it refers to an invalid
> interface ID. The important thing is that such an interface has to have a
> fixed ID to be useful, which is why I think it should be established as a
> core part of capnp.
>
> Here is my straw man version of this interface. I've selected AnyPointer
> so that implementing this interface doesn't require pulling the entire
> capnp schema into your own schema. Since the capnp schema is known to both
> parties (even if it's a different version), this is fine.
>
> interface Introspectable {
> codeGeneratorRequest @0 () -> (cgr :AnyPointer);
> }
>
> --
> You received this message because you are subscribed to the Google Groups
> "Cap'n Proto" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/capnproto/131b69c1-8782-4935-97d0-6fd11fa31e06n%40googlegroups.com
> <https://groups.google.com/d/msgid/capnproto/131b69c1-8782-4935-97d0-6fd11fa31e06n%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>
--
You received this message because you are subscribed to the Google Groups
"Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/capnproto/CAJouXQknuEShkOQn_DE_EEXDsCT%3D6uMDSV7aHMVeUyWDDY64xg%40mail.gmail.com.