Infrastructure code for managing RPCs which evolve over time to use different types at different versions
This module contains infrastructure code for managing RPCs which evolve over time to use different types at different versions. Three scenarios are supported
  • The caller is responsible for managing versions and dispatches to callees that are written in a version-oblivious way.

    The proto-typical example of this scenario is a commander that needs to call out to many assistants for that same system. In this scenario, the assistants each implement a single version of the rpc and the commander has to take this into account.

  • The callee is responsible for managing versions and callers need not bother themselves with any versions.

    The proto-typical example of this scenario is an assistant from one system calling out the commander of another system In this scenario, the assistants each know a single version of the rpc to call and the commander has to implement them all.

  • Both caller and callee cooperate to decide which version to use, each one being able to use some subset of all possible versions.

    The proto-typical example of this scenario is when two systems developed independently with their rpc types defined in some shared library which has yet another independent rollout schedule. In this case one may roll out a new rpc version (V) in the shared library (L) and then the caller and callee systems can each upgrade to the new version of L supporting version V at their own pace, with version V only being exercised once both caller and callee have upgraded.

In each scenario, it is desirable that the party responsible for managing versions be coded largely in terms of a single "master" version of the types involved, with all necessary type conversions relegated to a single module. Versioned_rpc is intended for implementing such a module.

Type coercions into and out of the model go in the directions indicated by the following diagram:

       Caller converts                 Callee converts
       ===============                 ===============

           caller                        callee
           |       callee                |      callee
           |       |       caller        |      |       callee
           |       |       |             |      |       |
        ,-->-- Q1 --> R1 -->-.      Q1 -->-.    |    ,-->-- R1
       /                      \             \   |   /
      Q --->-- Q2 --> R2 -->-- R    Q2 -->-- Q --> R --->-- R2
       \                      /             /       \
        `-->-- Q3 --> R3 -->-'      Q3 -->-'         `-->-- R3
module Menu : sig .. end
type t
a directory of supported rpc names and versions.
val add : 's Rpc.Implementation.t list -> 's Rpc.Implementation.t list
add impls extends a list of rpc implementations with an additional rpc implementation for providing a Menu.t when one is requested via Menu.request.
val request : Rpc.Connection.t -> t Core.Std.Or_error.t Import.Deferred.t
request an rpc version menu from an rpc connection
val supported_rpcs : t -> Rpc.Implementation.Description.t list
find what rpcs are supported
val supported_versions : t -> rpc_name:string -> Core.Std.Int.Set.t
find what versions of a particular rpc are supported
module Connection_with_menu : sig .. end
type t
an rpc connection paired with the menu of rpcs one may call on it
val create : Rpc.Connection.t -> t Import.Deferred.Or_error.t
val connection : t -> Rpc.Connection.t
val menu : t -> Menu.t
module Caller_converts : sig .. end
module Rpc : sig .. end
module type S = sig .. end
type query
type response
val deprecated_dispatch_multi : version:int ->
Rpc.Connection.t -> query -> response Core.Std.Or_error.t Import.Deferred.t
val dispatch_multi : Connection_with_menu.t ->
query -> response Core.Std.Or_error.t Import.Deferred.t
multi-version dispatch
val versions : unit -> Core.Std.Int.Set.t
all versions supported by dispatch_multi. (useful for computing which old versions may be pruned)
module Make : 
functor (Model : sig .. end) -> sig .. end
Given a model of the types involved in a family of RPCs, this functor provides a single RPC versioned dispatch function dispatch_multi in terms of that model and a mechanism for registering the individual versions that dispatch_multi knows about. Registration requires knowing how to get into and out of the model.
           ,-->-- Q1 --> R1 -->-.
          /                      \
         Q --->-- Q2 --> R2 -->-- R
          \                      /
           `-->-- Q3 --> R3 -->-'
module Register : 
functor (Version_i : sig .. end) -> sig .. end
add a new version to the set of versions available via dispatch_multi.
val rpc : (Version_i.query, Version_i.response) Rpc.Rpc.t
include S with type response := Model.response and type query := Model.query
module Pipe_rpc : sig .. end
module type S = sig .. end
type query
type response
type error
multi-version dispatch

The return type varies slightly from Rpc.Pipe_rpc.dispatch to make it clear that conversion of each individual element in the returned pipe may fail.

val deprecated_dispatch_multi : version:int ->
Rpc.Connection.t ->
query ->
(response Core.Std.Or_error.t Import.Pipe.Reader.t * Rpc.Pipe_rpc.Id.t,
 error)
Core.Std.Result.t Core.Std.Or_error.t Import.Deferred.t
val dispatch_multi : Connection_with_menu.t ->
query ->
(response Core.Std.Or_error.t Import.Pipe.Reader.t * Rpc.Pipe_rpc.Id.t,
 error)
Core.Std.Result.t Core.Std.Or_error.t Import.Deferred.t
val versions : unit -> Core.Std.Int.Set.t
all versions supported by dispatch_multi. (useful for computing which old versions may be pruned)
module Make : 
functor (Model : sig .. end) -> sig .. end
Given a model of the types involved in a family of Pipe_RPCs, this functor provides a single Pipe_RPC versioned dispatch function dispatch_multi in terms of that model and a mechanism for registering the individual versions that dispatch_multi knows about. Registration requires knowing how to get into and out of the model.
          ,-->-- Q1 --> R1 -->-.    E1 -->-.
         /                      \           \
        Q --->-- Q2 --> R2 -->-- R  E2 -->-- E
         \                      /           /
          `-->-- Q3 --> R3 -->-'    E3 -->-'
