diff --git a/benchkit/benchmark/gorums.pb.go b/benchkit/benchmark/gorums.pb.go index 1b1e4f886..19e45afd8 100644 --- a/benchkit/benchmark/gorums.pb.go +++ b/benchkit/benchmark/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/benchkit/gorums.pb.go b/benchkit/gorums.pb.go index fbb69a0e3..c73059c98 100644 --- a/benchkit/gorums.pb.go +++ b/benchkit/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/call.go b/call.go index 608381208..d23183773 100644 --- a/call.go +++ b/call.go @@ -20,7 +20,7 @@ type Call[Req, Resp proto.Message] = impl.Call[Req, Resp] type OnewayCall[Req proto.Message] = impl.OnewayCall[Req] // OnewayAsync is the send-completion handle of a one-way call dispatched with -// [OnewayCall.Async]; [OnewayAsync.Wait] reports the send error. +// [OnewayCall.Async]; [OnewayAsync.Wait] returns the send error. type OnewayAsync = impl.OnewayAsync // CallContext provides an interceptor with the context and state of a call. @@ -59,8 +59,9 @@ type Correctable[Resp any] = impl.Correctable[Resp] type ResponseSeq[T proto.Message] = impl.ResponseSeq[T] // AggregateFunc aggregates the responses of a quorum call into a result of -// type Out, which may differ from the response type. [Responses.Aggregate] -// runs an AggregateFunc. +// type Out, which may differ from the response type. It receives the call's +// single-use response sequence, decides how to handle each node error, and +// decides when to stop. [Responses.Aggregate] runs an AggregateFunc. type AggregateFunc[Resp proto.Message, Out any] = impl.AggregateFunc[Resp, Out] // NodeResponse contains a node's response value or error. diff --git a/cmd/protoc-gen-gorums/dev/aliases.go b/cmd/protoc-gen-gorums/dev/aliases.go index 0fcab47e0..c1fbcb221 100644 --- a/cmd/protoc-gen-gorums/dev/aliases.go +++ b/cmd/protoc-gen-gorums/dev/aliases.go @@ -2,18 +2,8 @@ package dev import gorums "github.com/relab/gorums" -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/cmd/protoc-gen-gorums/gengorums/template_multicast.go b/cmd/protoc-gen-gorums/gengorums/template_multicast.go index c79b8236c..0dc760f10 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_multicast.go +++ b/cmd/protoc-gen-gorums/gengorums/template_multicast.go @@ -14,8 +14,9 @@ var multicastComment = ` {{if ne $comments ""}} {{$comments -}} {{else}} -// {{$method}} starts a one-way call on the client's configuration; no reply -// is returned. Use gorums.MapRequest to send a different message to each node. +// {{$method}} returns a one-way call handle for the client's configuration. +// No reply is returned. Use gorums.MapRequest to send a different message to +// each node. // // Send blocks until every send completes and returns any send errors. // Async returns a handle at once; Wait on the handle returns the send errors. diff --git a/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go b/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go index 850a9eb7d..dca214b2a 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go +++ b/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go @@ -17,9 +17,9 @@ var quorumCallComment = ` {{if ne $comments ""}} {{$comments -}} {{else}} -// {{$method}} executes a quorum call on the client's configuration. Use -// terminal methods like Majority(), First(), or Threshold(n) to retrieve the -// aggregated result. +// {{$method}} returns a quorum-call handle for the client's configuration. +// A terminal method such as Majority(), First(), or Threshold(n) dispatches +// the call and retrieves the aggregated result. // // Example: // @@ -59,9 +59,9 @@ var quorumCallStreamComment = ` {{if ne $comments ""}} {{$comments -}} {{else}} -// {{$method}} executes a streaming quorum call on the client's configuration. -// The server can send multiple responses, and the response iterator continues -// until the context is canceled. +// {{$method}} returns a streaming quorum-call handle for the client's +// configuration. The server can send multiple responses, and the response +// iterator continues until the context is canceled. // // Example: // diff --git a/cmd/protoc-gen-gorums/gengorums/template_static.go b/cmd/protoc-gen-gorums/gengorums/template_static.go index a57349325..d61a76ca9 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_static.go +++ b/cmd/protoc-gen-gorums/gengorums/template_static.go @@ -11,18 +11,8 @@ var pkgIdentMap = map[string]string{"github.com/relab/gorums": "Config"} // These identifiers cannot be used to define message types in a proto file. var reservedIdents = []string{"Config", "Correctable", "ID", "Node"} -var staticCode = `// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +var staticCode = `// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/cmd/protoc-gen-gorums/gengorums/template_unicast.go b/cmd/protoc-gen-gorums/gengorums/template_unicast.go index ea6ea816a..aa14109eb 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_unicast.go +++ b/cmd/protoc-gen-gorums/gengorums/template_unicast.go @@ -14,8 +14,8 @@ var unicastComment = ` {{if ne $comments ""}} {{$comments -}} {{else}} -// {{$method}} starts a one-way call on the client's node; no reply is -// returned. +// {{$method}} returns a one-way call handle for the client's node. +// No reply is returned. // // Send blocks until the send completes and returns any send error. // Async returns a handle at once; Wait on the handle returns the send error. diff --git a/config.go b/config.go index 31b126cdd..084d7714d 100644 --- a/config.go +++ b/config.go @@ -65,8 +65,10 @@ type Node = conn.Node // It is compatible with [slices.SortFunc] and [Config.Sort]. var ByID = conn.ByID -// ByLastError compares nodes by their LastErr status. -// Nodes with no error sort before nodes with an error. +// ByLastError compares nodes by their [Node.LastErr] status. +// Nodes with no error sort before nodes with an error. LastErr reverts to nil +// once traffic flows again, so this orders nodes by whether they are failing +// now. // It is compatible with [slices.SortFunc] and [Config.Sort]. var ByLastError = conn.ByLastError diff --git a/dial_options.go b/dial_options.go index 0d71e7a51..e30d7eb18 100644 --- a/dial_options.go +++ b/dial_options.go @@ -56,6 +56,7 @@ func WithBackoff(backoff backoff.Config) DialOption { // DefaultSendBufferSize is the per-node send queue capacity Gorums uses when // [WithDialBufferSizes] or [WithBufferSizes] is applied with a send size of 0. +// Each node allocates the full capacity up front. const DefaultSendBufferSize = conn.DefaultSendBufferSize // DefaultRecvBufferSize is the number of received requests per channel that diff --git a/doc.go b/doc.go index f7353148e..71b1ea6cc 100644 --- a/doc.go +++ b/doc.go @@ -18,14 +18,21 @@ // - multicast: a one-way call to every node in a configuration. // - unicast: a one-way call to a single node. // -// Calls are dispatched lazily: the request is sent on the first consuming -// operation, such as a terminal method, [Call.Async], or a range over the -// response sequence. +// A quorum call or one-way call returns a handle and sends nothing until +// the caller invokes a terminal method, [Call.Async], [Responses.Correctable], +// [OnewayCall.Send], or [OnewayCall.Async], or ranges over +// [Responses.Results]. A remote call sends at once. // Lazy dispatch is what lets a call site attach client interceptors before the // request is sent. [Call.Async] sends the request at once and leaves the // replies to a later terminal method, and [Responses.Correctable] reports // progressively better results. // +// A call that fails with an error returns it from the method that collects +// its result: a terminal method of a quorum call, or [OnewayCall.Send] of a +// one-way call. Async returns a handle that collects the result later: +// [Call.Async] returns [Responses], whose terminal methods return the error, +// and [OnewayCall.Async] returns [OnewayAsync], whose Wait returns it. +// // # Service clients // // For each service S, protoc-gen-gorums generates up to two client @@ -44,11 +51,12 @@ // // A node client holds only its node, so building one allocates nothing. // Build a node client where you need it, and do not keep it. The node may be -// any node, also one outside cfg. If the node is nil, every call returns -// [ErrNoNode]. A quorum call or multicast on an empty configuration sends -// nothing and returns [ErrEmptyConfig]. To call another configuration, build -// a new SClient from it. A client does not own its configuration or node; -// the close function returned by [NewConfig] closes the connections. +// outside cfg. If the node is nil, a remote call or unicast fails with +// [ErrNoNode]. On an empty configuration, a quorum call or multicast sends +// nothing and fails with [ErrEmptyConfig]. +// To call another configuration, build a new SClient from it. +// A client does not own its configuration or node; the close function returned +// by [NewConfig] closes the connections. // // A Go package may hold several services, in one proto file or in several. // Each service gets its own clients, server interface, and register function. @@ -59,8 +67,8 @@ // // # Configurations and nodes // -// A [Node] is a client-side handle to a remote (or co-located) peer, not the -// local process. A configuration is an immutable, ordered set of nodes built +// A [Node] is a client-side handle to a peer reached through a network or +// local transport. A configuration is an immutable, ordered set of nodes built // with [NewConfig] from a node source, e.g., [WithNodes] or [WithNodeList]. // Set operations such as [Config.Add], [Config.Remove], and [Config.Union] // derive new configurations that share the underlying node connections, so diff --git a/doc/dev-guide.md b/doc/dev-guide.md index fb4067fed..ebf28f80e 100644 --- a/doc/dev-guide.md +++ b/doc/dev-guide.md @@ -518,7 +518,7 @@ Each call constructor except `RemoteCall` builds a `CallContext` that holds the A unicast's `CallContext` holds a one-node `Config`. A quorum call returns a `Call`, which embeds `Responses` and its terminal methods (`First`, `Majority`, `All`, `Threshold`, `Aggregate`, `Reduce`, `Matching`), as well as the `Correctable` future. `Call.Async` sends the requests at once and returns the dispatched `Responses`, so any terminal method collects the replies later. -A one-way call (`Multicast`, `Unicast`) returns a `OnewayCall`, whose `Send` waits for the send confirmations and whose `Async` returns an `OnewayAsync`. +A one-way call (`Multicast`, `Unicast`) returns a `OnewayCall`, whose `Send` waits for local send confirmations and whose `Async` returns a `OnewayAsync`. A `ClientInterceptor` wraps the response iterator and may register per-node request transformations before the call is dispatched. `CallContext` reaches each node's send path through `conn.NodeTransport`. diff --git a/doc/user-guide.md b/doc/user-guide.md index 4488750db..08e6aca3c 100644 --- a/doc/user-guide.md +++ b/doc/user-guide.md @@ -211,7 +211,7 @@ protoc -I="$(go list -m -f {{.Dir}} github.com/relab/gorums):." \ The above step should produce three files named `storage.pb.go`, `storage_gorums.pb.go`, and `gorums.pb.go` in your package directory. The first contains the Protobuf definitions of our messages. The second contains the Gorums generated client and server interfaces. -The third declares the `Config`, `ID`, and `Node` type aliases of the Go package. +The third declares the `Config`, `ID`, `Node`, and `Correctable` type aliases of the Go package. ### Examining the Gorums Generated Code @@ -228,15 +228,19 @@ func NewStorageClient(config gorums.Config) StorageClient func NewStorageNodeClient(node *gorums.Node) StorageNodeClient ``` -The `StorageClient` methods send a request to every node in the configuration. +The `StorageClient` methods target every node in the configuration. A quorum call method returns a `*gorums.Call[*ReadRequest, *ReadResponse]` handle. The handle embeds `*gorums.Responses[*ReadResponse]`, which provides the terminal methods. The `Call` type also has an `Intercept` method, which registers client interceptors before the call is sent. The one-way `WriteMulticast` method returns a `*gorums.OnewayCall[*WriteRequest]` handle. `Send` blocks until every send completes and returns any send errors. -`Async` sends the request and returns a handle at once. +`Async` sends the request and returns a `*gorums.OnewayAsync` handle at once. `Wait` on that handle returns the send errors. +When this guide says that a call *fails with* an error, the method that collects the call's result returns that error. +For a quorum call, this is a terminal method, also on the `*gorums.Responses` that `Async` returns. +For a one-way call, this is `Send`, or `Wait` on the `*gorums.OnewayAsync` that `Async` returns. + ```go type StorageClient interface { ReadQC(ctx context.Context, in *ReadRequest) *gorums.Call[*ReadRequest, *ReadResponse] @@ -246,7 +250,7 @@ type StorageClient interface { } ``` -The `StorageNodeClient` methods send a request to the node of the client: +The `StorageNodeClient` methods target the client's node: ```go type StorageNodeClient interface { @@ -284,12 +288,13 @@ Follow these rules when you use the clients: Do not keep node clients in a struct or a map. * **A nil node does not panic.** `cfg.Node(id)` returns nil if the configuration has no node with that ID. - `NewStorageNodeClient(nil)` is legal, and every call on that client returns `gorums.ErrNoNode`. + `NewStorageNodeClient(nil)` is legal. + A remote call or unicast on that client fails with `gorums.ErrNoNode`. This error is a gRPC status error with the `InvalidArgument` code, because retrying cannot fix it. * **Any node works.** The node can also be a node that is not in a configuration that you use for quorum calls. * **An empty configuration does not panic.** - A quorum call or multicast on an empty configuration sends nothing and returns `gorums.ErrEmptyConfig`. + A quorum call or multicast on an empty configuration sends nothing and fails with `gorums.ErrEmptyConfig`. This error is a gRPC status error with the `InvalidArgument` code. `Results()` yields one response whose `Err` is `gorums.ErrEmptyConfig`, and `Correctable` completes at once with this error. * **Build a new client for a new configuration.** @@ -322,10 +327,8 @@ type StorageServer interface { } ``` -It is worth noting that unicast and multicast methods do not return a response or error, as they are one-way calls. -On the client side, `Send` returns an error if the request could not be sent. -`Async` returns a handle at once; `Wait` on that handle returns the same send errors. -Neither reports the success or failure of the request at the server side. +Unicast and multicast server methods return no response or error. +On the client side, a one-way call fails only if a request could not be sent; it does not return the result of the server handler. **Note:** You may decide to keep the `.proto` file and the generated `.pb.go` files in a separate directory/package and import that package (the generated Gorums API) into your application. @@ -831,9 +834,9 @@ Collecting the replies one call after the other takes about as long as the slowe The call is not dispatched until the caller calls `.Send()` or `.Async()` on the handle. If the caller drops the handle without calling either method, the call is never dispatched. -`.Send()` and the `.Wait()` of an `.Async()` handle report every send failure, so a one-way call cannot fail silently. +`.Send()` returns every send failure, and so does `.Wait()` on the `*OnewayAsync` that `.Async()` returns, so a one-way call cannot fail silently. Each send completes when the message reaches the stream of its node. -A one-way call has no reply, so there is nothing more to wait for. +Neither can return errors that occur later in the server handler, because a one-way call has no reply. A server handler that multicasts calls `.Async()` before `ServerContext.Release()` and `.Wait()` after it. The sends then keep the order of the requests. @@ -2306,7 +2309,7 @@ Nested calls follow the rule in [Request Ordering and ServerContext.Release](#re `ReadNestedQC` below calls `Release` first, because the nested reads of different requests may run in any order; the next request starts while the nested calls are in flight. `WriteNestedMulticast` dispatches its multicast before `Release`, so the peers receive the writes in the order the requests arrived. -A quorum call or a multicast on an empty configuration returns `gorums.ErrEmptyConfig`. +A quorum call or a multicast on an empty configuration sends nothing and fails with `gorums.ErrEmptyConfig`. The `len(config) == 0` checks below return a clearer error for a server that has no peers. ```go @@ -2553,7 +2556,7 @@ This is how a protocol can use a new tree for each view, or fall back to a star The root is simply the first node of the configuration, and it is a node like any other: it holds the data and runs the same handlers. A caller that is not in the configuration starts a tree call by sending to the root, with a one-node configuration, for example `gorums.Config{tree.Root()}`. The root's handler then relays to `tree.ChildrenOf(root.ID())` like every other internal node, so the root's own work is done too. -Do not start at `tree.ChildrenOf(tree.Root().ID())` from outside: that skips the root, and a call on an empty configuration, which is what `ChildrenOf` returns for a one-node tree, returns `gorums.ErrEmptyConfig`. +Do not start at `tree.ChildrenOf(tree.Root().ID())` from outside: that skips the root, and a call on an empty configuration, which is what `ChildrenOf` returns for a one-node tree, fails with `gorums.ErrEmptyConfig`. When the process that starts the call is itself the root, it does its local work and then sends to `tree.ChildrenOf(id)` with its own ID. ### Relaying in a Server diff --git a/errors.go b/errors.go index 298cbfbf4..80f466af1 100644 --- a/errors.go +++ b/errors.go @@ -27,22 +27,22 @@ var ErrStreamDown = stream.ErrStreamDown // is a gRPC status error with the Unavailable code; match it with [errors.Is]. var ErrNodeClosed = stream.ErrNodeClosed -// ErrNoNode is returned by a remote call or unicast on a nil node, such as -// the node that [Config.Node] returns for an ID outside the configuration. +// ErrNoNode is the error of a remote call or unicast on a nil node. +// [Config.Node] returns nil for an ID outside the configuration. // It is a gRPC status error with the InvalidArgument code; match it with // [errors.Is]. var ErrNoNode = impl.ErrNoNode -// ErrEmptyConfig is returned by a quorum call or multicast on an empty +// ErrEmptyConfig is the error of a quorum call or multicast on an empty // configuration, such as one that [Config.Remove] or [Config.WithoutErrors] -// emptied. It is a gRPC status error with the InvalidArgument code; match it -// with [errors.Is]. +// emptied. Such a call sends nothing. It is a gRPC status error with the +// InvalidArgument code; match it with [errors.Is]. var ErrEmptyConfig = impl.ErrEmptyConfig // ErrSendQueueFull is returned for a two-way call enqueued while the node's // send queue is full, so quorum logic counts the peer as failed; see -// [WithDialBufferSizes]. It is a gRPC status error with the Unavailable code; -// match it with [errors.Is]. +// [WithDialBufferSizes]. A one-way call waits for space instead. It is a gRPC +// status error with the Unavailable code; match it with [errors.Is]. var ErrSendQueueFull = stream.ErrSendQueueFull // ErrSendStalled is reported by [Node.LastErr] while a send to the node has diff --git a/examples/storage/proto/gorums.pb.go b/examples/storage/proto/gorums.pb.go index 106a7183d..0a1d7c871 100644 --- a/examples/storage/proto/gorums.pb.go +++ b/examples/storage/proto/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/conn/config.go b/internal/conn/config.go index 48f5b5ff1..dba904686 100644 --- a/internal/conn/config.go +++ b/internal/conn/config.go @@ -8,42 +8,10 @@ import ( "time" ) -// Config represents a static set of nodes on which multicast or -// quorum calls may be invoked. A configuration is created using [NewConfig]. -// A configuration should be treated as immutable. Therefore, methods that -// operate on a configuration always return a new Config instance. +// Config is exported and documented as gorums.Config. type Config []*Node -// NewConfig returns a new [Config] based on the provided nodes and dial -// options, and a function that closes the configuration's connection pool. -// -// The returned configuration and every configuration derived from it, for -// example with [Config.Extend], [Config.Remove], or [Config.Sort], share one -// connection pool. Only the returned close function closes that pool, and it -// closes every node in it, including nodes that [Config.Extend] added later. -// After the close function returns: -// - calls to the pool's nodes fail with an Unavailable "node closed" error; -// - [Config.Extend] on any configuration in the pool returns an error. -// -// One exception: a node that runs in-process, or that reuses a server's -// inbound stream, owns no connection, so calls to it still work. -// -// The close function is idempotent and safe for concurrent use; every call -// returns after the nodes are closed. It returns no error, because closing a -// connection has no failure that a caller can act on. -// -// On error, NewConfig returns a nil configuration and a nil close function. -// -// Example: -// -// cfg, closeFn, err := NewConfig( -// gorums.WithNodeList([]string{"localhost:8080", "localhost:8081", "localhost:8082"}), -// gorums.WithGRPCDialOptions(grpc.WithTransportCredentials(insecure.NewCredentials())), -// ) -// if err != nil { -// return err -// } -// defer closeFn() +// NewConfig is exported and documented as gorums.NewConfig. func NewConfig(nodes NodeSource, opts ...DialOption) (Config, func(), error) { if nodes == nil { return nil, nil, fmt.Errorf("gorums: missing required node list") diff --git a/internal/conn/dial_options.go b/internal/conn/dial_options.go index 14e11216e..46103370c 100644 --- a/internal/conn/dial_options.go +++ b/internal/conn/dial_options.go @@ -11,7 +11,7 @@ import ( "github.com/relab/gorums/internal/stream" ) -// DialOption sets an option on a configuration's [DialOptions]. +// DialOption is exported and documented as gorums.DialOption. type DialOption func(*DialOptions) // DialOptions holds the accumulated dial configuration for a [Config]'s @@ -45,12 +45,10 @@ func (o *DialOptions) borrowsStream(id ID) bool { return o.StreamDedup && o.InboundManager != nil && id < o.LocalNodeID } -// DefaultSendBufferSize is the per-node send queue capacity used when no -// explicit size is configured. Each node allocates the full capacity up front. +// DefaultSendBufferSize is exported and documented as gorums.DefaultSendBufferSize. const DefaultSendBufferSize = 4096 -// DefaultRecvBufferSize is the number of received requests per channel that can -// wait for the running handler when no explicit size is configured. +// DefaultRecvBufferSize is exported and documented as gorums.DefaultRecvBufferSize. const DefaultRecvBufferSize = 4096 // NewDialOptions returns a DialOptions initialized with default values. diff --git a/internal/conn/errors.go b/internal/conn/errors.go index 8a6ef48b0..ead0d44d7 100644 --- a/internal/conn/errors.go +++ b/internal/conn/errors.go @@ -6,13 +6,10 @@ import ( "strings" ) -// ErrStopped is returned by the server wait methods ([InboundManager.WaitForPeers], -// [InboundManager.WaitForClients]) when the server is stopped before the -// condition is met. +// ErrStopped is exported and documented as gorums.ErrStopped. var ErrStopped = errors.New("server stopped") -// QuorumCallError reports on a failed quorum call. -// It provides detailed information about which nodes failed. +// QuorumCallError is exported and documented as gorums.QuorumCallError. type QuorumCallError struct { cause error errors []NodeError diff --git a/internal/conn/node.go b/internal/conn/node.go index b2329b977..48d4edc73 100644 --- a/internal/conn/node.go +++ b/internal/conn/node.go @@ -29,14 +29,10 @@ func sharedNodeTransport(peer *Node) *stream.Transport { return stream.NewSharedTransport(transport) } -// ID identifies a node. ID 0 is reserved for a node that has no ID: a -// handler-only server, or a client that announces no ID. Dynamic client IDs -// start at [ClientIDStart] and skip configured IDs. Under stream -// deduplication, the peer with the lower ID dials. +// ID is exported and documented as gorums.ID. type ID = stream.ID -// Node encapsulates the state of a node on which a remote procedure call -// can be performed. +// Node is exported and documented as gorums.Node. type Node struct { // Only assigned at creation. id ID @@ -342,16 +338,12 @@ func (n *Node) Latency() time.Duration { return NodeTransport(n).Latency().Load() } -// ByID compares nodes by their identifier in increasing order. -// It is compatible with [slices.SortFunc] and [Config.Sort]. +// ByID is exported and documented as gorums.ByID. var ByID = func(a, b *Node) int { return cmp.Compare(a.id, b.id) } -// ByLastError compares nodes by their [Node.LastErr] status, sorting the nodes -// with no recorded error first. Since LastErr reverts to nil once traffic flows -// again, this orders nodes by whether they are currently failing. -// It is compatible with [slices.SortFunc] and [Config.Sort]. +// ByLastError is exported and documented as gorums.ByLastError. var ByLastError = func(a, b *Node) int { aErr := a.LastErr() bErr := b.LastErr() @@ -365,9 +357,7 @@ var ByLastError = func(a, b *Node) int { } } -// ByLatency compares nodes by their current latency estimate in ascending order. -// Nodes with no measurement yet (negative latency value) sort after nodes with a -// measurement. It is compatible with [slices.SortFunc] and [Config.Sort]. +// ByLatency is exported and documented as gorums.ByLatency. var ByLatency = func(a, b *Node) int { la, lb := a.Latency(), b.Latency() // Note: cmp.Compare alone would sort negative sentinel values first diff --git a/internal/conn/node_source.go b/internal/conn/node_source.go index 55c470551..ec0d5d557 100644 --- a/internal/conn/node_source.go +++ b/internal/conn/node_source.go @@ -7,9 +7,7 @@ import ( "slices" ) -// NodeSource identifies the set of nodes to build a [Config] from. Create one -// with [WithNodes] or [WithNodeList]; the interface is sealed so it can only -// be implemented within this package. +// NodeSource is exported and documented as gorums.NodeSource. type NodeSource interface { newConfig(nodeRegistry) (Config, error) // nodeMap returns the captured, explicitly numbered node records, or nil @@ -26,21 +24,17 @@ type nodeRegistry interface { newNode(id ID, addr string, verify PeerVerifier) (*Node, error) } -// NodeAddress must be implemented by types that can be used as node addresses. +// NodeAddress is exported and documented as gorums.NodeAddress. type NodeAddress interface { Addr() string } -// TLSIdentity supplies the expected URI SAN or DNS SAN of a peer's verified -// TLS certificate. A node source captures it alongside the node's address. +// TLSIdentity is exported and documented as gorums.TLSIdentity. type TLSIdentity interface { TLSIdentity() string } -// WithNodes returns a NodeSource containing the provided mapping from -// application-specific IDs to types implementing NodeAddress. -// Node IDs must be greater than 0. It captures each node's address and, when -// the node implements [TLSIdentity], its expected certificate identity. +// WithNodes is exported and documented as gorums.WithNodes. func WithNodes[T NodeAddress](nodes map[ID]T) NodeSource { nm := make(nodeMap, len(nodes)) for _, id := range slices.Sorted(maps.Keys(nodes)) { @@ -96,10 +90,7 @@ func (nm nodeMap) newConfig(registry nodeRegistry) (Config, error) { return builder.configuration(), nil } -// WithNodeList returns a NodeSource for the provided list of node addresses. -// Unique Node IDs are generated sequentially starting from the maximum existing -// node ID plus one, or from 1 if no nodes exist, preventing conflicts with -// existing nodes. +// WithNodeList is exported and documented as gorums.WithNodeList. func WithNodeList(addrsList []string) NodeSource { return nodeList(addrsList) } diff --git a/internal/impl/call.go b/internal/impl/call.go index c006a1f8f..8a0ab387c 100644 --- a/internal/impl/call.go +++ b/internal/impl/call.go @@ -10,19 +10,16 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -// Call represents a lazily dispatched quorum call. -// Register interceptors with [Call.Intercept] before the call is dispatched. -// A Call may be dispatched once. +// Call is exported and documented as gorums.Call. type Call[Req, Resp proto.Message] struct { *Responses[Resp] ctx *CallContext[Req, Resp] } -// ClientInterceptor transforms a call's request or response sequence. +// ClientInterceptor is exported and documented as gorums.ClientInterceptor. type ClientInterceptor[Req, Resp proto.Message] func(ctx *CallContext[Req, Resp], next ResponseSeq[Resp]) ResponseSeq[Resp] -// MapRequest returns an interceptor that transforms the request for each node. -// Returning nil or an invalid message skips the node with [ErrSkipNode]. +// MapRequest is exported and documented as gorums.MapRequest. func MapRequest[Req, Resp proto.Message](fn func(Req, *Node) Req) ClientInterceptor[Req, Resp] { return func(ctx *CallContext[Req, Resp], next ResponseSeq[Resp]) ResponseSeq[Resp] { if fn != nil { @@ -32,7 +29,7 @@ func MapRequest[Req, Resp proto.Message](fn func(Req, *Node) Req) ClientIntercep } } -// MapResponse returns an interceptor that transforms each successful response. +// MapResponse is exported and documented as gorums.MapResponse. func MapResponse[Req, Resp proto.Message](fn func(Resp, *Node) Resp) ClientInterceptor[Req, Resp] { return func(ctx *CallContext[Req, Resp], next ResponseSeq[Resp]) ResponseSeq[Resp] { if fn == nil { @@ -98,9 +95,7 @@ func (c *Call[Req, Resp]) Async() *Responses[Resp] { return c.Responses } -// OnewayCall represents a lazily dispatched multicast or unicast call. -// [OnewayCall.Send] and [OnewayCall.Async] each dispatch the call; invoking -// either after the call was dispatched panics. +// OnewayCall is exported and documented as gorums.OnewayCall. type OnewayCall[Req proto.Message] struct { ctx *CallContext[Req, *emptypb.Empty] unicast bool @@ -218,8 +213,7 @@ func (c *OnewayCall[Req]) receive() (NodeResponse[*stream.Message], error) { } } -// OnewayAsync is the send-completion handle of a one-way call dispatched with -// [OnewayCall.Async]. +// OnewayAsync is exported and documented as gorums.OnewayAsync. type OnewayAsync struct { collect func() error once sync.Once diff --git a/internal/impl/call_context.go b/internal/impl/call_context.go index a4ebfd3bb..ed77a9650 100644 --- a/internal/impl/call_context.go +++ b/internal/impl/call_context.go @@ -11,7 +11,7 @@ import ( "google.golang.org/protobuf/proto" ) -// CallContext provides an interceptor with the context and state of a call. +// CallContext is exported and documented as gorums.CallContext. type CallContext[Req, Resp proto.Message] struct { context.Context config Config diff --git a/internal/impl/correctable.go b/internal/impl/correctable.go index 9e06e72e7..601d7a670 100644 --- a/internal/impl/correctable.go +++ b/internal/impl/correctable.go @@ -7,8 +7,7 @@ import ( "github.com/relab/gorums/internal/conn" ) -// LevelNotSet is the zero value level used to indicate that no level -// (and thereby no reply) has been set for a correctable quorum call. +// LevelNotSet is exported and documented as gorums.LevelNotSet. const LevelNotSet = -1 type watcher struct { @@ -16,7 +15,7 @@ type watcher struct { ch chan struct{} } -// Correctable represents the progressive result of a correctable quorum call. +// Correctable is exported and documented as gorums.Correctable. type Correctable[Resp any] struct { mu sync.Mutex reply Resp diff --git a/internal/impl/errors.go b/internal/impl/errors.go index ec36ee817..a750878aa 100644 --- a/internal/impl/errors.go +++ b/internal/impl/errors.go @@ -7,28 +7,20 @@ import ( "google.golang.org/grpc/status" ) -// ErrIncomplete is the error returned by a quorum call when the call cannot be completed -// due to insufficient non-error replies to form a quorum according to the quorum function. +// ErrIncomplete is exported and documented as gorums.ErrIncomplete. var ErrIncomplete = errors.New("incomplete call") -// ErrSendFailure is the error returned by a multicast call when message sending fails for one or more nodes. +// ErrSendFailure is exported and documented as gorums.ErrSendFailure. var ErrSendFailure = errors.New("send failure") -// ErrSkipNode is returned when a node is skipped by request transformations. -// This allows the response iterator to account for all nodes without blocking. +// ErrSkipNode is exported and documented as gorums.ErrSkipNode. var ErrSkipNode = errors.New("skip node") -// ErrTypeMismatch is returned when a response cannot be cast to the expected type. +// ErrTypeMismatch is exported and documented as gorums.ErrTypeMismatch. var ErrTypeMismatch = errors.New("response type mismatch") -// ErrNoNode is returned by a remote call or unicast on a nil node, such as -// the node that [Config.Node] returns for an ID outside the configuration. -// It is a gRPC status error with the InvalidArgument code, because the call -// cannot succeed until the caller passes a node. +// ErrNoNode is exported and documented as gorums.ErrNoNode. var ErrNoNode = status.Error(codes.InvalidArgument, "no such node") -// ErrEmptyConfig is returned by a quorum call or multicast on an empty -// configuration, such as one that [Config.Remove] or [Config.WithoutErrors] -// emptied. It is a gRPC status error with the InvalidArgument code, because -// the call cannot succeed until the caller passes a node. +// ErrEmptyConfig is exported and documented as gorums.ErrEmptyConfig. var ErrEmptyConfig = status.Error(codes.InvalidArgument, "empty configuration") diff --git a/internal/impl/multicast.go b/internal/impl/multicast.go index b9d3add44..2268923f3 100644 --- a/internal/impl/multicast.go +++ b/internal/impl/multicast.go @@ -7,18 +7,7 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -// Multicast is a one-way call to every node in config; no replies are -// returned to the client. It returns an -// [OnewayCall] handle that dispatches the request only when it is consumed: -// [OnewayCall.Send] blocks until the send completes for every node and reports -// any send failures, while [OnewayCall.Async] dispatches without waiting and -// defers those failures to [OnewayAsync.Wait]. If config is empty, the handle -// sends nothing and reports [ErrEmptyConfig]. -// -// Register per-node request transforms with [OnewayCall.Intercept] and -// [MapRequest] before consuming the handle. -// -// This function should be used by generated code only. +// Multicast is exported and documented as gorumsimpl.Multicast. func Multicast[Req proto.Message](ctx context.Context, config Config, req Req, method string) *OnewayCall[Req] { callCtx := &CallContext[Req, *emptypb.Empty]{ Context: ctx, diff --git a/internal/impl/quorumcall.go b/internal/impl/quorumcall.go index f3f9c3b81..9e9c30958 100644 --- a/internal/impl/quorumcall.go +++ b/internal/impl/quorumcall.go @@ -6,35 +6,12 @@ import ( "google.golang.org/protobuf/proto" ) -// QuorumCall performs a quorum call on the nodes in config and returns a -// [Call] handle that provides access to node responses via terminal methods -// and fluent iteration. If config is empty, the call sends nothing and -// reports [ErrEmptyConfig]. -// -// Type parameters: -// - Req: The request message type -// - Resp: The response message type from individual nodes -// -// Register interceptors with [Call.Intercept] before invoking a terminal -// method. Messages are sent to nodes when a terminal method (like -// [Responses.Majority] or [Responses.First]) or an iterator method (like -// [Responses.Results]) is first called, applying any registered request -// transformations. -// -// This function should only be used by generated code. +// QuorumCall is exported and documented as gorumsimpl.QuorumCall. func QuorumCall[Req, Resp proto.Message](ctx context.Context, config Config, req Req, method string) *Call[Req, Resp] { return invokeQuorumCall[Req, Resp](ctx, config, req, method, false) } -// QuorumCallStream performs a streaming quorum call on the nodes in config and -// returns a [Call] handle. If config is empty, the call sends nothing and -// reports [ErrEmptyConfig]. -// This is used for correctable stream methods where the server sends multiple responses. -// -// In streaming mode, the response iterator continues indefinitely until the context -// is canceled, allowing the server to send multiple responses over time. -// -// This function should only be used by generated code. +// QuorumCallStream is exported and documented as gorumsimpl.QuorumCallStream. func QuorumCallStream[Req, Resp proto.Message](ctx context.Context, config Config, req Req, method string) *Call[Req, Resp] { return invokeQuorumCall[Req, Resp](ctx, config, req, method, true) } diff --git a/internal/impl/remotecall.go b/internal/impl/remotecall.go index 6c723d295..3189bc971 100644 --- a/internal/impl/remotecall.go +++ b/internal/impl/remotecall.go @@ -8,10 +8,7 @@ import ( "google.golang.org/protobuf/proto" ) -// RemoteCall executes a remote procedure call on node. It returns -// [ErrNoNode] if node is nil. -// -// This method should be used by generated code only. +// RemoteCall is exported and documented as gorumsimpl.RemoteCall. func RemoteCall[Req, Resp proto.Message](ctx context.Context, node *Node, req Req, method string) (Resp, error) { if node == nil { var zero Resp diff --git a/internal/impl/responses.go b/internal/impl/responses.go index b61449771..028d73987 100644 --- a/internal/impl/responses.go +++ b/internal/impl/responses.go @@ -38,7 +38,7 @@ func mapToCallResponse[Resp proto.Message](channelResp NodeResponse[*stream.Mess // Iterator Helpers // ------------------------------------------------------------------------- -// ResponseSeq yields the responses from a quorum call. +// ResponseSeq is exported and documented as gorums.ResponseSeq. type ResponseSeq[T proto.Message] iter.Seq[NodeResponse[T]] // IgnoreErrors returns a sequence containing only successful responses. @@ -58,7 +58,7 @@ func (seq ResponseSeq[Resp]) IgnoreErrors() ResponseSeq[Resp] { // Response Methods // ------------------------------------------------------------------------- -// Responses provides response iteration and aggregation for a quorum call. +// Responses is exported and documented as gorums.Responses. type Responses[Resp proto.Message] struct { seq ResponseSeq[Resp] size int diff --git a/internal/impl/terminals.go b/internal/impl/terminals.go index 75005ef35..cd5863056 100644 --- a/internal/impl/terminals.go +++ b/internal/impl/terminals.go @@ -47,10 +47,7 @@ func (t *nodeTally) incomplete() error { return conn.NewQuorumCallError(ErrIncomplete, t.errs) } -// AggregateFunc aggregates the responses of a quorum call into a result of -// type Out, which may differ from the response type. It receives the call's -// single-use response sequence, decides how to handle each node error, and -// decides when to stop. [Responses.Aggregate] runs an AggregateFunc. +// AggregateFunc is exported and documented as gorums.AggregateFunc. type AggregateFunc[Resp proto.Message, Out any] func(ResponseSeq[Resp]) (Out, error) // Aggregate applies fn to the single-use response sequence and returns its @@ -126,8 +123,7 @@ func (r *Responses[Resp]) Reduce[Acc any]( return acc, tally.incomplete() } -// Agreement contains a representative value and the successful node -// responses that established it. Responses are retained in arrival order. +// Agreement is exported and documented as gorums.Agreement. type Agreement[Resp proto.Message] struct { Value Resp Responses []NodeResponse[Resp] diff --git a/internal/impl/unicast.go b/internal/impl/unicast.go index b2a81285c..dd782b220 100644 --- a/internal/impl/unicast.go +++ b/internal/impl/unicast.go @@ -7,17 +7,7 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -// Unicast is a one-way call to node; no reply is returned to the client. It -// returns an [OnewayCall] handle that dispatches the request only when it is -// consumed: [OnewayCall.Send] blocks until the send completes and reports any -// send error, while [OnewayCall.Async] dispatches without waiting and defers -// that error to [OnewayAsync.Wait]. If node is nil, the handle sends nothing -// and reports [ErrNoNode]. -// -// Register request transforms with [OnewayCall.Intercept] and [MapRequest] -// before consuming the handle. -// -// This function should be used by generated code only. +// Unicast is exported and documented as gorumsimpl.Unicast. func Unicast[Req proto.Message](ctx context.Context, node *Node, req Req, method string) *OnewayCall[Req] { callCtx := &CallContext[Req, *emptypb.Empty]{ Context: ctx, diff --git a/internal/stream/errors.go b/internal/stream/errors.go index 2ed2d1717..cf3e2eacb 100644 --- a/internal/stream/errors.go +++ b/internal/stream/errors.go @@ -8,18 +8,13 @@ import ( ) var ( - // ErrNodeClosed is returned for requests enqueued after the node closed. + // ErrNodeClosed is exported and documented as gorums.ErrNodeClosed. ErrNodeClosed = status.Error(codes.Unavailable, "node closed") - // ErrStreamDown is returned for requests that cannot be delivered or - // retried because the node's stream is not available. + // ErrStreamDown is exported and documented as gorums.ErrStreamDown. ErrStreamDown = status.Error(codes.Unavailable, "stream is down") - // ErrSendQueueFull is returned for two-way requests enqueued while the - // node's send queue is full; one-way requests wait for space instead (see - // [Channel]). + // ErrSendQueueFull is exported and documented as gorums.ErrSendQueueFull. ErrSendQueueFull = status.Error(codes.Unavailable, "send queue full") - // ErrSendStalled is reported by [Channel.LastErr] while a send on the - // node's stream has been blocked for [stallReportDelay] or longer, as when - // the peer has stopped reading the stream. + // ErrSendStalled is exported and documented as gorums.ErrSendStalled. ErrSendStalled = status.Error(codes.Unavailable, "send stalled") ) diff --git a/internal/stream/request.go b/internal/stream/request.go index a98150100..889cfd0ec 100644 --- a/internal/stream/request.go +++ b/internal/stream/request.go @@ -55,7 +55,7 @@ func (r Request) sendErrorResponse(nodeID ID, err error) { // ID identifies a node. A channel with ID 0 belongs to a peer that has no ID. type ID = uint32 -// NodeResponse wraps a response value from node ID, and an error if any. +// NodeResponse is exported and documented as gorums.NodeResponse. type NodeResponse[T any] struct { NodeID ID Value T diff --git a/internal/testprotos/testdata/calltypes/importmsgs/gorums.pb.go.golden b/internal/testprotos/testdata/calltypes/importmsgs/gorums.pb.go.golden index 68b3404ad..e8da55702 100644 --- a/internal/testprotos/testdata/calltypes/importmsgs/gorums.pb.go.golden +++ b/internal/testprotos/testdata/calltypes/importmsgs/gorums.pb.go.golden @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/testprotos/testdata/calltypes/importmsgs/importmsgs_gorums.pb.go.golden b/internal/testprotos/testdata/calltypes/importmsgs/importmsgs_gorums.pb.go.golden index 916cee2d9..bdf85f93d 100644 --- a/internal/testprotos/testdata/calltypes/importmsgs/importmsgs_gorums.pb.go.golden +++ b/internal/testprotos/testdata/calltypes/importmsgs/importmsgs_gorums.pb.go.golden @@ -25,9 +25,9 @@ const ( // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type QuorumCallClient interface { - // QuorumCall executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // QuorumCall returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // diff --git a/internal/testprotos/testdata/calltypes/multicast/gorums.pb.go.golden b/internal/testprotos/testdata/calltypes/multicast/gorums.pb.go.golden index 153f085af..9bde0e93d 100644 --- a/internal/testprotos/testdata/calltypes/multicast/gorums.pb.go.golden +++ b/internal/testprotos/testdata/calltypes/multicast/gorums.pb.go.golden @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/testprotos/testdata/calltypes/multicast/multicast_gorums.pb.go.golden b/internal/testprotos/testdata/calltypes/multicast/multicast_gorums.pb.go.golden index 274d8671b..65e219f61 100644 --- a/internal/testprotos/testdata/calltypes/multicast/multicast_gorums.pb.go.golden +++ b/internal/testprotos/testdata/calltypes/multicast/multicast_gorums.pb.go.golden @@ -28,8 +28,9 @@ var _ emptypb.Empty // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type MulticastClient interface { - // Multicast starts a one-way call on the client's configuration; no reply - // is returned. Use gorums.MapRequest to send a different message to each node. + // Multicast returns a one-way call handle for the client's configuration. + // No reply is returned. Use gorums.MapRequest to send a different message to + // each node. // // Send blocks until every send completes and returns any send errors. // Async returns a handle at once; Wait on the handle returns the send errors. diff --git a/internal/testprotos/testdata/calltypes/multiservice/gorums.pb.go.golden b/internal/testprotos/testdata/calltypes/multiservice/gorums.pb.go.golden index 858cf3066..736f1e936 100644 --- a/internal/testprotos/testdata/calltypes/multiservice/gorums.pb.go.golden +++ b/internal/testprotos/testdata/calltypes/multiservice/gorums.pb.go.golden @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/testprotos/testdata/calltypes/multiservice/multiservice_gorums.pb.go.golden b/internal/testprotos/testdata/calltypes/multiservice/multiservice_gorums.pb.go.golden index 2cb5432c3..f3448a6c5 100644 --- a/internal/testprotos/testdata/calltypes/multiservice/multiservice_gorums.pb.go.golden +++ b/internal/testprotos/testdata/calltypes/multiservice/multiservice_gorums.pb.go.golden @@ -28,9 +28,9 @@ var _ emptypb.Empty // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type AlphaClient interface { - // Heartbeat executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // Heartbeat returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // @@ -76,17 +76,18 @@ type alphaNodeClient struct { // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type BetaClient interface { - // Heartbeat executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // Heartbeat returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // // resp, err := client.Heartbeat(ctx, in).Majority() Heartbeat(ctx context.Context, in *Request) *gorums.Call[*Request, *Response] - // Write starts a one-way call on the client's configuration; no reply - // is returned. Use gorums.MapRequest to send a different message to each node. + // Write returns a one-way call handle for the client's configuration. + // No reply is returned. Use gorums.MapRequest to send a different message to + // each node. // // Send blocks until every send completes and returns any send errors. // Async returns a handle at once; Wait on the handle returns the send errors. diff --git a/internal/testprotos/testdata/calltypes/quorumcall/gorums.pb.go.golden b/internal/testprotos/testdata/calltypes/quorumcall/gorums.pb.go.golden index 1951a6e78..a0b7aea14 100644 --- a/internal/testprotos/testdata/calltypes/quorumcall/gorums.pb.go.golden +++ b/internal/testprotos/testdata/calltypes/quorumcall/gorums.pb.go.golden @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/testprotos/testdata/calltypes/quorumcall/quorumcall_gorums.pb.go.golden b/internal/testprotos/testdata/calltypes/quorumcall/quorumcall_gorums.pb.go.golden index c7d109720..443df3129 100644 --- a/internal/testprotos/testdata/calltypes/quorumcall/quorumcall_gorums.pb.go.golden +++ b/internal/testprotos/testdata/calltypes/quorumcall/quorumcall_gorums.pb.go.golden @@ -24,9 +24,9 @@ const ( // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type QuorumCallClient interface { - // QuorumCall executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // QuorumCall returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // diff --git a/internal/testprotos/testdata/clientnames/gorums.pb.go.golden b/internal/testprotos/testdata/clientnames/gorums.pb.go.golden index 7403f5a52..ab76090bd 100644 --- a/internal/testprotos/testdata/clientnames/gorums.pb.go.golden +++ b/internal/testprotos/testdata/clientnames/gorums.pb.go.golden @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/testprotos/testdata/nodeonly/gorums.pb.go.golden b/internal/testprotos/testdata/nodeonly/gorums.pb.go.golden index 241edac57..ddf499a9e 100644 --- a/internal/testprotos/testdata/nodeonly/gorums.pb.go.golden +++ b/internal/testprotos/testdata/nodeonly/gorums.pb.go.golden @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/testprotos/testdata/nodeonly/nodeonly_gorums.pb.go.golden b/internal/testprotos/testdata/nodeonly/nodeonly_gorums.pb.go.golden index ff1c290c0..e063fb314 100644 --- a/internal/testprotos/testdata/nodeonly/nodeonly_gorums.pb.go.golden +++ b/internal/testprotos/testdata/nodeonly/nodeonly_gorums.pb.go.golden @@ -36,8 +36,8 @@ type NodeOnlyNodeClient interface { // resp, err := nodeClient.Read(ctx, in) Read(ctx context.Context, in *Request) (*Response, error) - // Write starts a one-way call on the client's node; no reply is - // returned. + // Write returns a one-way call handle for the client's node. + // No reply is returned. // // Send blocks until the send completes and returns any send error. // Async returns a handle at once; Wait on the handle returns the send error. diff --git a/internal/tests/correctable/correctable_gorums.pb.go b/internal/tests/correctable/correctable_gorums.pb.go index 5c3e9e09d..072923c17 100644 --- a/internal/tests/correctable/correctable_gorums.pb.go +++ b/internal/tests/correctable/correctable_gorums.pb.go @@ -24,18 +24,18 @@ const ( // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type CorrectableTestClient interface { - // Correctable executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // Correctable returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // // resp, err := client.Correctable(ctx, in).Majority() Correctable(ctx context.Context, in *Request) *gorums.Call[*Request, *Response] - // CorrectableStream executes a streaming quorum call on the client's configuration. - // The server can send multiple responses, and the response iterator continues - // until the context is canceled. + // CorrectableStream returns a streaming quorum-call handle for the client's + // configuration. The server can send multiple responses, and the response + // iterator continues until the context is canceled. // // Example: // diff --git a/internal/tests/correctable/gorums.pb.go b/internal/tests/correctable/gorums.pb.go index 8eb00c7b9..b743b49d9 100644 --- a/internal/tests/correctable/gorums.pb.go +++ b/internal/tests/correctable/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/tests/metadata/gorums.pb.go b/internal/tests/metadata/gorums.pb.go index c2e9c73f3..2e85c8824 100644 --- a/internal/tests/metadata/gorums.pb.go +++ b/internal/tests/metadata/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/tests/multiservice/gorums.pb.go b/internal/tests/multiservice/gorums.pb.go index 39df8b1b9..80bd34295 100644 --- a/internal/tests/multiservice/gorums.pb.go +++ b/internal/tests/multiservice/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/tests/multiservice/multiservice_gorums.pb.go b/internal/tests/multiservice/multiservice_gorums.pb.go index a65358a63..e5387662e 100644 --- a/internal/tests/multiservice/multiservice_gorums.pb.go +++ b/internal/tests/multiservice/multiservice_gorums.pb.go @@ -24,9 +24,9 @@ const ( // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type AlphaClient interface { - // Heartbeat executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // Heartbeat returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // @@ -72,18 +72,18 @@ type alphaNodeClient struct { // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type BetaClient interface { - // Heartbeat executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // Heartbeat returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // // resp, err := client.Heartbeat(ctx, in).Majority() Heartbeat(ctx context.Context, in *Request) *gorums.Call[*Request, *Response] - // Write executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // Write returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // diff --git a/internal/tests/oneway/gorums.pb.go b/internal/tests/oneway/gorums.pb.go index cf4fed667..83bbc7ee1 100644 --- a/internal/tests/oneway/gorums.pb.go +++ b/internal/tests/oneway/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/tests/oneway/oneway_gorums.pb.go b/internal/tests/oneway/oneway_gorums.pb.go index 3e3334afb..3bd2fa24d 100644 --- a/internal/tests/oneway/oneway_gorums.pb.go +++ b/internal/tests/oneway/oneway_gorums.pb.go @@ -28,8 +28,9 @@ var _ emptypb.Empty // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type OnewayTestClient interface { - // Multicast starts a one-way call on the client's configuration; no reply - // is returned. Use gorums.MapRequest to send a different message to each node. + // Multicast returns a one-way call handle for the client's configuration. + // No reply is returned. Use gorums.MapRequest to send a different message to + // each node. // // Send blocks until every send completes and returns any send errors. // Async returns a handle at once; Wait on the handle returns the send errors. @@ -59,8 +60,8 @@ type onewayTestClient struct { // unicasts on the client's node. If the node is nil, no message is sent and the // call fails with [gorums.ErrNoNode]. type OnewayTestNodeClient interface { - // Unicast starts a one-way call on the client's node; no reply is - // returned. + // Unicast returns a one-way call handle for the client's node. + // No reply is returned. // // Send blocks until the send completes and returns any send error. // Async returns a handle at once; Wait on the handle returns the send error. diff --git a/internal/tests/ordering/gorums.pb.go b/internal/tests/ordering/gorums.pb.go index aebd3ceef..23f326dd6 100644 --- a/internal/tests/ordering/gorums.pb.go +++ b/internal/tests/ordering/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/tests/ordering/ordering_gorums.pb.go b/internal/tests/ordering/ordering_gorums.pb.go index 084d236d6..3b4bd504b 100644 --- a/internal/tests/ordering/ordering_gorums.pb.go +++ b/internal/tests/ordering/ordering_gorums.pb.go @@ -24,9 +24,9 @@ const ( // multicasts on the client's configuration. If the configuration is empty, no // message is sent and the call fails with [gorums.ErrEmptyConfig]. type OrderingClient interface { - // QuorumCall executes a quorum call on the client's configuration. Use - // terminal methods like Majority(), First(), or Threshold(n) to retrieve the - // aggregated result. + // QuorumCall returns a quorum-call handle for the client's configuration. + // A terminal method such as Majority(), First(), or Threshold(n) dispatches + // the call and retrieves the aggregated result. // // Example: // diff --git a/internal/tests/tls/gorums.pb.go b/internal/tests/tls/gorums.pb.go index 9697e0b14..ef98cfd8b 100644 --- a/internal/tests/tls/gorums.pb.go +++ b/internal/tests/tls/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/internal/tests/unresponsive/gorums.pb.go b/internal/tests/unresponsive/gorums.pb.go index 0f05e1a54..bd3b5f849 100644 --- a/internal/tests/unresponsive/gorums.pb.go +++ b/internal/tests/unresponsive/gorums.pb.go @@ -9,18 +9,8 @@ import ( gorums "github.com/relab/gorums" ) -// The type aliases below are useful Gorums types that we make accessible -// from generated code. These names therefore become reserved identifiers, -// meaning that proto message types with these names would collide with the -// generated aliases and cause a compile error. -// -// The protoc-gen-gorums bundler is responsible for discovering these -// aliases and any other identifiers defined herein, and adding them to -// the reserved identifiers list. -// -// If necessary, additional aliases and other identifiers should be added in -// the generator's cmd/protoc-gen-gorums/dev directory, and the bundler will -// automatically discover them and add them to the reserved identifiers list. +// These aliases make common Gorums types available to application code. +// Their names are reserved in packages with generated Gorums code. type ( Config = gorums.Config diff --git a/message.go b/message.go index c9e2bc1d0..162616160 100644 --- a/message.go +++ b/message.go @@ -15,7 +15,7 @@ type Message struct { *stream.Message } -// MetadataEntry is a type alias for [stream.MetadataEntry]. +// MetadataEntry is a key-value pair in a message's metadata. type MetadataEntry = stream.MetadataEntry // newResponse returns the response envelope to the request in. It carries the diff --git a/runtime/gorumsimpl/call.go b/runtime/gorumsimpl/call.go index 63caf3be9..032924fe9 100644 --- a/runtime/gorumsimpl/call.go +++ b/runtime/gorumsimpl/call.go @@ -12,33 +12,34 @@ import ( ) // QuorumCall returns a quorum-call handle for the nodes in config. -// The call is dispatched when the handle is consumed. If config is empty, -// the handle sends nothing and reports [gorums.ErrEmptyConfig]. +// A terminal method or [gorums.Call.Async] dispatches the call. If config is +// empty, the call sends nothing and fails with [gorums.ErrEmptyConfig]. // It is used by generated code. func QuorumCall[Req, Resp proto.Message](ctx context.Context, config gorums.Config, req Req, method string) *gorums.Call[Req, Resp] { return impl.QuorumCall[Req, Resp](ctx, config, req, method) } // QuorumCallStream returns a streaming quorum-call handle for the nodes in -// config. The call is dispatched when the handle is consumed. If config is -// empty, the handle sends nothing and reports [gorums.ErrEmptyConfig]. +// config. It accepts multiple responses per node until ctx ends. A terminal +// method or [gorums.Call.Async] dispatches the call. If config is empty, the +// call sends nothing and fails with [gorums.ErrEmptyConfig]. // It is used by generated code. func QuorumCallStream[Req, Resp proto.Message](ctx context.Context, config gorums.Config, req Req, method string) *gorums.Call[Req, Resp] { return impl.QuorumCallStream[Req, Resp](ctx, config, req, method) } // Multicast returns a one-way call handle for every node in config. -// [gorums.OnewayCall.Send] or [gorums.OnewayCall.Async] dispatches the call; -// Send errors are reported by Send or [gorums.OnewayAsync.Wait]. If config is -// empty, the handle sends nothing and reports [gorums.ErrEmptyConfig]. +// [gorums.OnewayCall.Send] or [gorums.OnewayCall.Async] dispatches the call. +// If config is empty, the call sends nothing and fails with +// [gorums.ErrEmptyConfig]. // It is used by generated code. func Multicast[Req proto.Message](ctx context.Context, config gorums.Config, req Req, method string) *gorums.OnewayCall[Req] { return impl.Multicast(ctx, config, req, method) } -// Unicast executes a one-way call on node. No reply is returned. -// Send errors are reported by [gorums.OnewayCall.Send] or -// [gorums.OnewayAsync.Wait]. If node is nil, they report [gorums.ErrNoNode]. +// Unicast returns a one-way call handle for node. +// [gorums.OnewayCall.Send] or [gorums.OnewayCall.Async] dispatches the call. +// If node is nil, the call sends nothing and fails with [gorums.ErrNoNode]. // It is used by generated code. func Unicast[Req proto.Message](ctx context.Context, node *gorums.Node, req Req, method string) *gorums.OnewayCall[Req] { return impl.Unicast(ctx, node, req, method)