Services and rpc
A service declares a contract, a set of rpc methods; impl implements it. The call Users.get(1) does not know where the
implementation lives: in this process or on another node of the cluster. The result of a call always includes NetError
(§6.5): the network may fail even when the call is currently in process. Access to methods is closed by default:
@public opens a method to the outside, @internal is for calls between services (§13.4).
@error data NotFound { what: str }
service Users {
@public rpc get(id: u64) -> str | NotFound
@public rpc count() -> i32
}
impl Users {
rpc get(id) {
if id == 1 { "ann" } else { fail NotFound{ what: "user {id}" } }
}
rpc count() { 2 }
}
fn show(r: str | NotFound | NetError) io {
match r {
str s => print("ok {s}")
NotFound e => print("not found: {e.what}")
NetError e => print("net error")
}
}
fn main() io, net {
show(Users.get(1))
show(Users.get(7))
match Users.count() {
i32 n => print("{n} users")
NetError e => print("net error")
}
}ok ann not found: user 7 2 users
rpc attributes#
The behaviour of a method is set by attributes next to the contract: @timeout(5s) (default deadline), @idempotent (a repeat
with the same key returns the stored response), @readonly, @retry(n, backoff: b) (only together with @idempotent or
@readonly), @public / @internal / @allow(...); the caller's deadline is passed on along the chain of calls
(§10.2, §8.6). Inside a handler the context ctx is available: the caller (ctx.caller), the deadline,
traceId, meta.
service Info {
@timeout(5s)
@public rpc whoami() -> str
}
impl Info {
rpc whoami() {
let anon = ctx.caller is .Anonymous
let live = ctx.deadline > now() && ctx.deadline <= now() + 5s
"anonymous={anon} deadline-ok={live}"
}
}
fn main() io, net, time {
match Info.whoami() {
str s => print(s)
NetError e => print("net error")
}
}anonymous=true deadline-ok=true
A client to an external address#
Users.connect("host:port", tls: ClientTls{}) returns a Client<Users>: the same methods, but to a concrete address (for example to
a service outside the cluster). gRPC services are imported with import proto (§16.3), and clients in other languages
are generated by oxl gen. Contracts are checked for compatibility against the file oxl.schema.lock
(§9.2, III.2): a breaking schema change is a compile error (schema-break).