module Register : 
functor (Version_i : sig .. end) -> sig .. end
add a new version to the set of versions available via dispatch_multi.
val rpc : (Version_i.query, Version_i.response, Version_i.error) Rpc.Pipe_rpc.t
include S with type error := Model.error and type response := Model.response and type query := Model.query
module Callee_converts : sig .. end
module Rpc : sig .. end
module type S = sig .. end
type query
type response
val implement_multi : ?log_not_previously_seen_version:(name:string -> int -> unit) ->
('state -> query -> response Import.Deferred.t) ->
'state Rpc.Implementation.t list
implement multiple versions at once
val versions : unit -> Core.Std.Int.Set.t
all versions implemented by implement_multi (useful for computing which old versions may be pruned)
module Make : 
functor (Model : sig .. end) -> sig .. end
Given a model of the types involved in a family of RPCs, this functor provides a single multi-version implementation function implement_multi in terms of that model and a mechanism for registering the individual versions that implement_multi knows about. Registration requires knowing how to get into and out of the model.
        Q1 -->-.         ,-->-- R1
                \       /
        Q2 -->-- Q --> R --->-- R2
                /       \
        Q3 -->-'         `-->-- R3
module Register : 
functor (Version_i : sig .. end) -> sig .. end
add a new version to the set of versions implemented by implement_multi.
val rpc : (Version_i.query, Version_i.response) Rpc.Rpc.t
include S with type response := Model.response and type query := Model.query
module Pipe_rpc : sig .. end
module type S = sig .. end
type query
type response
type error
val implement_multi : ?log_not_previously_seen_version:(name:string -> int -> unit) ->
('state ->
 query ->
 aborted:unit Import.Deferred.t ->
 (response Import.Pipe.Reader.t, error) Core.Std.Result.t Import.Deferred.t) ->
'state Rpc.Implementation.t list
implement multiple versions at once
val versions : unit -> Core.Std.Int.Set.t
all versions supported by dispatch_multi. (useful for computing which old versions may be pruned)
module Make : 
functor (Model : sig .. end) -> sig .. end
Given a model of the types involved in a family of Pipe_RPCs, this functor provides a single multi-version implementation function implement_multi in terms of that model and a mechanism for registering the individual versions that implement_multi knows about. Registration requires knowing how to get into and out of the model.
        Q1 -->-.         ,-->-- R1
                \       /
        Q2 -->-- Q --> R --->-- R2
                /       \
        Q3 -->-'         `-->-- R3
module Register : 
functor (Version_i : sig .. end) -> sig .. end
add a new version to the set of versions available via implement_multi.
val rpc : (Version_i.query, Version_i.response, Version_i.error) Rpc.Pipe_rpc.t
include S with type error := Model.error and type response := Model.response and type query := Model.query
module Both_convert : sig .. end
Both_convert rpcs combine features of both caller-converts and callee-converts versioning schemes in such a way that one can smoothly add a new version of the rpc to a shared library, and it doesn't matter whether the callee or caller upgrades to the latest version of the shared library first, the new version will not be exercised until both sides support it.
                      (conv)   (conv)                          (conv)   (conv)
                      caller   callee                          callee   caller
                      |        |                               |        |
                      |        |                               |        |
         Q.caller ---->-- Q1 -->-.             (impl)        .->-- R1 -->---- R.caller
                 \                \            callee       /                /
                  \--->-- Q2 -->---\           |           /--->-- R2 -->---/
                   \                \          |          /                /
                    `->-- Q3 -->---- Q.callee --> R.callee ---->-- R3 -->-'
module Plain : sig .. end
module type S = sig .. end
type caller_query
type caller_response
type callee_query
type callee_response
val dispatch_multi : Connection_with_menu.t ->
caller_query -> caller_response Core.Std.Or_error.t Import.Deferred.t
multi-version dispatch
val implement_multi : ?log_not_previously_seen_version:(name:string -> int -> unit) ->
('state -> callee_query -> callee_response Import.Deferred.t) ->
'state Rpc.Implementation.t list
implement multiple versions at once
val versions : unit -> Core.Std.Int.Set.t
all supported versions. useful for detecting old versions which may be pruned
module Make : 
functor (Model : sig .. end) -> sig .. end
module Register : 
functor (Version : sig .. end) -> sig .. end
include S with type callee_response := Model.Callee.response and type callee_query := Model.Callee.query and type caller_response := Model.Caller.response and type caller_query := Model.Caller.query