diff --git a/async.go b/async.go index 1f1328bb..10a9c071 100644 --- a/async.go +++ b/async.go @@ -44,21 +44,21 @@ type asyncCallState struct { // AsyncCall starts an asynchronous quorum call, returning an Async object that can be used to retrieve the results. // // This function should only be used by generated code. -func (c RawConfiguration) AsyncCall(ctx context.Context, d QuorumCallData) *Async { - expectedReplies := len(c) +func (c RawConfiguration[NODE, QSPEC]) AsyncCall(ctx context.Context, d QuorumCallData) *Async { + expectedReplies := len(c.nodes) md := &ordering.Metadata{MessageID: c.getMsgID(), Method: d.Method} replyChan := make(chan response, expectedReplies) - for _, n := range c { + for _, n := range c.nodes { msg := d.Message if d.PerNodeArgFn != nil { - msg = d.PerNodeArgFn(d.Message, n.id) + msg = d.PerNodeArgFn(d.Message, n.AsRaw().id) if !msg.ProtoReflect().IsValid() { expectedReplies-- continue // don't send if no msg } } - n.channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: msg}}, replyChan, false) + n.AsRaw().channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: msg}}, replyChan, false) } fut := &Async{c: make(chan struct{}, 1)} @@ -73,7 +73,7 @@ func (c RawConfiguration) AsyncCall(ctx context.Context, d QuorumCallData) *Asyn return fut } -func (c RawConfiguration) handleAsyncCall(ctx context.Context, fut *Async, state asyncCallState) { +func (c RawConfiguration[NODE, QSPEC]) handleAsyncCall(ctx context.Context, fut *Async, state asyncCallState) { defer close(fut.c) var ( diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index 753c0170..de708d78 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -37,7 +37,7 @@ type ( serverFunc func(context.Context, *TimedMsg) ) -func runQCBenchmark(opts Options, cfg *Configuration, f qcFunc) (*Result, error) { +func runQCBenchmark(opts Options, cfg Configuration, f qcFunc) (*Result, error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() msg := &Echo{Payload: make([]byte, opts.Payload)} @@ -103,7 +103,7 @@ func runQCBenchmark(opts Options, cfg *Configuration, f qcFunc) (*Result, error) return result, nil } -func runAsyncQCBenchmark(opts Options, cfg *Configuration, f asyncQCFunc) (*Result, error) { +func runAsyncQCBenchmark(opts Options, cfg Configuration, f asyncQCFunc) (*Result, error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() msg := &Echo{Payload: make([]byte, opts.Payload)} @@ -187,7 +187,7 @@ func runAsyncQCBenchmark(opts Options, cfg *Configuration, f asyncQCFunc) (*Resu return result, nil } -func runServerBenchmark(opts Options, cfg *Configuration, f serverFunc) (*Result, error) { +func runServerBenchmark(opts Options, cfg Configuration, f serverFunc) (*Result, error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() payload := make([]byte, opts.Payload) @@ -241,7 +241,7 @@ func runServerBenchmark(opts Options, cfg *Configuration, f serverFunc) (*Result } // GetBenchmarks returns a list of Benchmarks that can be performed on the configuration -func GetBenchmarks(cfg *Configuration) []Bench { +func GetBenchmarks(cfg Configuration) []Bench { m := []Bench{ { Name: "QuorumCall", @@ -270,7 +270,7 @@ func GetBenchmarks(cfg *Configuration) []Bench { } // RunBenchmarks runs all the benchmarks that match the given regex with the given options -func RunBenchmarks(benchRegex *regexp.Regexp, options Options, cfg *Configuration) ([]*Result, error) { +func RunBenchmarks(benchRegex *regexp.Regexp, options Options, cfg Configuration) ([]*Result, error) { benchmarks := GetBenchmarks(cfg) var results []*Result for _, b := range benchmarks { diff --git a/benchmark/benchmark.pb.go b/benchmark/benchmark.pb.go index 9effc0c1..18e67a7f 100644 --- a/benchmark/benchmark.pb.go +++ b/benchmark/benchmark.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: benchmark/benchmark.proto package benchmark diff --git a/benchmark/benchmark_gorums.pb.go b/benchmark/benchmark_gorums.pb.go index 25fcbbad..37618297 100644 --- a/benchmark/benchmark_gorums.pb.go +++ b/benchmark/benchmark_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: benchmark/benchmark.proto package benchmark @@ -24,10 +24,10 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -35,41 +35,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -100,31 +118,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node, QuorumSpec](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -144,6 +161,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // AsyncQuorumCall asynchronously invokes a quorum call on configuration c // and returns a AsyncEcho, which can be used to inspect the quorum call // reply and error when available. @@ -157,10 +178,10 @@ func (c *Configuration) AsyncQuorumCall(ctx context.Context, in *Echo) *AsyncEch for k, v := range replies { r[k] = v.(*Echo) } - return c.qspec.AsyncQuorumCallQF(req.(*Echo), r) + return c.AsRaw().QSpec().AsyncQuorumCallQF(req.(*Echo), r) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncEcho{fut} } @@ -175,7 +196,7 @@ func (c *Configuration) Multicast(ctx context.Context, in *TimedMsg, opts ...gor Method: "benchmark.Benchmark.Multicast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } // QuorumSpec is the interface of quorum functions for Benchmark. @@ -244,10 +265,10 @@ func (c *Configuration) StartServerBenchmark(ctx context.Context, in *StartReque for k, v := range replies { r[k] = v.(*StartResponse) } - return c.qspec.StartServerBenchmarkQF(req.(*StartRequest), r) + return c.AsRaw().QSpec().StartServerBenchmarkQF(req.(*StartRequest), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -266,10 +287,10 @@ func (c *Configuration) StopServerBenchmark(ctx context.Context, in *StopRequest for k, v := range replies { r[k] = v.(*Result) } - return c.qspec.StopServerBenchmarkQF(req.(*StopRequest), r) + return c.AsRaw().QSpec().StopServerBenchmarkQF(req.(*StopRequest), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -288,10 +309,10 @@ func (c *Configuration) StartBenchmark(ctx context.Context, in *StartRequest) (r for k, v := range replies { r[k] = v.(*StartResponse) } - return c.qspec.StartBenchmarkQF(req.(*StartRequest), r) + return c.AsRaw().QSpec().StartBenchmarkQF(req.(*StartRequest), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -310,10 +331,10 @@ func (c *Configuration) StopBenchmark(ctx context.Context, in *StopRequest) (res for k, v := range replies { r[k] = v.(*MemoryStat) } - return c.qspec.StopBenchmarkQF(req.(*StopRequest), r) + return c.AsRaw().QSpec().StopBenchmarkQF(req.(*StopRequest), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -331,10 +352,10 @@ func (c *Configuration) QuorumCall(ctx context.Context, in *Echo) (resp *Echo, e for k, v := range replies { r[k] = v.(*Echo) } - return c.qspec.QuorumCallQF(req.(*Echo), r) + return c.AsRaw().QSpec().QuorumCallQF(req.(*Echo), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -353,10 +374,10 @@ func (c *Configuration) SlowServer(ctx context.Context, in *Echo) (resp *Echo, e for k, v := range replies { r[k] = v.(*Echo) } - return c.qspec.SlowServerQF(req.(*Echo), r) + return c.AsRaw().QSpec().SlowServerQF(req.(*Echo), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } diff --git a/cmd/benchmark/main.go b/cmd/benchmark/main.go index dbe1e3d1..59880bfe 100644 --- a/cmd/benchmark/main.go +++ b/cmd/benchmark/main.go @@ -58,7 +58,7 @@ func (f *listFlag) Get() []string { func listBenchmarks() { tw := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0) - benchmarks := benchmark.GetBenchmarks(nil) + benchmarks := benchmark.GetBenchmarks(benchmark.Configuration{}) for _, b := range benchmarks { fmt.Fprintf(tw, "%s:\t%s\n", b.Name, b.Description) } diff --git a/cmd/protoc-gen-gorums/dev/config.go b/cmd/protoc-gen-gorums/dev/config.go index a698e5fa..74dbc562 100644 --- a/cmd/protoc-gen-gorums/dev/config.go +++ b/cmd/protoc-gen-gorums/dev/config.go @@ -6,10 +6,11 @@ import ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -17,39 +18,57 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() +} + +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } diff --git a/cmd/protoc-gen-gorums/dev/mgr.go b/cmd/protoc-gen-gorums/dev/mgr.go index 4fec657d..6c13201b 100644 --- a/cmd/protoc-gen-gorums/dev/mgr.go +++ b/cmd/protoc-gen-gorums/dev/mgr.go @@ -35,31 +35,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. diff --git a/cmd/protoc-gen-gorums/dev/node.go b/cmd/protoc-gen-gorums/dev/node.go index e5acc464..d5d3a9a3 100644 --- a/cmd/protoc-gen-gorums/dev/node.go +++ b/cmd/protoc-gen-gorums/dev/node.go @@ -7,3 +7,7 @@ import "github.com/relab/gorums" type Node struct { *gorums.RawNode } + +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} diff --git a/cmd/protoc-gen-gorums/dev/zorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums.pb.go index 5452abdb..20de9cf5 100644 --- a/cmd/protoc-gen-gorums/dev/zorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev diff --git a/cmd/protoc-gen-gorums/dev/zorums_async_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_async_gorums.pb.go index 0d524f20..42cdc8db 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_async_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_async_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev @@ -31,10 +31,10 @@ func (c *Configuration) QuorumCallAsync(ctx context.Context, in *Request) *Async for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallAsyncQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallAsyncQF(req.(*Request), r) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncResponse{fut} } @@ -49,13 +49,13 @@ func (c *Configuration) QuorumCallAsyncPerNodeArg(ctx context.Context, in *Reque for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallAsyncPerNodeArgQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallAsyncPerNodeArgQF(req.(*Request), r) } cd.PerNodeArgFn = func(req protoreflect.ProtoMessage, nid uint32) protoreflect.ProtoMessage { return f(req.(*Request), nid) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncResponse{fut} } @@ -70,10 +70,10 @@ func (c *Configuration) QuorumCallAsyncCustomReturnType(ctx context.Context, in for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallAsyncCustomReturnTypeQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallAsyncCustomReturnTypeQF(req.(*Request), r) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncMyResponse{fut} } @@ -88,13 +88,13 @@ func (c *Configuration) QuorumCallAsyncCombo(ctx context.Context, in *Request, f for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallAsyncComboQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallAsyncComboQF(req.(*Request), r) } cd.PerNodeArgFn = func(req protoreflect.ProtoMessage, nid uint32) protoreflect.ProtoMessage { return f(req.(*Request), nid) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncMyResponse{fut} } @@ -109,10 +109,10 @@ func (c *Configuration) QuorumCallAsync2(ctx context.Context, in *Request) *Asyn for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallAsync2QF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallAsync2QF(req.(*Request), r) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncResponse{fut} } @@ -127,10 +127,10 @@ func (c *Configuration) QuorumCallAsyncEmpty(ctx context.Context, in *Request) * for k, v := range replies { r[k] = v.(*emptypb.Empty) } - return c.qspec.QuorumCallAsyncEmptyQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallAsyncEmptyQF(req.(*Request), r) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncEmpty{fut} } @@ -146,9 +146,9 @@ func (c *Configuration) QuorumCallAsyncEmpty2(ctx context.Context, in *emptypb.E for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallAsyncEmpty2QF(req.(*emptypb.Empty), r) + return c.AsRaw().QSpec().QuorumCallAsyncEmpty2QF(req.(*emptypb.Empty), r) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncResponse{fut} } diff --git a/cmd/protoc-gen-gorums/dev/zorums_correctable_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_correctable_gorums.pb.go index 121ed119..52edf845 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_correctable_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_correctable_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev @@ -32,10 +32,10 @@ func (c *Configuration) Correctable(ctx context.Context, in *Request) *Correctab for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableQF(req.(*Request), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableResponse{corr} } @@ -51,13 +51,13 @@ func (c *Configuration) CorrectablePerNodeArg(ctx context.Context, in *Request, for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectablePerNodeArgQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectablePerNodeArgQF(req.(*Request), r) } cd.PerNodeArgFn = func(req protoreflect.ProtoMessage, nid uint32) protoreflect.ProtoMessage { return f(req.(*Request), nid) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableResponse{corr} } @@ -73,10 +73,10 @@ func (c *Configuration) CorrectableCustomReturnType(ctx context.Context, in *Req for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableCustomReturnTypeQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableCustomReturnTypeQF(req.(*Request), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableMyResponse{corr} } @@ -92,13 +92,13 @@ func (c *Configuration) CorrectableCombo(ctx context.Context, in *Request, f fun for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableComboQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableComboQF(req.(*Request), r) } cd.PerNodeArgFn = func(req protoreflect.ProtoMessage, nid uint32) protoreflect.ProtoMessage { return f(req.(*Request), nid) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableMyResponse{corr} } @@ -114,10 +114,10 @@ func (c *Configuration) CorrectableEmpty(ctx context.Context, in *Request) *Corr for k, v := range replies { r[k] = v.(*emptypb.Empty) } - return c.qspec.CorrectableEmptyQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableEmptyQF(req.(*Request), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableEmpty{corr} } @@ -134,10 +134,10 @@ func (c *Configuration) CorrectableEmpty2(ctx context.Context, in *emptypb.Empty for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableEmpty2QF(req.(*emptypb.Empty), r) + return c.AsRaw().QSpec().CorrectableEmpty2QF(req.(*emptypb.Empty), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableResponse{corr} } @@ -153,10 +153,10 @@ func (c *Configuration) CorrectableStream(ctx context.Context, in *Request) *Cor for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableStreamQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableStreamQF(req.(*Request), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableStreamResponse{corr} } @@ -172,13 +172,13 @@ func (c *Configuration) CorrectableStreamPerNodeArg(ctx context.Context, in *Req for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableStreamPerNodeArgQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableStreamPerNodeArgQF(req.(*Request), r) } cd.PerNodeArgFn = func(req protoreflect.ProtoMessage, nid uint32) protoreflect.ProtoMessage { return f(req.(*Request), nid) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableStreamResponse{corr} } @@ -194,10 +194,10 @@ func (c *Configuration) CorrectableStreamCustomReturnType(ctx context.Context, i for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableStreamCustomReturnTypeQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableStreamCustomReturnTypeQF(req.(*Request), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableStreamMyResponse{corr} } @@ -213,13 +213,13 @@ func (c *Configuration) CorrectableStreamCombo(ctx context.Context, in *Request, for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableStreamComboQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableStreamComboQF(req.(*Request), r) } cd.PerNodeArgFn = func(req protoreflect.ProtoMessage, nid uint32) protoreflect.ProtoMessage { return f(req.(*Request), nid) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableStreamMyResponse{corr} } @@ -235,10 +235,10 @@ func (c *Configuration) CorrectableStreamEmpty(ctx context.Context, in *Request) for k, v := range replies { r[k] = v.(*emptypb.Empty) } - return c.qspec.CorrectableStreamEmptyQF(req.(*Request), r) + return c.AsRaw().QSpec().CorrectableStreamEmptyQF(req.(*Request), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableStreamEmpty{corr} } @@ -255,9 +255,9 @@ func (c *Configuration) CorrectableStreamEmpty2(ctx context.Context, in *emptypb for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.CorrectableStreamEmpty2QF(req.(*emptypb.Empty), r) + return c.AsRaw().QSpec().CorrectableStreamEmpty2QF(req.(*emptypb.Empty), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableStreamResponse{corr} } diff --git a/cmd/protoc-gen-gorums/dev/zorums_multicast_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_multicast_gorums.pb.go index c22416f7..df8d220c 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_multicast_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_multicast_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev @@ -27,7 +27,7 @@ func (c *Configuration) Multicast(ctx context.Context, in *Request, opts ...goru Method: "dev.ZorumsService.Multicast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } // MulticastPerNodeArg with per_node_arg option. @@ -41,7 +41,7 @@ func (c *Configuration) MulticastPerNodeArg(ctx context.Context, in *Request, f return f(req.(*Request), nid) } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } // Multicast2 is testing whether multiple streams work. @@ -51,7 +51,7 @@ func (c *Configuration) Multicast2(ctx context.Context, in *Request, opts ...gor Method: "dev.ZorumsService.Multicast2", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } // Reference imports to suppress errors if they are not otherwise used. @@ -64,7 +64,7 @@ func (c *Configuration) Multicast3(ctx context.Context, in *Request, opts ...gor Method: "dev.ZorumsService.Multicast3", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } // Reference imports to suppress errors if they are not otherwise used. @@ -77,5 +77,5 @@ func (c *Configuration) Multicast4(ctx context.Context, in *emptypb.Empty, opts Method: "dev.ZorumsService.Multicast4", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } diff --git a/cmd/protoc-gen-gorums/dev/zorums_qspec_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_qspec_gorums.pb.go index 170acb45..48777367 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_qspec_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_qspec_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev diff --git a/cmd/protoc-gen-gorums/dev/zorums_quorumcall_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_quorumcall_gorums.pb.go index 7752cf86..10a5a4ae 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_quorumcall_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_quorumcall_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev @@ -31,10 +31,10 @@ func (c *Configuration) QuorumCall(ctx context.Context, in *Request) (resp *Resp for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallQF(req.(*Request), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -52,13 +52,13 @@ func (c *Configuration) QuorumCallPerNodeArg(ctx context.Context, in *Request, f for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallPerNodeArgQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallPerNodeArgQF(req.(*Request), r) } cd.PerNodeArgFn = func(req protoreflect.ProtoMessage, nid uint32) protoreflect.ProtoMessage { return f(req.(*Request), nid) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -76,10 +76,10 @@ func (c *Configuration) QuorumCallCustomReturnType(ctx context.Context, in *Requ for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallCustomReturnTypeQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallCustomReturnTypeQF(req.(*Request), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -97,13 +97,13 @@ func (c *Configuration) QuorumCallCombo(ctx context.Context, in *Request, f func for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallComboQF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallComboQF(req.(*Request), r) } cd.PerNodeArgFn = func(req protoreflect.ProtoMessage, nid uint32) protoreflect.ProtoMessage { return f(req.(*Request), nid) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -121,10 +121,10 @@ func (c *Configuration) QuorumCallEmpty(ctx context.Context, in *emptypb.Empty) for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QuorumCallEmptyQF(req.(*emptypb.Empty), r) + return c.AsRaw().QSpec().QuorumCallEmptyQF(req.(*emptypb.Empty), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -142,10 +142,10 @@ func (c *Configuration) QuorumCallEmpty2(ctx context.Context, in *Request) (resp for k, v := range replies { r[k] = v.(*emptypb.Empty) } - return c.qspec.QuorumCallEmpty2QF(req.(*Request), r) + return c.AsRaw().QSpec().QuorumCallEmpty2QF(req.(*Request), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } diff --git a/cmd/protoc-gen-gorums/dev/zorums_rpc_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_rpc_gorums.pb.go index 7f3c63c2..cf669b92 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_rpc_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_rpc_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev diff --git a/cmd/protoc-gen-gorums/dev/zorums_server_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_server_gorums.pb.go index c0b4713b..9dd556df 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_server_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_server_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev diff --git a/cmd/protoc-gen-gorums/dev/zorums_types_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_types_gorums.pb.go index 9d09c891..8fb1b08b 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_types_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_types_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev diff --git a/cmd/protoc-gen-gorums/dev/zorums_unicast_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_unicast_gorums.pb.go index 5ed86b6e..66020726 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_unicast_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_unicast_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: zorums.proto package dev diff --git a/cmd/protoc-gen-gorums/gengorums/template_async.go b/cmd/protoc-gen-gorums/gengorums/template_async.go index fa6e842d..b42bff4a 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_async.go +++ b/cmd/protoc-gen-gorums/gengorums/template_async.go @@ -39,7 +39,7 @@ var asyncBody = ` cd := {{$callData}}{ for k, v := range replies { r[k] = v.(*{{$out}}) } - return c.qspec.{{$method}}QF(req.(*{{$in}}), r) + return c.AsRaw().QSpec().{{$method}}QF(req.(*{{$in}}), r) } {{- if hasPerNodeArg .Method}} cd.PerNodeArgFn = func(req {{$protoMessage}}, nid uint32) {{$protoMessage}} { @@ -47,7 +47,7 @@ var asyncBody = ` cd := {{$callData}}{ } {{- end}} - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &{{$asyncOut}}{fut} } ` diff --git a/cmd/protoc-gen-gorums/gengorums/template_correctable.go b/cmd/protoc-gen-gorums/gengorums/template_correctable.go index d480de1e..fac65908 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_correctable.go +++ b/cmd/protoc-gen-gorums/gengorums/template_correctable.go @@ -48,7 +48,7 @@ var correctableBody = ` cd := {{$callData}}{ for k, v := range replies { r[k] = v.(*{{$out}}) } - return c.qspec.{{$method}}QF(req.(*{{$in}}), r) + return c.AsRaw().QSpec().{{$method}}QF(req.(*{{$in}}), r) } {{- if hasPerNodeArg .Method}} cd.PerNodeArgFn = func(req {{$protoMessage}}, nid uint32) {{$protoMessage}} { @@ -56,7 +56,7 @@ var correctableBody = ` cd := {{$callData}}{ } {{- end}} - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &{{$correctableOut}}{corr} } ` diff --git a/cmd/protoc-gen-gorums/gengorums/template_multicast.go b/cmd/protoc-gen-gorums/gengorums/template_multicast.go index 40b40c19..2c2ef241 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_multicast.go +++ b/cmd/protoc-gen-gorums/gengorums/template_multicast.go @@ -32,7 +32,7 @@ var multicastBody = ` cd := {{$callData}}{ } {{- end}} - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } ` diff --git a/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go b/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go index 7fd40858..f4569d38 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go +++ b/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go @@ -53,7 +53,7 @@ var quorumCallBody = ` cd := {{$callData}}{ for k, v := range replies { r[k] = v.(*{{$out}}) } - return c.qspec.{{$method}}QF(req.(*{{$in}}), r) + return c.AsRaw().QSpec().{{$method}}QF(req.(*{{$in}}), r) } {{- if hasPerNodeArg .Method}} cd.PerNodeArgFn = func(req {{$protoMessage}}, nid uint32) {{$protoMessage}} { @@ -61,7 +61,7 @@ var quorumCallBody = ` cd := {{$callData}}{ } {{- end}} - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } diff --git a/cmd/protoc-gen-gorums/gengorums/template_static.go b/cmd/protoc-gen-gorums/gengorums/template_static.go index d5b4999c..08d58599 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_static.go +++ b/cmd/protoc-gen-gorums/gengorums/template_static.go @@ -5,18 +5,19 @@ package gengorums // pkgIdentMap maps from package name to one of the package's identifiers. // These identifiers are used by the Gorums protoc plugin to generate import statements. -var pkgIdentMap = map[string]string{"fmt": "Errorf", "github.com/relab/gorums": "ConfigOption", "google.golang.org/grpc/encoding": "GetCodec"} +var pkgIdentMap = map[string]string{"fmt": "Errorf", "github.com/relab/gorums": "ConfigCreationError", "google.golang.org/grpc/encoding": "GetCodec"} // reservedIdents holds the set of Gorums reserved identifiers. // These identifiers cannot be used to define message types in a proto file. -var reservedIdents = []string{"Configuration", "Manager", "Node", "QuorumSpec"} +var reservedIdents = []string{"Configuration", "ConfigurationFromRaw", "Manager", "NODE", "Node", "QSPEC", "QuorumSpec"} var staticCode = `// A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -24,41 +25,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() +} + +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -89,31 +108,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -133,4 +151,8 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + ` diff --git a/config.go b/config.go index 15a569ef..3fdb4213 100644 --- a/config.go +++ b/config.go @@ -10,24 +10,39 @@ import ( // // This type is intended to be used by generated code. // You should use the generated `Configuration` type instead. -type RawConfiguration []*RawNode +type RawConfiguration[NODE RawNodeConstraint, QSPEC any] struct { + nodes []NODE + qspec QSPEC +} // NewRawConfiguration returns a configuration based on the provided list of nodes. // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func NewRawConfiguration(mgr *RawManager, opt NodeListOption) (nodes RawConfiguration, err error) { +func NewRawConfiguration[NODE RawNodeConstraint, QSPEC any](mgr *RawManager, qspec QSPEC, opt NodeListOption) (cfg RawConfiguration[NODE, QSPEC], err error) { if opt == nil { - return nil, ConfigCreationError(fmt.Errorf("missing required node list")) + return cfg, ConfigCreationError(fmt.Errorf("missing required node list")) + } + rawNodes, err := opt.newConfig(mgr) + if err != nil { + return cfg, ConfigCreationError(err) + } + genNodes := make([]NODE, 0, len(rawNodes)) + for _, n := range rawNodes { + genNodes = append(genNodes, NODE{n}) } - return opt.newConfig(mgr) + return NewRawConfigurationFromNodeSlice(genNodes, qspec), nil +} + +func NewRawConfigurationFromNodeSlice[NODE RawNodeConstraint, QSPEC any](nodes []NODE, qspec QSPEC) RawConfiguration[NODE, QSPEC] { + return RawConfiguration[NODE, QSPEC]{nodes, qspec} } -// NodeIDs returns a slice of this configuration's Node IDs. -func (c RawConfiguration) NodeIDs() []uint32 { - ids := make([]uint32, len(c)) - for i, node := range c { - ids[i] = node.ID() +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c RawConfiguration[NODE, QSPEC]) NodeIDs() []uint32 { + ids := make([]uint32, len(c.nodes)) + for i, node := range c.nodes { + ids[i] = node.AsRaw().ID() } return ids } @@ -35,28 +50,42 @@ func (c RawConfiguration) NodeIDs() []uint32 { // Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c RawConfiguration) Nodes() []*RawNode { - return c +func (c RawConfiguration[NODES, QSPEC]) Nodes() []NODES { + return c.nodes +} + +// QSpec returns the quorum specification object. +func (c RawConfiguration[NODES, QSPEC]) QSpec() QSPEC { + return c.qspec } // Size returns the number of nodes in this configuration. -func (c RawConfiguration) Size() int { - return len(c) +func (c RawConfiguration[NODES, QSPEC]) Size() int { + return len(c.nodes) } // Equal returns true if configurations b and c have the same set of nodes. -func (c RawConfiguration) Equal(b RawConfiguration) bool { - if len(c) != len(b) { +func (c RawConfiguration[NODES, QSPEC]) Equal(b RawConfiguration[NODES, QSPEC]) bool { + if len(c.nodes) != len(b.nodes) { return false } - for i := range c { - if c[i].ID() != b[i].ID() { + for i := range c.nodes { + if c.nodes[i].AsRaw().ID() != b.nodes[i].AsRaw().ID() { return false } } return true } -func (c RawConfiguration) getMsgID() uint64 { - return c[0].mgr.getMsgID() +// shortcut to the manager through one of the nodes +func (c RawConfiguration[NODES, QSPEC]) getMsgID() uint64 { + return c.nodes[0].AsRaw().mgr.getMsgID() +} + +func (c RawConfiguration[NODES, QSPEC]) rawNodes() (nodes []*RawNode) { + nodes = make([]*RawNode, 0, len(c.nodes)) + for _, n := range c.nodes { + nodes = append(nodes, n.AsRaw()) + } + return nodes } diff --git a/config_opts.go b/config_opts.go index 155e586a..63603f96 100644 --- a/config_opts.go +++ b/config_opts.go @@ -8,18 +8,18 @@ type ConfigOption interface{} // NodeListOption must be implemented by node providers. type NodeListOption interface { ConfigOption - newConfig(*RawManager) (RawConfiguration, error) + newConfig(*RawManager) ([]*RawNode, error) } type nodeIDMap struct { idMap map[string]uint32 } -func (o nodeIDMap) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { +func (o nodeIDMap) newConfig(mgr *RawManager) (nodes []*RawNode, err error) { if len(o.idMap) == 0 { return nil, ConfigCreationError(fmt.Errorf("node-to-ID map required: WithNodeMap")) } - nodes = make(RawConfiguration, 0, len(o.idMap)) + nodes = make([]*RawNode, 0, len(o.idMap)) for naddr, id := range o.idMap { node, found := mgr.Node(id) if !found { @@ -35,7 +35,6 @@ func (o nodeIDMap) newConfig(mgr *RawManager) (nodes RawConfiguration, err error nodes = append(nodes, node) } // Sort nodes to ensure deterministic iteration. - OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) return nodes, nil } @@ -50,11 +49,11 @@ type nodeList struct { addrsList []string } -func (o nodeList) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { +func (o nodeList) newConfig(mgr *RawManager) (nodes []*RawNode, err error) { if len(o.addrsList) == 0 { return nil, ConfigCreationError(fmt.Errorf("node addresses required: WithNodeList")) } - nodes = make(RawConfiguration, 0, len(o.addrsList)) + nodes = make([]*RawNode, 0, len(o.addrsList)) for _, naddr := range o.addrsList { node, err := NewRawNode(naddr) if err != nil { @@ -71,7 +70,6 @@ func (o nodeList) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) nodes = append(nodes, node) } // Sort nodes to ensure deterministic iteration. - OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) return nodes, nil } @@ -86,11 +84,11 @@ type nodeIDs struct { nodeIDs []uint32 } -func (o nodeIDs) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { +func (o nodeIDs) newConfig(mgr *RawManager) (nodes []*RawNode, err error) { if len(o.nodeIDs) == 0 { return nil, ConfigCreationError(fmt.Errorf("node IDs required: WithNodeIDs")) } - nodes = make(RawConfiguration, 0, len(o.nodeIDs)) + nodes = make([]*RawNode, 0, len(o.nodeIDs)) for _, id := range o.nodeIDs { node, found := mgr.Node(id) if !found { @@ -100,7 +98,6 @@ func (o nodeIDs) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) nodes = append(nodes, node) } // Sort nodes to ensure deterministic iteration. - OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) return nodes, nil } @@ -111,12 +108,12 @@ func WithNodeIDs(ids []uint32) NodeListOption { return &nodeIDs{nodeIDs: ids} } -type addNodes struct { - old RawConfiguration +type addNodes[NODE RawNodeConstraint] struct { + old []*RawNode new NodeListOption } -func (o addNodes) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { +func (o addNodes[NODE]) newConfig(mgr *RawManager) (nodes []*RawNode, err error) { newNodes, err := o.new.newConfig(mgr) if err != nil { return nil, err @@ -127,17 +124,17 @@ func (o addNodes) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) // WithNewNodes returns a NodeListOption that can be used to create a new configuration // combining c and the new nodes. -func (c RawConfiguration) WithNewNodes(new NodeListOption) NodeListOption { - return &addNodes{old: c, new: new} +func (c RawConfiguration[NODE, QSPEC]) WithNewNodes(new NodeListOption) NodeListOption { + return &addNodes[NODE]{old: c.rawNodes(), new: new} } type addConfig struct { - old RawConfiguration - add RawConfiguration + old []*RawNode + add []*RawNode } -func (o addConfig) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { - nodes = make(RawConfiguration, 0, len(o.old)+len(o.add)) +func (o addConfig) newConfig(mgr *RawManager) (nodes []*RawNode, err error) { + nodes = make([]*RawNode, 0, len(o.old)+len(o.add)) m := make(map[uint32]bool) for _, n := range append(o.old, o.add...) { if !m[n.id] { @@ -146,27 +143,26 @@ func (o addConfig) newConfig(mgr *RawManager) (nodes RawConfiguration, err error } } // Sort nodes to ensure deterministic iteration. - OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) return nodes, err } // And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c RawConfiguration) And(d RawConfiguration) NodeListOption { - return &addConfig{old: c, add: d} +func (c RawConfiguration[NODE, QSPEC]) And(d RawConfiguration[NODE, QSPEC]) NodeListOption { + return &addConfig{old: c.rawNodes(), add: d.rawNodes()} } // WithoutNodes returns a NodeListOption that can be used to create a new configuration // from c without the given node IDs. -func (c RawConfiguration) WithoutNodes(ids ...uint32) NodeListOption { +func (c RawConfiguration[NODE, QSPEC]) WithoutNodes(ids ...uint32) NodeListOption { rmIDs := make(map[uint32]bool) for _, id := range ids { rmIDs[id] = true } - keepIDs := make([]uint32, 0, len(c)) - for _, cNode := range c { - if !rmIDs[cNode.id] { - keepIDs = append(keepIDs, cNode.id) + keepIDs := make([]uint32, 0, len(c.nodes)) + for _, cNode := range c.nodes { + if !rmIDs[cNode.AsRaw().id] { + keepIDs = append(keepIDs, cNode.AsRaw().id) } } return &nodeIDs{nodeIDs: keepIDs} @@ -174,15 +170,15 @@ func (c RawConfiguration) WithoutNodes(ids ...uint32) NodeListOption { // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. -func (c RawConfiguration) Except(rm RawConfiguration) NodeListOption { +func (c RawConfiguration[NODE, QSPEC]) Except(rm RawConfiguration[NODE, QSPEC]) NodeListOption { rmIDs := make(map[uint32]bool) - for _, rmNode := range rm { - rmIDs[rmNode.id] = true + for _, rmNode := range rm.nodes { + rmIDs[rmNode.AsRaw().id] = true } - keepIDs := make([]uint32, 0, len(c)) - for _, cNode := range c { - if !rmIDs[cNode.id] { - keepIDs = append(keepIDs, cNode.id) + keepIDs := make([]uint32, 0, len(c.nodes)) + for _, cNode := range c.nodes { + if !rmIDs[cNode.AsRaw().id] { + keepIDs = append(keepIDs, cNode.AsRaw().id) } } return &nodeIDs{nodeIDs: keepIDs} diff --git a/config_test.go b/config_test.go index dd1cff3a..1022d2e5 100644 --- a/config_test.go +++ b/config_test.go @@ -9,7 +9,7 @@ import ( func TestNewConfigurationNodeList(t *testing.T) { mgr := gorums.NewRawManager(gorums.WithNoConnect()) - cfg, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(nodes)) + cfg, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } @@ -17,7 +17,7 @@ func TestNewConfigurationNodeList(t *testing.T) { t.Errorf("cfg.Size() = %d, expected %d", cfg.Size(), len(nodes)) } - contains := func(nodes []*gorums.RawNode, addr string) bool { + contains := func(nodes []dummyNode, addr string) bool { for _, node := range nodes { if addr == node.Address() { return true @@ -45,7 +45,7 @@ func TestNewConfigurationNodeList(t *testing.T) { func TestNewConfigurationNodeMap(t *testing.T) { mgr := gorums.NewRawManager(gorums.WithNoConnect()) - cfg, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeMap(nodeMap)) + cfg, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeMap(nodeMap)) if err != nil { t.Fatal(err) } @@ -69,7 +69,7 @@ func TestNewConfigurationNodeMap(t *testing.T) { func TestNewConfigurationNodeIDs(t *testing.T) { mgr := gorums.NewRawManager(gorums.WithNoConnect()) - c1, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(nodes)) + c1, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } @@ -79,7 +79,7 @@ func TestNewConfigurationNodeIDs(t *testing.T) { // Identical configurations c1 == c2 nodeIDs := c1.NodeIDs() - c2, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeIDs(nodeIDs)) + c2, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeIDs(nodeIDs)) if err != nil { t.Fatal(err) } @@ -91,7 +91,7 @@ func TestNewConfigurationNodeIDs(t *testing.T) { } // Configuration with one less node |c3| == |c1| - 1 - c3, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeIDs(nodeIDs[:len(nodeIDs)-1])) + c3, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeIDs(nodeIDs[:len(nodeIDs)-1])) if err != nil { t.Fatal(err) } @@ -105,20 +105,21 @@ func TestNewConfigurationNodeIDs(t *testing.T) { func TestNewConfigurationAnd(t *testing.T) { mgr := gorums.NewRawManager(gorums.WithNoConnect()) - c1, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(nodes)) + c1, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } c2Nodes := []string{"127.0.0.1:8080"} - c2, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(c2Nodes)) + c2, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeList(c2Nodes)) if err != nil { t.Fatal(err) } // Add newNodes to c1, giving a new c3 with a total of 3+2 nodes newNodes := []string{"127.0.0.1:9083", "127.0.0.1:9084"} - c3, err := gorums.NewRawConfiguration( + c3, err := gorums.NewRawConfiguration[dummyNode, any]( mgr, + nil, c1.WithNewNodes(gorums.WithNodeList(newNodes)), ) if err != nil { @@ -129,8 +130,9 @@ func TestNewConfigurationAnd(t *testing.T) { } // Combine c2 to c1, giving a new c4 with a total of 3+1 nodes - c4, err := gorums.NewRawConfiguration( + c4, err := gorums.NewRawConfiguration[dummyNode, any]( mgr, + nil, c1.And(c2), ) if err != nil { @@ -143,8 +145,9 @@ func TestNewConfigurationAnd(t *testing.T) { // Combine c2 to c4, giving a new c5 with a total of 4 nodes // c4 already contains all nodes from c2 (see above): c4 = c1+c2 // c5 should essentially just be a copy of c4 (ignoring duplicates from c2) - c5, err := gorums.NewRawConfiguration( + c5, err := gorums.NewRawConfiguration[dummyNode, any]( mgr, + nil, c4.And(c2), ) if err != nil { @@ -157,13 +160,14 @@ func TestNewConfigurationAnd(t *testing.T) { func TestNewConfigurationExcept(t *testing.T) { mgr := gorums.NewRawManager(gorums.WithNoConnect()) - c1, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(nodes)) + c1, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } - c2, err := gorums.NewRawConfiguration( + c2, err := gorums.NewRawConfiguration[dummyNode, any]( mgr, - c1.WithoutNodes(c1[0].ID()), + nil, + c1.WithoutNodes(c1.Nodes()[0].ID()), ) if err != nil { t.Fatal(err) @@ -173,15 +177,17 @@ func TestNewConfigurationExcept(t *testing.T) { } newNodes := []string{"127.0.0.1:9083", "127.0.0.1:9084"} - c3, err := gorums.NewRawConfiguration( + c3, err := gorums.NewRawConfiguration[dummyNode, any]( mgr, + nil, c1.WithNewNodes(gorums.WithNodeList(newNodes)), ) if err != nil { t.Fatal(err) } - c4, err := gorums.NewRawConfiguration( + c4, err := gorums.NewRawConfiguration[dummyNode, any]( mgr, + nil, c3.Except(c1), ) if err != nil { diff --git a/correctable.go b/correctable.go index 787b9a9d..dcd23674 100644 --- a/correctable.go +++ b/correctable.go @@ -100,21 +100,21 @@ type correctableCallState struct { // CorrectableCall starts a new correctable quorum call and returns a new Correctable object. // // This method should only be used by generated code. -func (c RawConfiguration) CorrectableCall(ctx context.Context, d CorrectableCallData) *Correctable { - expectedReplies := len(c) +func (c RawConfiguration[NODE, QSPEC]) CorrectableCall(ctx context.Context, d CorrectableCallData) *Correctable { + expectedReplies := len(c.nodes) md := &ordering.Metadata{MessageID: c.getMsgID(), Method: d.Method} replyChan := make(chan response, expectedReplies) - for _, n := range c { + for _, n := range c.nodes { msg := d.Message if d.PerNodeArgFn != nil { - msg = d.PerNodeArgFn(d.Message, n.id) + msg = d.PerNodeArgFn(d.Message, n.AsRaw().id) if !msg.ProtoReflect().IsValid() { expectedReplies-- continue // don't send if no msg } } - n.channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: msg}}, replyChan, d.ServerStream) + n.AsRaw().channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: msg}}, replyChan, d.ServerStream) } corr := &Correctable{donech: make(chan struct{}, 1)} @@ -129,7 +129,7 @@ func (c RawConfiguration) CorrectableCall(ctx context.Context, d CorrectableCall return corr } -func (c RawConfiguration) handleCorrectableCall(ctx context.Context, corr *Correctable, state correctableCallState) { +func (c RawConfiguration[NODE, QSPEC]) handleCorrectableCall(ctx context.Context, corr *Correctable, state correctableCallState) { var ( resp protoreflect.ProtoMessage errs []Error @@ -140,8 +140,8 @@ func (c RawConfiguration) handleCorrectableCall(ctx context.Context, corr *Corre ) if state.data.ServerStream { - for _, n := range c { - defer n.channel.deleteRouter(state.md.MessageID) + for _, n := range c.nodes { + defer n.AsRaw().channel.deleteRouter(state.md.MessageID) } } diff --git a/examples/go.mod b/examples/go.mod index c8205b17..68b0000b 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -1,6 +1,6 @@ module github.com/relab/gorums/examples -go 1.14 +go 1.18 require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 @@ -10,4 +10,17 @@ require ( google.golang.org/protobuf v1.27.1 ) +require ( + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.6 // indirect + golang.org/x/mod v0.5.1 // indirect + golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.9-0.20220111140354-d7a4bb4f6a59 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 // indirect +) + replace github.com/relab/gorums => ../ diff --git a/examples/go.sum b/examples/go.sum index 11d79fc6..97eec459 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -58,10 +58,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -75,10 +73,8 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -87,17 +83,13 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -106,7 +98,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -114,11 +105,8 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.1.9-0.20220111140354-d7a4bb4f6a59 h1:3Gop58KiFHMvw+ydt4ieSEbxGhqEP5t2KKouwWnEA+Q= +golang.org/x/tools v0.1.9-0.20220111140354-d7a4bb4f6a59/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/examples/storage/proto/storage.pb.go b/examples/storage/proto/storage.pb.go index 2bed060f..4ed525d1 100644 --- a/examples/storage/proto/storage.pb.go +++ b/examples/storage/proto/storage.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: storage/proto/storage.proto package proto diff --git a/examples/storage/proto/storage_gorums.pb.go b/examples/storage/proto/storage_gorums.pb.go index 1ed07100..edf62868 100644 --- a/examples/storage/proto/storage_gorums.pb.go +++ b/examples/storage/proto/storage_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: storage/proto/storage.proto package proto @@ -24,10 +24,10 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -35,41 +35,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -100,31 +118,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node, QuorumSpec](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -144,6 +161,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // Reference imports to suppress errors if they are not otherwise used. var _ emptypb.Empty @@ -155,7 +176,7 @@ func (c *Configuration) WriteMulticast(ctx context.Context, in *WriteRequest, op Method: "storage.Storage.WriteMulticast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } // QuorumSpec is the interface of quorum functions for Storage. @@ -189,10 +210,10 @@ func (c *Configuration) ReadQC(ctx context.Context, in *ReadRequest) (resp *Read for k, v := range replies { r[k] = v.(*ReadResponse) } - return c.qspec.ReadQCQF(req.(*ReadRequest), r) + return c.AsRaw().QSpec().ReadQCQF(req.(*ReadRequest), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -211,10 +232,10 @@ func (c *Configuration) WriteQC(ctx context.Context, in *WriteRequest) (resp *Wr for k, v := range replies { r[k] = v.(*WriteResponse) } - return c.qspec.WriteQCQF(req.(*WriteRequest), r) + return c.AsRaw().QSpec().WriteQCQF(req.(*WriteRequest), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } diff --git a/examples/storage/repl.go b/examples/storage/repl.go index 05015ca1..a291e198 100644 --- a/examples/storage/repl.go +++ b/examples/storage/repl.go @@ -53,11 +53,11 @@ The command performs the write quorum call on node 0 and 2 type repl struct { mgr *proto.Manager - cfg *proto.Configuration + cfg proto.Configuration term *term.Terminal } -func newRepl(mgr *proto.Manager, cfg *proto.Configuration) *repl { +func newRepl(mgr *proto.Manager, cfg proto.Configuration) *repl { return &repl{ mgr: mgr, cfg: cfg, @@ -90,7 +90,7 @@ func (r repl) ReadLine() (string, error) { // Repl runs an interactive Read-eval-print loop, that allows users to run commands that perform // RPCs and quorum calls using the manager and configuration. -func Repl(mgr *proto.Manager, defaultCfg *proto.Configuration) { +func Repl(mgr *proto.Manager, defaultCfg proto.Configuration) { r := newRepl(mgr, defaultCfg) fmt.Println(help) @@ -198,8 +198,8 @@ func (r repl) qcCfg(args []string) { fmt.Println("'cfg' requires a configuration and an operation.") return } - cfg := r.parseConfiguration(args[0]) - if cfg == nil { + cfg, ok := r.parseConfiguration(args[0]) + if !ok { return } switch args[1] { @@ -210,7 +210,7 @@ func (r repl) qcCfg(args []string) { } } -func (repl) readRPC(args []string, node *proto.Node) { +func (repl) readRPC(args []string, node proto.Node) { if len(args) < 1 { fmt.Println("Read requires a key to read.") return @@ -229,7 +229,7 @@ func (repl) readRPC(args []string, node *proto.Node) { fmt.Printf("%s = %s\n", args[0], resp.GetValue()) } -func (repl) writeRPC(args []string, node *proto.Node) { +func (repl) writeRPC(args []string, node proto.Node) { if len(args) < 2 { fmt.Println("Write requires a key and a value to write.") return @@ -248,7 +248,7 @@ func (repl) writeRPC(args []string, node *proto.Node) { fmt.Println("Write OK") } -func (repl) readQC(args []string, cfg *proto.Configuration) { +func (repl) readQC(args []string, cfg proto.Configuration) { if len(args) < 1 { fmt.Println("Read requires a key to read.") return @@ -267,7 +267,7 @@ func (repl) readQC(args []string, cfg *proto.Configuration) { fmt.Printf("%s = %s\n", args[0], resp.GetValue()) } -func (repl) writeQC(args []string, cfg *proto.Configuration) { +func (repl) writeQC(args []string, cfg proto.Configuration) { if len(args) < 2 { fmt.Println("Write requires a key and a value to write.") return @@ -286,7 +286,7 @@ func (repl) writeQC(args []string, cfg *proto.Configuration) { fmt.Println("Write OK") } -func (r repl) parseConfiguration(cfgStr string) (cfg *proto.Configuration) { +func (r repl) parseConfiguration(cfgStr string) (cfg proto.Configuration, ok bool) { // configuration using range syntax if i := strings.Index(cfgStr, ":"); i > -1 { var start, stop int @@ -298,7 +298,7 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *proto.Configuration) { start, err = strconv.Atoi(cfgStr[:i]) if err != nil { fmt.Printf("Failed to parse configuration: %v\n", err) - return nil + return cfg, false } } if i == len(cfgStr)-1 { @@ -307,12 +307,12 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *proto.Configuration) { stop, err = strconv.Atoi(cfgStr[i+1:]) if err != nil { fmt.Printf("Failed to parse configuration: %v\n", err) - return nil + return cfg, false } } if start >= stop || start < 0 || stop >= numNodes { fmt.Println("Invalid configuration.") - return nil + return cfg, false } nodes := make([]string, 0) for _, node := range r.mgr.Nodes()[start:stop] { @@ -321,9 +321,9 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *proto.Configuration) { cfg, err = r.mgr.NewConfiguration(&qspec{cfgSize: stop - start}, gorums.WithNodeList(nodes)) if err != nil { fmt.Printf("Failed to create configuration: %v\n", err) - return nil + return cfg, false } - return cfg + return cfg, true } // configuration using list of indices if indices := strings.Split(cfgStr, ","); len(indices) > 0 { @@ -333,21 +333,21 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *proto.Configuration) { i, err := strconv.Atoi(index) if err != nil { fmt.Printf("Failed to parse configuration: %v\n", err) - return nil + return cfg, false } if i < 0 || i >= len(nodes) { fmt.Println("Invalid configuration.") - return nil + return cfg, false } selectedNodes = append(selectedNodes, nodes[i].Address()) } cfg, err := r.mgr.NewConfiguration(&qspec{cfgSize: len(selectedNodes)}, gorums.WithNodeList(selectedNodes)) if err != nil { fmt.Printf("Failed to create configuration: %v\n", err) - return nil + return cfg, false } - return cfg + return cfg, true } fmt.Println("Invalid configuration.") - return nil + return cfg, false } diff --git a/go.mod b/go.mod index 78174c53..5e399c87 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,22 @@ module github.com/relab/gorums -go 1.16 +go 1.18 require ( github.com/google/go-cmp v0.5.6 - golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect - golang.org/x/tools v0.1.8 + golang.org/x/tools v0.1.9-0.20220111140354-d7a4bb4f6a59 google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb google.golang.org/grpc v1.43.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 google.golang.org/protobuf v1.27.1 ) + +require ( + github.com/golang/protobuf v1.5.2 // indirect + golang.org/x/mod v0.5.1 // indirect + golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect +) diff --git a/go.sum b/go.sum index fb55c3f6..fc22ac65 100644 --- a/go.sum +++ b/go.sum @@ -56,10 +56,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -73,10 +71,8 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -93,16 +89,13 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -110,11 +103,10 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.1.9-0.20220111140354-d7a4bb4f6a59 h1:3Gop58KiFHMvw+ydt4ieSEbxGhqEP5t2KKouwWnEA+Q= +golang.org/x/tools v0.1.9-0.20220111140354-d7a4bb4f6a59/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gorums.pb.go b/gorums.pb.go index 78befb0a..534190b1 100644 --- a/gorums.pb.go +++ b/gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: gorums.proto package gorums diff --git a/internal/correctable/opts.pb.go b/internal/correctable/opts.pb.go index eef54eae..726aeb52 100644 --- a/internal/correctable/opts.pb.go +++ b/internal/correctable/opts.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: internal/correctable/opts.proto package correctable diff --git a/mgr_test.go b/mgr_test.go index df24616c..6658907d 100644 --- a/mgr_test.go +++ b/mgr_test.go @@ -17,6 +17,14 @@ var ( nodeMap = map[string]uint32{"127.0.0.1:9080": 1, "127.0.0.1:9081": 2, "127.0.0.1:9082": 3, "127.0.0.1:9083": 4} ) +type dummyNode struct { + *gorums.RawNode +} + +func (n dummyNode) AsRaw() *gorums.RawNode { + return n.RawNode +} + func TestManagerLogging(t *testing.T) { var ( buf bytes.Buffer @@ -32,7 +40,7 @@ func TestManagerLogging(t *testing.T) { func TestManagerAddNode(t *testing.T) { mgr := gorums.NewRawManager(gorums.WithNoConnect()) - _, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeMap(nodeMap)) + _, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeMap(nodeMap)) if err != nil { t.Fatal(err) } @@ -81,7 +89,7 @@ func TestManagerAddNodeWithConn(t *testing.T) { ) defer mgr.Close() - _, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(addrs[:2])) + _, err := gorums.NewRawConfiguration[dummyNode, any](mgr, nil, gorums.WithNodeList(addrs[:2])) if err != nil { t.Fatal(err) } diff --git a/multicast.go b/multicast.go index bd5d00de..652ad4e3 100644 --- a/multicast.go +++ b/multicast.go @@ -10,24 +10,24 @@ import ( // By default this function returns once the message has been sent to all nodes. // Providing the call option WithNoSendWaiting, the function may return // before the message has been sent. -func (c RawConfiguration) Multicast(ctx context.Context, d QuorumCallData, opts ...CallOption) { +func (c RawConfiguration[NODES, QSPEC]) Multicast(ctx context.Context, d QuorumCallData, opts ...CallOption) { o := getCallOptions(E_Multicast, opts) md := &ordering.Metadata{MessageID: c.getMsgID(), Method: d.Method} sentMsgs := 0 var replyChan chan response if !o.noSendWaiting { - replyChan = make(chan response, len(c)) + replyChan = make(chan response, len(c.nodes)) } - for _, n := range c { + for _, n := range c.nodes { msg := d.Message if d.PerNodeArgFn != nil { - msg = d.PerNodeArgFn(d.Message, n.id) + msg = d.PerNodeArgFn(d.Message, n.AsRaw().id) if !msg.ProtoReflect().IsValid() { continue // don't send if no msg } } - n.channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: msg}, opts: o}, replyChan, false) + n.AsRaw().channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: msg}, opts: o}, replyChan, false) sentMsgs++ } diff --git a/node.go b/node.go index a7914d44..5f2a7eb2 100644 --- a/node.go +++ b/node.go @@ -32,6 +32,17 @@ type RawNode struct { channel *channel } +// RawNodeConstraint is a type constraint on types that can be converted to RawNodes. +type RawNodeConstraint interface { + ~struct{ *RawNode } + AsRaw() *RawNode +} + +// AsRawNode is an interface that all generated Node structures should implement. +type AsRawNode interface { + AsRaw() *RawNode +} + // NewRawNode returns a new node for the provided address. func NewRawNode(addr string) (*RawNode, error) { tcpAddr, err := net.ResolveTCPAddr("tcp", addr) diff --git a/node_test.go b/node_test.go index 33cd31b9..83d5c1cc 100644 --- a/node_test.go +++ b/node_test.go @@ -7,6 +7,14 @@ import ( "time" ) +type dummyNode struct { + *RawNode +} + +func (n dummyNode) AsRaw() *RawNode { + return n.RawNode +} + func TestNodeSort(t *testing.T) { nodes := []*RawNode{ { diff --git a/ordering/ordering.pb.go b/ordering/ordering.pb.go index eb33acd5..d6ad76a3 100644 --- a/ordering/ordering.pb.go +++ b/ordering/ordering.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: ordering/ordering.proto package ordering diff --git a/ordering/ordering_grpc.pb.go b/ordering/ordering_grpc.pb.go index 15e595b5..cfef8d18 100644 --- a/ordering/ordering_grpc.pb.go +++ b/ordering/ordering_grpc.pb.go @@ -1,8 +1,4 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.19.2 -// source: ordering/ordering.proto package ordering diff --git a/quorumcall.go b/quorumcall.go index d8001f43..38dc76be 100644 --- a/quorumcall.go +++ b/quorumcall.go @@ -22,21 +22,21 @@ type QuorumCallData struct { // QuorumCall performs a quorum call on the configuration. // // This method should be used by generated code only. -func (c RawConfiguration) QuorumCall(ctx context.Context, d QuorumCallData) (resp protoreflect.ProtoMessage, err error) { - expectedReplies := len(c) +func (c RawConfiguration[NODES, QSPEC]) QuorumCall(ctx context.Context, d QuorumCallData) (resp protoreflect.ProtoMessage, err error) { + expectedReplies := len(c.nodes) md := &ordering.Metadata{MessageID: c.getMsgID(), Method: d.Method} replyChan := make(chan response, expectedReplies) - for _, n := range c { + for _, n := range c.nodes { msg := d.Message if d.PerNodeArgFn != nil { - msg = d.PerNodeArgFn(d.Message, n.id) + msg = d.PerNodeArgFn(d.Message, n.AsRaw().id) if !msg.ProtoReflect().IsValid() { expectedReplies-- continue // don't send if no msg } } - n.channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: msg}}, replyChan, false) + n.AsRaw().channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: msg}}, replyChan, false) } var ( diff --git a/tests/config/config.pb.go b/tests/config/config.pb.go index b5d74dda..a98d1061 100644 --- a/tests/config/config.pb.go +++ b/tests/config/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: config/config.proto package config diff --git a/tests/config/config_gorums.pb.go b/tests/config/config_gorums.pb.go index 915eaa55..fcb28eae 100644 --- a/tests/config/config_gorums.pb.go +++ b/tests/config/config_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: config/config.proto package config @@ -23,10 +23,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -34,41 +35,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) +func (c Configuration) And(d Configuration) gorums.NodeListOption { + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. -func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) +func (c Configuration) Except(rm Configuration) gorums.NodeListOption { + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -99,31 +118,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -143,6 +161,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // QuorumSpec is the interface of quorum functions for ConfigTest. type QuorumSpec interface { gorums.ConfigOption @@ -167,10 +189,10 @@ func (c *Configuration) Config(ctx context.Context, in *Request) (resp *Response for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.ConfigQF(req.(*Request), r) + return c.AsRaw().QSpec().ConfigQF(req.(*Request), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } diff --git a/tests/config/config_test.go b/tests/config/config_test.go index 14c2b4e5..52aba864 100644 --- a/tests/config/config_test.go +++ b/tests/config/config_test.go @@ -46,7 +46,7 @@ func (q cfgQSpec) ConfigQF(_ *Request, replies map[uint32]*Response) (*Response, // setup returns a new configuration of cfgSize and a corresponding teardown function. // Calling setup multiple times will return a different configuration with different // sets of nodes. -func setup(t *testing.T, mgr *Manager, cfgSize int) (cfg *Configuration, teardown func()) { +func setup(t *testing.T, mgr *Manager, cfgSize int) (cfg Configuration, teardown func()) { t.Helper() srvs := make([]*cfgSrv, cfgSize) for i := range srvs { @@ -74,7 +74,7 @@ func setup(t *testing.T, mgr *Manager, cfgSize int) (cfg *Configuration, teardow // TestConfig creates and combines multiple configurations and invokes the Config RPC // method on the different configurations created below. func TestConfig(t *testing.T) { - callRPC := func(cfg *Configuration) { + callRPC := func(cfg Configuration) { for i := 0; i < 5; i++ { resp, err := cfg.Config(context.Background(), &Request{Num: uint64(i)}) if err != nil { diff --git a/tests/correctable/correctable.pb.go b/tests/correctable/correctable.pb.go index 98fca4bc..c94d7305 100644 --- a/tests/correctable/correctable.pb.go +++ b/tests/correctable/correctable.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: correctable/correctable.proto package correctable diff --git a/tests/correctable/correctable_gorums.pb.go b/tests/correctable/correctable_gorums.pb.go index 736409fe..3a0fef0a 100644 --- a/tests/correctable/correctable_gorums.pb.go +++ b/tests/correctable/correctable_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: correctable/correctable.proto package correctable @@ -25,10 +25,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -36,41 +37,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -101,31 +120,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -145,6 +163,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // Correctable asynchronously invokes a correctable quorum call on each node // in configuration c and returns a CorrectableCorrectableResponse, which can be used // to inspect any replies or errors when available. @@ -159,10 +181,10 @@ func (c *Configuration) Correctable(ctx context.Context, in *CorrectableRequest) for k, v := range replies { r[k] = v.(*CorrectableResponse) } - return c.qspec.CorrectableQF(req.(*CorrectableRequest), r) + return c.AsRaw().QSpec().CorrectableQF(req.(*CorrectableRequest), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableCorrectableResponse{corr} } @@ -181,10 +203,10 @@ func (c *Configuration) CorrectableStream(ctx context.Context, in *CorrectableRe for k, v := range replies { r[k] = v.(*CorrectableResponse) } - return c.qspec.CorrectableStreamQF(req.(*CorrectableRequest), r) + return c.AsRaw().QSpec().CorrectableStreamQF(req.(*CorrectableRequest), r) } - corr := c.RawConfiguration.CorrectableCall(ctx, cd) + corr := c.AsRaw().CorrectableCall(ctx, cd) return &CorrectableStreamCorrectableResponse{corr} } diff --git a/tests/correctable/correctable_test.go b/tests/correctable/correctable_test.go index f90a3f90..0be930f3 100644 --- a/tests/correctable/correctable_test.go +++ b/tests/correctable/correctable_test.go @@ -14,7 +14,7 @@ import ( // n is the number of replicas, and div is a divider. // the target level is n, and the level is calculated by the quorum function // by dividing the sum of levels from the servers with the divider. -func run(t *testing.T, n int, div int, corr func(context.Context, *Configuration) *gorums.Correctable) { +func run(t *testing.T, n int, div int, corr func(context.Context, Configuration) *gorums.Correctable) { addrs, teardown := gorums.TestSetup(t, n, func(i int) gorums.ServerIface { gorumsSrv := gorums.NewServer() RegisterCorrectableTestServer(gorumsSrv, &testSrv{n}) @@ -57,14 +57,14 @@ func run(t *testing.T, n int, div int, corr func(context.Context, *Configuration } func TestCorrectable(t *testing.T) { - run(t, 4, 1, func(ctx context.Context, c *Configuration) *gorums.Correctable { + run(t, 4, 1, func(ctx context.Context, c Configuration) *gorums.Correctable { corr := c.Correctable(ctx, &CorrectableRequest{}) return corr.Correctable }) } func TestCorrectableStream(t *testing.T) { - run(t, 4, 4, func(ctx context.Context, c *Configuration) *gorums.Correctable { + run(t, 4, 4, func(ctx context.Context, c Configuration) *gorums.Correctable { corr := c.CorrectableStream(ctx, &CorrectableRequest{}) return corr.Correctable }) diff --git a/tests/dummy/dummy.pb.go b/tests/dummy/dummy.pb.go index a1b8c647..7fcbca55 100644 --- a/tests/dummy/dummy.pb.go +++ b/tests/dummy/dummy.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: dummy/dummy.proto package dummy diff --git a/tests/dummy/dummy_gorums.pb.go b/tests/dummy/dummy_gorums.pb.go index ad31c670..894adec2 100644 --- a/tests/dummy/dummy_gorums.pb.go +++ b/tests/dummy/dummy_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: dummy/dummy.proto package dummy @@ -22,10 +22,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -33,41 +34,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -98,31 +117,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -142,6 +160,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // QuorumSpec is the interface of quorum functions for Dummy. type QuorumSpec interface { gorums.ConfigOption diff --git a/tests/metadata/metadata.pb.go b/tests/metadata/metadata.pb.go index 27ca2b4d..d5228862 100644 --- a/tests/metadata/metadata.pb.go +++ b/tests/metadata/metadata.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: metadata/metadata.proto package metadata diff --git a/tests/metadata/metadata_gorums.pb.go b/tests/metadata/metadata_gorums.pb.go index 6818ce29..bddff981 100644 --- a/tests/metadata/metadata_gorums.pb.go +++ b/tests/metadata/metadata_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: metadata/metadata.proto package metadata @@ -23,10 +23,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -34,41 +35,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -99,31 +118,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -143,6 +161,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // QuorumSpec is the interface of quorum functions for MetadataTest. type QuorumSpec interface { gorums.ConfigOption diff --git a/tests/oneway/oneway.pb.go b/tests/oneway/oneway.pb.go index bc220e24..9d3abeb8 100644 --- a/tests/oneway/oneway.pb.go +++ b/tests/oneway/oneway.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: oneway/oneway.proto package oneway diff --git a/tests/oneway/oneway_gorums.pb.go b/tests/oneway/oneway_gorums.pb.go index c8c60d41..f59242be 100644 --- a/tests/oneway/oneway_gorums.pb.go +++ b/tests/oneway/oneway_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: oneway/oneway.proto package oneway @@ -23,10 +23,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -34,41 +35,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() +} + +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -99,31 +118,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -143,6 +161,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // Multicast is a quorum call invoked on all nodes in configuration c, // with the same argument in, and returns a combined result. func (c *Configuration) Multicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { @@ -151,7 +173,7 @@ func (c *Configuration) Multicast(ctx context.Context, in *Request, opts ...goru Method: "oneway.OnewayTest.Multicast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } // MulticastPerNode is a quorum call invoked on each node in configuration c, @@ -169,7 +191,7 @@ func (c *Configuration) MulticastPerNode(ctx context.Context, in *Request, f fun return f(req.(*Request), nid) } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.AsRaw().Multicast(ctx, cd, opts...) } // QuorumSpec is the interface of quorum functions for OnewayTest. diff --git a/tests/oneway/oneway_test.go b/tests/oneway/oneway_test.go index d34f3991..015df6e5 100644 --- a/tests/oneway/oneway_test.go +++ b/tests/oneway/oneway_test.go @@ -45,7 +45,7 @@ func (s *onewaySrv) MulticastPerNode(ctx gorums.ServerCtx, r *oneway.Request) { type testQSpec struct{} -func setup(t testing.TB, cfgSize int) (cfg *oneway.Configuration, srvs []*onewaySrv, teardown func()) { +func setup(t testing.TB, cfgSize int) (cfg oneway.Configuration, srvs []*onewaySrv, teardown func()) { t.Helper() srvs = make([]*onewaySrv, cfgSize) for i := 0; i < cfgSize; i++ { @@ -99,7 +99,7 @@ func TestOnewayCalls(t *testing.T) { {name: "MulticastNoSendWaiting", calls: numCalls, servers: 9, sendWait: false}, } - f := func(c *oneway.Configuration) func(context.Context, *oneway.Request, ...gorums.CallOption) { + f := func(c oneway.Configuration) func(context.Context, *oneway.Request, ...gorums.CallOption) { if c.Size() == 1 { return c.Nodes()[0].Unicast } diff --git a/tests/ordering/order.pb.go b/tests/ordering/order.pb.go index 19cf8da3..0fba2319 100644 --- a/tests/ordering/order.pb.go +++ b/tests/ordering/order.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: ordering/order.proto package ordering diff --git a/tests/ordering/order_gorums.pb.go b/tests/ordering/order_gorums.pb.go index 663494a8..0c1e79d7 100644 --- a/tests/ordering/order_gorums.pb.go +++ b/tests/ordering/order_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: ordering/order.proto package ordering @@ -23,10 +23,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -34,41 +35,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -99,31 +118,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -143,6 +161,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // QCAsync asynchronously invokes a quorum call on configuration c // and returns a AsyncResponse, which can be used to inspect the quorum call // reply and error when available. @@ -156,10 +178,10 @@ func (c *Configuration) QCAsync(ctx context.Context, in *Request) *AsyncResponse for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QCAsyncQF(req.(*Request), r) + return c.AsRaw().QSpec().QCAsyncQF(req.(*Request), r) } - fut := c.RawConfiguration.AsyncCall(ctx, cd) + fut := c.AsRaw().AsyncCall(ctx, cd) return &AsyncResponse{fut} } @@ -194,10 +216,10 @@ func (c *Configuration) QC(ctx context.Context, in *Request) (resp *Response, er for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.QCQF(req.(*Request), r) + return c.AsRaw().QSpec().QCQF(req.(*Request), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } diff --git a/tests/ordering/order_test.go b/tests/ordering/order_test.go index ae65561f..c3d4b960 100644 --- a/tests/ordering/order_test.go +++ b/tests/ordering/order_test.go @@ -78,7 +78,7 @@ func (q testQSpec) AsyncHandlerQF(_ *Request, replies map[uint32]*Response) (*Re return q.qf(replies) } -func setup(t *testing.T, cfgSize int) (cfg *Configuration, teardown func()) { +func setup(t *testing.T, cfgSize int) (cfg Configuration, teardown func()) { t.Helper() addrs, closeServers := gorums.TestSetup(t, cfgSize, func(_ int) gorums.ServerIface { srv := gorums.NewServer() @@ -207,7 +207,7 @@ func TestMixedOrdering(t *testing.T) { var wg sync.WaitGroup wg.Add(len(nodes)) for _, node := range nodes { - go func(node *Node) { + go func(node Node) { defer wg.Done() resp, err := node.UnaryRPC(context.Background(), &Request{Num: uint64(i)}) if err != nil { diff --git a/tests/qf/qf.pb.go b/tests/qf/qf.pb.go index fc15b1a4..c13a6b60 100644 --- a/tests/qf/qf.pb.go +++ b/tests/qf/qf.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: qf/qf.proto package qf diff --git a/tests/qf/qf_gorums.pb.go b/tests/qf/qf_gorums.pb.go index 0fa19d9b..35a575f4 100644 --- a/tests/qf/qf_gorums.pb.go +++ b/tests/qf/qf_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: qf/qf.proto package qf @@ -23,10 +23,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -34,41 +35,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -99,31 +118,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -143,6 +161,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // QuorumSpec is the interface of quorum functions for QuorumFunction. type QuorumSpec interface { gorums.ConfigOption @@ -174,10 +196,10 @@ func (c *Configuration) UseReq(ctx context.Context, in *Request) (resp *Response for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.UseReqQF(req.(*Request), r) + return c.AsRaw().QSpec().UseReqQF(req.(*Request), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } @@ -196,10 +218,10 @@ func (c *Configuration) IgnoreReq(ctx context.Context, in *Request) (resp *Respo for k, v := range replies { r[k] = v.(*Response) } - return c.qspec.IgnoreReqQF(req.(*Request), r) + return c.AsRaw().QSpec().IgnoreReqQF(req.(*Request), r) } - res, err := c.RawConfiguration.QuorumCall(ctx, cd) + res, err := c.AsRaw().QuorumCall(ctx, cd) if err != nil { return nil, err } diff --git a/tests/tls/tls.pb.go b/tests/tls/tls.pb.go index bcc35f2f..f13e982e 100644 --- a/tests/tls/tls.pb.go +++ b/tests/tls/tls.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: tls/tls.proto package tls diff --git a/tests/tls/tls_gorums.pb.go b/tests/tls/tls_gorums.pb.go index 297c2773..50aaa79a 100644 --- a/tests/tls/tls_gorums.pb.go +++ b/tests/tls/tls_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: tls/tls.proto package tls @@ -22,10 +22,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -33,41 +34,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -98,31 +117,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -142,6 +160,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // QuorumSpec is the interface of quorum functions for TLS. type QuorumSpec interface { gorums.ConfigOption diff --git a/tests/unresponsive/unresponsive.pb.go b/tests/unresponsive/unresponsive.pb.go index c1af48bf..b145be93 100644 --- a/tests/unresponsive/unresponsive.pb.go +++ b/tests/unresponsive/unresponsive.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.2 +// protoc v3.17.3 // source: unresponsive/unresponsive.proto package unresponsive diff --git a/tests/unresponsive/unresponsive_gorums.pb.go b/tests/unresponsive/unresponsive_gorums.pb.go index ce3f8c88..c199cfa0 100644 --- a/tests/unresponsive/unresponsive_gorums.pb.go +++ b/tests/unresponsive/unresponsive_gorums.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: // protoc-gen-gorums v0.7.0-devel -// protoc v3.19.2 +// protoc v3.17.3 // source: unresponsive/unresponsive.proto package unresponsive @@ -22,10 +22,11 @@ const ( // A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. -type Configuration struct { - gorums.RawConfiguration - nodes []*Node - qspec QuorumSpec +type Configuration gorums.RawConfiguration[Node, QuorumSpec] + +// AsRaw returns the RawConfiguration. +func (c Configuration) AsRaw() gorums.RawConfiguration[Node, QuorumSpec] { + return gorums.RawConfiguration[Node, QuorumSpec](c) } // ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. @@ -33,41 +34,59 @@ type Configuration struct { // This function may for example be used to "clone" a configuration but install a different QuorumSpec: // cfg1, err := mgr.NewConfiguration(qspec1, opts...) // cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigurationFromRaw(rawCfg gorums.RawConfiguration, qspec QuorumSpec) *Configuration { +func ConfigurationFromRaw[NODE gorums.RawNodeConstraint, QSPEC QuorumSpec](rawCfg gorums.RawConfiguration[NODE, QSPEC], qspec QuorumSpec) Configuration { // return an error if the QuorumSpec interface is not empty and no implementation was provided. var test interface{} = struct{}{} if _, empty := test.(QuorumSpec); !empty && qspec == nil { panic("QuorumSpec may not be nil") } - return &Configuration{ - RawConfiguration: rawCfg, - qspec: qspec, + + otherNodes := rawCfg.Nodes() + ourNodes := make([]Node, rawCfg.Size()) + + for i, node := range otherNodes { + ourNodes[i] = Node{node.AsRaw()} } + + return Configuration(gorums.NewRawConfigurationFromNodeSlice(ourNodes, qspec)) +} + +// NodeIDs returns a slice of the IDs of the nodes in this configuration. +func (c Configuration) NodeIDs() []uint32 { + return c.AsRaw().NodeIDs() } -// Nodes returns a slice of each available node. IDs are returned in the same -// order as they were provided in the creation of the Manager. +// Nodes returns the nodes in this configuration. // // NOTE: mutating the returned slice is not supported. -func (c *Configuration) Nodes() []*Node { - if c.nodes == nil { - c.nodes = make([]*Node, 0, c.Size()) - for _, n := range c.RawConfiguration { - c.nodes = append(c.nodes, &Node{n}) - } - } - return c.nodes +func (c Configuration) Nodes() []Node { + return c.AsRaw().Nodes() +} + +// QSpec returns the quorum specification object. +func (c Configuration) QSpec() QuorumSpec { + return c.AsRaw().QSpec() +} + +// Size returns the number of nodes in this configuration. +func (c Configuration) Size() int { + return c.AsRaw().Size() +} + +// Equal returns true if configurations b and c have the same set of nodes. +func (c Configuration) Equal(b Configuration) bool { + return c.AsRaw().Equal(b.AsRaw()) } // And returns a NodeListOption that can be used to create a new configuration combining c and d. func (c Configuration) And(d *Configuration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) + return c.AsRaw().And(d.AsRaw()) } // Except returns a NodeListOption that can be used to create a new configuration // from c without the nodes in rm. func (c Configuration) Except(rm *Configuration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) + return c.AsRaw().Except(rm.AsRaw()) } func init() { @@ -98,31 +117,30 @@ func NewManager(opts ...gorums.ManagerOption) (mgr *Manager) { // Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. // A new configuration can also be created from an existing configuration, // using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c *Configuration, err error) { +func (m *Manager) NewConfiguration(opts ...gorums.ConfigOption) (c Configuration, err error) { if len(opts) < 1 || len(opts) > 2 { - return nil, fmt.Errorf("wrong number of options: %d", len(opts)) + return c, gorums.ConfigCreationError(fmt.Errorf("wrong number of options: %d", len(opts))) } - c = &Configuration{} + var ( + qspec QuorumSpec + nodes gorums.NodeListOption + ) for _, opt := range opts { switch v := opt.(type) { case gorums.NodeListOption: - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, v) - if err != nil { - return nil, err - } + nodes = v case QuorumSpec: // Must be last since v may match QuorumSpec if it is interface{} - c.qspec = v + qspec = v default: - return nil, fmt.Errorf("unknown option type: %v", v) + return c, gorums.ConfigCreationError(fmt.Errorf("unknown option type: %v", v)) } } - // return an error if the QuorumSpec interface is not empty and no implementation was provided. - var test interface{} = struct{}{} - if _, empty := test.(QuorumSpec); !empty && c.qspec == nil { - return nil, fmt.Errorf("missing required QuorumSpec") + rawCfg, err := gorums.NewRawConfiguration[Node](m.RawManager, qspec, nodes) + if err != nil { + return c, gorums.ConfigCreationError(err) } - return c, nil + return ConfigurationFromRaw(rawCfg, qspec), nil } // Nodes returns a slice of available nodes on this manager. @@ -142,6 +160,10 @@ type Node struct { *gorums.RawNode } +func (n Node) AsRaw() *gorums.RawNode { + return n.RawNode +} + // QuorumSpec is the interface of quorum functions for Unresponsive. type QuorumSpec interface { gorums.ConfigOption