diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index 9c49227d..8c18bc05 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -37,7 +37,8 @@ type ( serverFunc func(context.Context, *TimedMsg) ) -func runQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int, f qcFunc) (*Result, error) { +func runQCBenchmark(opts Options, cfg *gorums.Configuration, quorum int, f qcFunc) (*Result, error) { + rpcCfg := BenchmarkConfigurationRpc(cfg) ctx, cancel := context.WithCancel(context.Background()) defer cancel() msg := Echo_builder{Payload: make([]byte, opts.Payload)}.Build() @@ -63,7 +64,7 @@ func runQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int, f qcF } if opts.Remote { - err := qf(cfg.StartBenchmark(ctx, &StartRequest{}), quorum) + err := qf(rpcCfg.StartBenchmark(ctx, &StartRequest{}), quorum) if err != nil { return nil, err } @@ -93,7 +94,7 @@ func runQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int, f qcF result := s.GetResult() if opts.Remote { - memStats, err := stopBenchmarkQF(cfg.StopBenchmark(ctx, &StopRequest{}), cfg.Size()) + memStats, err := stopBenchmarkQF(rpcCfg.StopBenchmark(ctx, &StopRequest{}), cfg.Size()) if err != nil { return nil, err } @@ -103,7 +104,8 @@ func runQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int, f qcF return result, nil } -func runAsyncQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int) (*Result, error) { +func runAsyncQCBenchmark(opts Options, cfg *gorums.Configuration, quorum int) (*Result, error) { + rpcCfg := BenchmarkConfigurationRpc(cfg) ctx, cancel := context.WithCancel(context.Background()) defer cancel() msg := Echo_builder{Payload: make([]byte, opts.Payload)}.Build() @@ -116,7 +118,7 @@ func runAsyncQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int) var warmupFunc func() error warmupFunc = func() error { for ; !time.Now().After(warmupEnd) && atomic.LoadUint64(&async) < uint64(opts.MaxAsync); atomic.AddUint64(&async, 1) { - responses := cfg.AsyncQuorumCall(ctx, msg) + responses := rpcCfg.AsyncQuorumCall(ctx, msg) g.Go(func() error { err := qf(responses, quorum) if err != nil { @@ -139,7 +141,7 @@ func runAsyncQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int) } if opts.Remote { - err := qf(cfg.StartBenchmark(ctx, &StartRequest{}), cfg.Size()) + err := qf(rpcCfg.StartBenchmark(ctx, &StartRequest{}), cfg.Size()) if err != nil { return nil, err } @@ -150,7 +152,7 @@ func runAsyncQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int) benchmarkFunc = func() error { for ; !time.Now().After(endTime) && atomic.LoadUint64(&async) < uint64(opts.MaxAsync); atomic.AddUint64(&async, 1) { start := time.Now() - responses := cfg.AsyncQuorumCall(ctx, msg) + responses := rpcCfg.AsyncQuorumCall(ctx, msg) g.Go(func() error { err := qf(responses, quorum) if err != nil { @@ -177,7 +179,7 @@ func runAsyncQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int) result := s.GetResult() if opts.Remote { - memStats, err := stopBenchmarkQF(cfg.StopBenchmark(ctx, &StopRequest{}), cfg.Size()) + memStats, err := stopBenchmarkQF(BenchmarkConfigurationRpc(cfg).StopBenchmark(ctx, &StopRequest{}), cfg.Size()) if err != nil { return nil, err } @@ -187,7 +189,8 @@ func runAsyncQCBenchmark(opts Options, cfg *BenchmarkConfiguration, quorum int) return result, nil } -func runServerBenchmark(opts Options, cfg *BenchmarkConfiguration, f serverFunc) (*Result, error) { +func runServerBenchmark(opts Options, cfg *gorums.Configuration, f serverFunc) (*Result, error) { + rpcCfg := BenchmarkConfigurationRpc(cfg) ctx, cancel := context.WithCancel(context.Background()) defer cancel() payload := make([]byte, opts.Payload) @@ -211,7 +214,7 @@ func runServerBenchmark(opts Options, cfg *BenchmarkConfiguration, f serverFunc) return nil, err } - err = qf(cfg.StartServerBenchmark(ctx, &StartRequest{}), cfg.Size()) + err = qf(rpcCfg.StartServerBenchmark(ctx, &StartRequest{}), cfg.Size()) if err != nil { return nil, err } @@ -227,7 +230,7 @@ func runServerBenchmark(opts Options, cfg *BenchmarkConfiguration, f serverFunc) } runtime.ReadMemStats(&end) - resp, err := stopServerBenchmarkQF(cfg.StopServerBenchmark(ctx, &StopRequest{}), cfg.Size()) + resp, err := stopServerBenchmarkQF(rpcCfg.StopServerBenchmark(ctx, &StopRequest{}), cfg.Size()) if err != nil { return nil, err } @@ -241,12 +244,15 @@ func runServerBenchmark(opts Options, cfg *BenchmarkConfiguration, f serverFunc) } // GetBenchmarks returns a list of Benchmarks that can be performed on the configuration -func GetBenchmarks(cfg *BenchmarkConfiguration, quorum int) []Bench { +func GetBenchmarks(cfg *gorums.Configuration, quorum int) []Bench { + rpcCfg := BenchmarkConfigurationRpc(cfg) m := []Bench{ { Name: "QuorumCall", Description: "NodeStream based quorum call implementation with FIFO ordering", - runBench: func(opts Options) (*Result, error) { return runQCBenchmark(opts, cfg, quorum, cfg.QuorumCall) }, + runBench: func(opts Options) (*Result, error) { + return runQCBenchmark(opts, cfg, quorum, rpcCfg.QuorumCall) + }, }, { Name: "AsyncQuorumCall", @@ -256,13 +262,15 @@ func GetBenchmarks(cfg *BenchmarkConfiguration, quorum int) []Bench { { Name: "SlowServer", Description: "Quorum Call with a 10s processing time on the server", - runBench: func(opts Options) (*Result, error) { return runQCBenchmark(opts, cfg, quorum, cfg.SlowServer) }, + runBench: func(opts Options) (*Result, error) { + return runQCBenchmark(opts, cfg, quorum, rpcCfg.SlowServer) + }, }, { Name: "Multicast", Description: "NodeStream based multicast implementation (servers measure latency and throughput)", runBench: func(opts Options) (*Result, error) { - return runServerBenchmark(opts, cfg, func(ctx context.Context, msg *TimedMsg) { cfg.Multicast(ctx, msg) }) + return runServerBenchmark(opts, cfg, func(ctx context.Context, msg *TimedMsg) { rpcCfg.Multicast(ctx, msg) }) }, }, } @@ -270,7 +278,7 @@ func GetBenchmarks(cfg *BenchmarkConfiguration, quorum int) []Bench { } // RunBenchmarks runs all the benchmarks that match the given regex with the given options -func RunBenchmarks(benchRegex *regexp.Regexp, options Options, cfg *BenchmarkConfiguration, quorum int) ([]*Result, error) { +func RunBenchmarks(benchRegex *regexp.Regexp, options Options, cfg *gorums.Configuration, quorum int) ([]*Result, error) { benchmarks := GetBenchmarks(cfg, quorum) var results []*Result for _, b := range benchmarks { diff --git a/benchmark/benchmark_gorums.pb.go b/benchmark/benchmark_gorums.pb.go index 990e552d..063ba60f 100644 --- a/benchmark/benchmark_gorums.pb.go +++ b/benchmark/benchmark_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: benchmark/benchmark.proto @@ -51,86 +51,13 @@ var _ BenchmarkNodeClient = (*BenchmarkNode)(nil) // A BenchmarkConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type BenchmarkConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewBenchmarkConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewBenchmarkConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *BenchmarkConfiguration, err error) { - c = &BenchmarkConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func BenchmarkConfigurationRpc(cfg *gorums.Configuration) BenchmarkConfiguration { + return BenchmarkConfiguration{ + cfg: cfg, } - return c, nil -} - -// SubBenchmarkConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *BenchmarkConfiguration) SubBenchmarkConfiguration(cfg gorums.NodeListOption) (subCfg *BenchmarkConfiguration, err error) { - subCfg = &BenchmarkConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// BenchmarkConfigurationFromRaw returns a new BenchmarkConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func BenchmarkConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*BenchmarkConfiguration, error) { - newCfg := &BenchmarkConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *BenchmarkConfiguration) Nodes() []*BenchmarkNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*BenchmarkNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &BenchmarkNode{n} - } - return nodes -} - -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *BenchmarkConfiguration) AllNodes() []*BenchmarkNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*BenchmarkNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &BenchmarkNode{n} - } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c BenchmarkConfiguration) And(d *BenchmarkConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c BenchmarkConfiguration) Except(rm *BenchmarkConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) } // Reference imports to suppress errors if they are not otherwise used. @@ -139,108 +66,121 @@ var _ emptypb.Empty // Multicast is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *BenchmarkConfiguration) Multicast(ctx context.Context, in *TimedMsg, opts ...gorums.CallOption) { +func (c BenchmarkConfiguration) Multicast(ctx context.Context, in *TimedMsg, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "benchmark.Benchmark.Multicast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // BenchmarkNode holds the node specific methods for the Benchmark service. type BenchmarkNode struct { - *gorums.RawNode + node *gorums.Node +} + +func BenchmarkNodeRpc(node *gorums.Node) BenchmarkNode { + return BenchmarkNode{ + node: node, + } } // StartServerBenchmark is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *BenchmarkConfiguration) StartServerBenchmark(ctx context.Context, in *StartRequest) gorums.Responses[*StartResponse] { +func (c BenchmarkConfiguration) StartServerBenchmark(ctx context.Context, in *StartRequest) gorums.Responses[*StartResponse] { cd := gorums.QuorumCallData{ Message: in, Method: "benchmark.Benchmark.StartServerBenchmark", ServerStream: false, } - return gorums.QuorumCall[*StartResponse](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*StartResponse](responses) } // StopServerBenchmark is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *BenchmarkConfiguration) StopServerBenchmark(ctx context.Context, in *StopRequest) gorums.Responses[*Result] { +func (c BenchmarkConfiguration) StopServerBenchmark(ctx context.Context, in *StopRequest) gorums.Responses[*Result] { cd := gorums.QuorumCallData{ Message: in, Method: "benchmark.Benchmark.StopServerBenchmark", ServerStream: false, } - return gorums.QuorumCall[*Result](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Result](responses) } // StartBenchmark is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *BenchmarkConfiguration) StartBenchmark(ctx context.Context, in *StartRequest) gorums.Responses[*StartResponse] { +func (c BenchmarkConfiguration) StartBenchmark(ctx context.Context, in *StartRequest) gorums.Responses[*StartResponse] { cd := gorums.QuorumCallData{ Message: in, Method: "benchmark.Benchmark.StartBenchmark", ServerStream: false, } - return gorums.QuorumCall[*StartResponse](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*StartResponse](responses) } // StopBenchmark is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *BenchmarkConfiguration) StopBenchmark(ctx context.Context, in *StopRequest) gorums.Responses[*MemoryStat] { +func (c BenchmarkConfiguration) StopBenchmark(ctx context.Context, in *StopRequest) gorums.Responses[*MemoryStat] { cd := gorums.QuorumCallData{ Message: in, Method: "benchmark.Benchmark.StopBenchmark", ServerStream: false, } - return gorums.QuorumCall[*MemoryStat](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*MemoryStat](responses) } // benchmarks -func (c *BenchmarkConfiguration) QuorumCall(ctx context.Context, in *Echo) gorums.Responses[*Echo] { +func (c BenchmarkConfiguration) QuorumCall(ctx context.Context, in *Echo) gorums.Responses[*Echo] { cd := gorums.QuorumCallData{ Message: in, Method: "benchmark.Benchmark.QuorumCall", ServerStream: false, } - return gorums.QuorumCall[*Echo](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Echo](responses) } // AsyncQuorumCall is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *BenchmarkConfiguration) AsyncQuorumCall(ctx context.Context, in *Echo) gorums.Responses[*Echo] { +func (c BenchmarkConfiguration) AsyncQuorumCall(ctx context.Context, in *Echo) gorums.Responses[*Echo] { cd := gorums.QuorumCallData{ Message: in, Method: "benchmark.Benchmark.AsyncQuorumCall", ServerStream: false, } - return gorums.QuorumCall[*Echo](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Echo](responses) } // SlowServer is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *BenchmarkConfiguration) SlowServer(ctx context.Context, in *Echo) gorums.Responses[*Echo] { +func (c BenchmarkConfiguration) SlowServer(ctx context.Context, in *Echo) gorums.Responses[*Echo] { cd := gorums.QuorumCallData{ Message: in, Method: "benchmark.Benchmark.SlowServer", ServerStream: false, } - return gorums.QuorumCall[*Echo](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Echo](responses) } // BenchmarkServer is the server-side API for the Benchmark Service diff --git a/channel.go b/channel.go index 45154bc3..5feece82 100644 --- a/channel.go +++ b/channel.go @@ -43,7 +43,7 @@ type responseRouter struct { type channel struct { sendQ chan request - node *RawNode + node *Node mu sync.Mutex lastError error latency time.Duration @@ -67,7 +67,7 @@ type channel struct { // connection has not yet been established. This is to prevent // deadlock when invoking a call type, as the goroutine will // block on the sendQ until a connection has been established. -func newChannel(n *RawNode) *channel { +func newChannel(n *Node) *channel { c := &channel{ sendQ: make(chan request, n.mgr.opts.sendBuffer), backoffCfg: n.mgr.opts.backoff, diff --git a/channel_test.go b/channel_test.go index 302ebfe4..827da67e 100644 --- a/channel_test.go +++ b/channel_test.go @@ -40,7 +40,7 @@ func newDummySrv() *Server { } func TestChannelCreation(t *testing.T) { - node, err := NewRawNode("127.0.0.1:5000") + node, err := NewNode("127.0.0.1:5000") if err != nil { t.Fatal(err) } @@ -71,7 +71,7 @@ func TestChannelSuccessfulConnection(t *testing.T) { defer teardown() mgr := dummyMgr() defer mgr.close() - node, err := NewRawNode(addrs[0]) + node, err := NewNode(addrs[0]) if err != nil { t.Fatal(err) } @@ -94,7 +94,7 @@ func TestChannelUnsuccessfulConnection(t *testing.T) { mgr := dummyMgr() defer mgr.close() // no servers are listening on the given address - node, err := NewRawNode("127.0.0.1:5000") + node, err := NewNode("127.0.0.1:5000") if err != nil { t.Fatal(err) } @@ -115,7 +115,7 @@ func TestChannelReconnection(t *testing.T) { srvAddr := "127.0.0.1:5000" // wait to start the server startServer, stopServer := testServerSetup(t, srvAddr, newDummySrv()) - node, err := NewRawNode(srvAddr) + node, err := NewNode(srvAddr) if err != nil { t.Fatal(err) } diff --git a/cmd/benchmark/main.go b/cmd/benchmark/main.go index 7056d77b..b96dae14 100644 --- a/cmd/benchmark/main.go +++ b/cmd/benchmark/main.go @@ -196,7 +196,7 @@ func main() { gorums.WithSendBufferSize(*sendBuffer), } - cfg, err := benchmark.NewBenchmarkConfiguration(gorums.WithNodeList(remotes[:options.NumNodes]), mgrOpts...) + cfg, err := gorums.NewConfiguration(gorums.WithNodeList(remotes[:options.NumNodes]), mgrOpts...) checkf("Failed to create configuration: %v", err) defer cfg.Close() diff --git a/cmd/protoc-gen-gorums/dev/zorums_client_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_client_gorums.pb.go index efad15ff..e5de1293 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_client_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_client_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: zorums.proto diff --git a/cmd/protoc-gen-gorums/dev/zorums_configuration_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_configuration_gorums.pb.go index 55724e0d..f5b4151e 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_configuration_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_configuration_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: zorums.proto @@ -20,169 +20,23 @@ const ( // A ZorumsServiceConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type ZorumsServiceConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewZorumsServiceConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewZorumsServiceConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *ZorumsServiceConfiguration, err error) { - c = &ZorumsServiceConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func ZorumsServiceConfigurationRpc(cfg *gorums.Configuration) ZorumsServiceConfiguration { + return ZorumsServiceConfiguration{ + cfg: cfg, } - return c, nil -} - -// SubZorumsServiceConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *ZorumsServiceConfiguration) SubZorumsServiceConfiguration(cfg gorums.NodeListOption) (subCfg *ZorumsServiceConfiguration, err error) { - subCfg = &ZorumsServiceConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// ZorumsServiceConfigurationFromRaw returns a new ZorumsServiceConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ZorumsServiceConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*ZorumsServiceConfiguration, error) { - newCfg := &ZorumsServiceConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *ZorumsServiceConfiguration) Nodes() []*ZorumsServiceNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*ZorumsServiceNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &ZorumsServiceNode{n} - } - return nodes -} - -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *ZorumsServiceConfiguration) AllNodes() []*ZorumsServiceNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*ZorumsServiceNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &ZorumsServiceNode{n} - } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c ZorumsServiceConfiguration) And(d *ZorumsServiceConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c ZorumsServiceConfiguration) Except(rm *ZorumsServiceConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) } // A ZorumsNoQCServiceConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type ZorumsNoQCServiceConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewZorumsNoQCServiceConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewZorumsNoQCServiceConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *ZorumsNoQCServiceConfiguration, err error) { - c = &ZorumsNoQCServiceConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func ZorumsNoQCServiceConfigurationRpc(cfg *gorums.Configuration) ZorumsNoQCServiceConfiguration { + return ZorumsNoQCServiceConfiguration{ + cfg: cfg, } - return c, nil -} - -// SubZorumsNoQCServiceConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *ZorumsNoQCServiceConfiguration) SubZorumsNoQCServiceConfiguration(cfg gorums.NodeListOption) (subCfg *ZorumsNoQCServiceConfiguration, err error) { - subCfg = &ZorumsNoQCServiceConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// ZorumsNoQCServiceConfigurationFromRaw returns a new ZorumsNoQCServiceConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ZorumsNoQCServiceConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*ZorumsNoQCServiceConfiguration, error) { - newCfg := &ZorumsNoQCServiceConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *ZorumsNoQCServiceConfiguration) Nodes() []*ZorumsNoQCServiceNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*ZorumsNoQCServiceNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &ZorumsNoQCServiceNode{n} - } - return nodes -} - -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *ZorumsNoQCServiceConfiguration) AllNodes() []*ZorumsNoQCServiceNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*ZorumsNoQCServiceNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &ZorumsNoQCServiceNode{n} - } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c ZorumsNoQCServiceConfiguration) And(d *ZorumsNoQCServiceConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c ZorumsNoQCServiceConfiguration) Except(rm *ZorumsNoQCServiceConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) } 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 26993040..f2dad0e0 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_multicast_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_multicast_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: zorums.proto @@ -21,18 +21,18 @@ const ( ) // Multicast plain. Response type is not needed here. -func (c *ZorumsServiceConfiguration) Multicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (c ZorumsServiceConfiguration) Multicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.Multicast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // MulticastPerNodeArg with per_node_arg option. -func (c *ZorumsServiceConfiguration) MulticastPerNodeArg(ctx context.Context, in *Request, f func(*Request, uint32) *Request, opts ...gorums.CallOption) { +func (c ZorumsServiceConfiguration) MulticastPerNodeArg(ctx context.Context, in *Request, f func(*Request, uint32) *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, @@ -43,61 +43,61 @@ func (c *ZorumsServiceConfiguration) MulticastPerNodeArg(ctx context.Context, in return f(req.(*Request), nid) } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // Multicast2 is testing whether multiple streams work. -func (c *ZorumsServiceConfiguration) Multicast2(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (c ZorumsServiceConfiguration) Multicast2(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.Multicast2", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // Reference imports to suppress errors if they are not otherwise used. var _ emptypb.Empty // Multicast3 is testing imported message type. -func (c *ZorumsServiceConfiguration) Multicast3(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (c ZorumsServiceConfiguration) Multicast3(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.Multicast3", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // Reference imports to suppress errors if they are not otherwise used. var _ emptypb.Empty // Multicast4 is testing imported message type. -func (c *ZorumsServiceConfiguration) Multicast4(ctx context.Context, in *emptypb.Empty, opts ...gorums.CallOption) { +func (c ZorumsServiceConfiguration) Multicast4(ctx context.Context, in *emptypb.Empty, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.Multicast4", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // Multicast plain. Response type is not needed here. -func (c *ZorumsNoQCServiceConfiguration) Multicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (c ZorumsNoQCServiceConfiguration) Multicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsNoQCService.Multicast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // MulticastPerNodeArg with per_node_arg option. -func (c *ZorumsNoQCServiceConfiguration) MulticastPerNodeArg(ctx context.Context, in *Request, f func(*Request, uint32) *Request, opts ...gorums.CallOption) { +func (c ZorumsNoQCServiceConfiguration) MulticastPerNodeArg(ctx context.Context, in *Request, f func(*Request, uint32) *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, @@ -108,44 +108,44 @@ func (c *ZorumsNoQCServiceConfiguration) MulticastPerNodeArg(ctx context.Context return f(req.(*Request), nid) } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // Multicast2 is testing whether multiple streams work. -func (c *ZorumsNoQCServiceConfiguration) Multicast2(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (c ZorumsNoQCServiceConfiguration) Multicast2(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsNoQCService.Multicast2", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // Reference imports to suppress errors if they are not otherwise used. var _ emptypb.Empty // Multicast3 is testing imported message type. -func (c *ZorumsNoQCServiceConfiguration) Multicast3(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (c ZorumsNoQCServiceConfiguration) Multicast3(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsNoQCService.Multicast3", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // Reference imports to suppress errors if they are not otherwise used. var _ emptypb.Empty // Multicast4 is testing imported message type. -func (c *ZorumsNoQCServiceConfiguration) Multicast4(ctx context.Context, in *emptypb.Empty, opts ...gorums.CallOption) { +func (c ZorumsNoQCServiceConfiguration) Multicast4(ctx context.Context, in *emptypb.Empty, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsNoQCService.Multicast4", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } diff --git a/cmd/protoc-gen-gorums/dev/zorums_node_gorums.pb.go b/cmd/protoc-gen-gorums/dev/zorums_node_gorums.pb.go index 3e8fc74c..d6107b04 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_node_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_node_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: zorums.proto @@ -19,10 +19,22 @@ const ( // ZorumsServiceNode holds the node specific methods for the ZorumsService service. type ZorumsServiceNode struct { - *gorums.RawNode + node *gorums.Node +} + +func ZorumsServiceNodeRpc(node *gorums.Node) ZorumsServiceNode { + return ZorumsServiceNode{ + node: node, + } } // ZorumsNoQCServiceNode holds the node specific methods for the ZorumsNoQCService service. type ZorumsNoQCServiceNode struct { - *gorums.RawNode + node *gorums.Node +} + +func ZorumsNoQCServiceNodeRpc(node *gorums.Node) ZorumsNoQCServiceNode { + return ZorumsNoQCServiceNode{ + node: node, + } } 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 984ec5d5..fcd3fc1d 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_quorumcall_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_quorumcall_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: zorums.proto @@ -21,18 +21,19 @@ const ( ) // QuorumCall plain. -func (c *ZorumsServiceConfiguration) QuorumCall(ctx context.Context, in *Request) gorums.Responses[*Response] { +func (c ZorumsServiceConfiguration) QuorumCall(ctx context.Context, in *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.QuorumCall", ServerStream: false, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // QuorumCall with per_node_arg option. -func (c *ZorumsServiceConfiguration) QuorumCallPerNodeArg(ctx context.Context, in *Request, f func(*Request, uint32) *Request) gorums.Responses[*Response] { +func (c ZorumsServiceConfiguration) QuorumCallPerNodeArg(ctx context.Context, in *Request, f func(*Request, uint32) *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.QuorumCallPerNodeArg", @@ -42,44 +43,48 @@ func (c *ZorumsServiceConfiguration) QuorumCallPerNodeArg(ctx context.Context, i return f(req.(*Request), nid) } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // QuorumCallEmpty for testing imported message type. -func (c *ZorumsServiceConfiguration) QuorumCallEmpty(ctx context.Context, in *emptypb.Empty) gorums.Responses[*Response] { +func (c ZorumsServiceConfiguration) QuorumCallEmpty(ctx context.Context, in *emptypb.Empty) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.QuorumCallEmpty", ServerStream: false, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // QuorumCallEmpty2 for testing imported message type. -func (c *ZorumsServiceConfiguration) QuorumCallEmpty2(ctx context.Context, in *Request) gorums.Responses[*emptypb.Empty] { +func (c ZorumsServiceConfiguration) QuorumCallEmpty2(ctx context.Context, in *Request) gorums.Responses[*emptypb.Empty] { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.QuorumCallEmpty2", ServerStream: false, } - return gorums.QuorumCall[*emptypb.Empty](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*emptypb.Empty](responses) } // QuorumCall plain. -func (c *ZorumsServiceConfiguration) QuorumCallStream(ctx context.Context, in *Request) gorums.Responses[*Response] { +func (c ZorumsServiceConfiguration) QuorumCallStream(ctx context.Context, in *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.QuorumCallStream", ServerStream: true, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // QuorumCall with per_node_arg option. -func (c *ZorumsServiceConfiguration) QuorumCallStreamPerNodeArg(ctx context.Context, in *Request, f func(*Request, uint32) *Request) gorums.Responses[*Response] { +func (c ZorumsServiceConfiguration) QuorumCallStreamPerNodeArg(ctx context.Context, in *Request, f func(*Request, uint32) *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.QuorumCallStreamPerNodeArg", @@ -89,27 +94,30 @@ func (c *ZorumsServiceConfiguration) QuorumCallStreamPerNodeArg(ctx context.Cont return f(req.(*Request), nid) } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // QuorumCallEmpty for testing imported message type. -func (c *ZorumsServiceConfiguration) QuorumCallStreamEmpty(ctx context.Context, in *emptypb.Empty) gorums.Responses[*Response] { +func (c ZorumsServiceConfiguration) QuorumCallStreamEmpty(ctx context.Context, in *emptypb.Empty) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.QuorumCallStreamEmpty", ServerStream: true, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // QuorumCallEmpty2 for testing imported message type. -func (c *ZorumsServiceConfiguration) QuorumCallStreamEmpty2(ctx context.Context, in *Request) gorums.Responses[*emptypb.Empty] { +func (c ZorumsServiceConfiguration) QuorumCallStreamEmpty2(ctx context.Context, in *Request) gorums.Responses[*emptypb.Empty] { cd := gorums.QuorumCallData{ Message: in, Method: "dev.ZorumsService.QuorumCallStreamEmpty2", ServerStream: true, } - return gorums.QuorumCall[*emptypb.Empty](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*emptypb.Empty](responses) } 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 e0b7de93..6f9303de 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_rpc_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_rpc_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: zorums.proto @@ -20,13 +20,13 @@ const ( // GRPCCall plain gRPC call; testing that Gorums can ignore these, but that // they are added to the _grpc.pb.go generated file. -func (n *ZorumsServiceNode) GRPCCall(ctx context.Context, in *Request) (resp *Response, err error) { +func (n ZorumsServiceNode) GRPCCall(ctx context.Context, in *Request) (resp *Response, err error) { cd := gorums.CallData{ Message: in, Method: "dev.ZorumsService.GRPCCall", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } @@ -35,13 +35,13 @@ func (n *ZorumsServiceNode) GRPCCall(ctx context.Context, in *Request) (resp *Re // GRPCCall plain gRPC call; testing that Gorums can ignore these, but that // they are added to the _grpc.pb.go generated file. -func (n *ZorumsNoQCServiceNode) GRPCCall(ctx context.Context, in *Request) (resp *Response, err error) { +func (n ZorumsNoQCServiceNode) GRPCCall(ctx context.Context, in *Request) (resp *Response, err error) { cd := gorums.CallData{ Message: in, Method: "dev.ZorumsNoQCService.GRPCCall", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } 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 234d3e6f..e04c0c49 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_server_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_server_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: zorums.proto 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 e88ad623..2bc09469 100644 --- a/cmd/protoc-gen-gorums/dev/zorums_unicast_gorums.pb.go +++ b/cmd/protoc-gen-gorums/dev/zorums_unicast_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: zorums.proto @@ -20,47 +20,47 @@ const ( ) // Unicast is a one-way call; no replies are processed. -func (n *ZorumsServiceNode) Unicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (n ZorumsServiceNode) Unicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.CallData{ Message: in, Method: "dev.ZorumsService.Unicast", } - n.RawNode.Unicast(ctx, cd, opts...) + n.node.Unicast(ctx, cd, opts...) } // Reference imports to suppress errors if they are not otherwise used. var _ emptypb.Empty // Unicast2 is a one-way call; no replies are processed. -func (n *ZorumsServiceNode) Unicast2(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (n ZorumsServiceNode) Unicast2(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.CallData{ Message: in, Method: "dev.ZorumsService.Unicast2", } - n.RawNode.Unicast(ctx, cd, opts...) + n.node.Unicast(ctx, cd, opts...) } // Unicast is a one-way call; no replies are processed. -func (n *ZorumsNoQCServiceNode) Unicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (n ZorumsNoQCServiceNode) Unicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.CallData{ Message: in, Method: "dev.ZorumsNoQCService.Unicast", } - n.RawNode.Unicast(ctx, cd, opts...) + n.node.Unicast(ctx, cd, opts...) } // Reference imports to suppress errors if they are not otherwise used. var _ emptypb.Empty // Unicast2 is a one-way call; no replies are processed. -func (n *ZorumsNoQCServiceNode) Unicast2(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (n ZorumsNoQCServiceNode) Unicast2(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.CallData{ Message: in, Method: "dev.ZorumsNoQCService.Unicast2", } - n.RawNode.Unicast(ctx, cd, opts...) + n.node.Unicast(ctx, cd, opts...) } diff --git a/cmd/protoc-gen-gorums/gengorums/template_configuration.go b/cmd/protoc-gen-gorums/gengorums/template_configuration.go index c0f7b062..0f8681df 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_configuration.go +++ b/cmd/protoc-gen-gorums/gengorums/template_configuration.go @@ -1,129 +1,26 @@ package gengorums -var configurationVars = ` -{{- $rawConfiguration := use "gorums.RawConfiguration" .GenFile}} +var configuration = ` +{{- $configuration := use "gorums.Configuration" .GenFile}} {{- $nodeListOptions := use "gorums.NodeListOption" .GenFile}} -` - -var configurationServicesBegin = ` {{- $genFile := .GenFile}} {{- range .Services}} {{- $service := .GoName}} {{- $configurationName := printf "%sConfiguration" $service}} {{- $nodeName := printf "%sNode" $service}} -` - -var configurationServicesEnd = ` -{{- end}} -` - -var configurationStruct = ` -// A {{$configurationName}} represents a static set of nodes on which quorum remote -// procedure calls may be invoked. -{{- reserveName $configurationName}} -type {{$configurationName}} struct { - {{$rawConfiguration}} -} -` - -var newConfiguration = ` -{{- $funcName := printf "New%s" $configurationName}} -// {{$funcName}} returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -{{- reserveName $funcName}} -func {{$funcName}}(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *{{$configurationName}}, err error) { - c = &{{$configurationName}}{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err - } - return c, nil -} -` - -var subConfiguration = ` -{{- $methodName := printf "Sub%s" $configurationName}} -// {{$methodName}} allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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. -{{- reserveMethod $configurationName $methodName}} -func (c *{{$configurationName}}) {{$methodName}}(cfg gorums.NodeListOption) (subCfg *{{$configurationName}}, err error) { - subCfg = &{{$configurationName}}{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} -` - -var configurationFromRaw = ` -{{- $funcName := printf "%sFromRaw" $configurationName}} -// {{$funcName}} returns a new {{$configurationName}} from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -{{- reserveName $funcName}} -func {{$funcName}}(rawCfg {{$rawConfiguration}}) (*{{$configurationName}}, error) { - newCfg := &{{$configurationName}}{ - RawConfiguration: rawCfg, + // A {{$configurationName}} represents a static set of nodes on which quorum remote + // procedure calls may be invoked. + {{- reserveName $configurationName}} + type {{$configurationName}} struct { + cfg *{{$configuration}} } - return newCfg, nil -} -` -var configurationMethodsTemplate = ` -// 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. -// -// NOTE: mutating the returned slice is not supported. -{{- reserveMethod $configurationName "Nodes"}} -func (c *{{$configurationName}}) Nodes() []*{{$nodeName}} { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*{{$nodeName}}, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &{{$nodeName}}{n} + {{$funcName := printf "%sRpc" $configurationName}} + {{- reserveName $funcName}} + func {{$funcName}}(cfg *{{$configuration}}) {{$configurationName}} { + return {{$configurationName}}{ + cfg: cfg, + } } - return nodes -} - -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -{{- reserveMethod $configurationName "AllNodes"}} -func (c *{{$configurationName}}) AllNodes() []*{{$nodeName}} { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*{{$nodeName}}, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &{{$nodeName}}{n} - } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -{{- reserveMethod $configurationName "And"}} -func (c {{$configurationName}}) And(d *{{$configurationName}}) {{$nodeListOptions}} { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -{{- reserveMethod $configurationName "Except"}} -func (c {{$configurationName}}) Except(rm *{{$configurationName}}) {{$nodeListOptions}} { - return c.RawConfiguration.Except(rm.RawConfiguration) -} +{{- end}} ` - -var configuration = configurationVars + - configurationServicesBegin + - configurationStruct + newConfiguration + subConfiguration + configurationFromRaw + configurationMethodsTemplate + - configurationServicesEnd diff --git a/cmd/protoc-gen-gorums/gengorums/template_multicast.go b/cmd/protoc-gen-gorums/gengorums/template_multicast.go index c468ee28..ba78a4e5 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_multicast.go +++ b/cmd/protoc-gen-gorums/gengorums/template_multicast.go @@ -17,7 +17,7 @@ var mcVar = ` var multicastReserve = `{{reserveMethod $configurationName $method}}` -var multicastSignature = `func (c *{{$configurationName}}) {{$method}}(` + +var multicastSignature = `func (c {{$configurationName}}) {{$method}}(` + `ctx {{$context}}, in *{{$in}}` + `{{perNodeFnType .GenFile .Method ", f"}},` + `opts ...{{$callOpt}}) { @@ -35,7 +35,7 @@ var multicastBody = ` } {{- end}} - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } ` diff --git a/cmd/protoc-gen-gorums/gengorums/template_node.go b/cmd/protoc-gen-gorums/gengorums/template_node.go index fadaeee2..ac0da947 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_node.go +++ b/cmd/protoc-gen-gorums/gengorums/template_node.go @@ -1,12 +1,9 @@ package gengorums -// gorums need to be imported in the zorums file -var nodeVariables = ` +var node = ` {{- $_ := use "gorums.EnforceVersion" .GenFile}} {{- $callOpt := use "gorums.CallOption" .GenFile}} -` - -var nodeStructs = ` +{{- $node := use "gorums.Node" .GenFile}} {{- $genFile := .GenFile}} {{- range .Services}} {{- $service := .GoName}} @@ -14,9 +11,15 @@ var nodeStructs = ` // {{$nodeName}} holds the node specific methods for the {{$service}} service. {{- reserveName $nodeName}} type {{$nodeName}} struct { - *gorums.RawNode + node *{{$node}} + } + + {{$funcName := printf "%sRpc" $nodeName}} + {{- reserveName $funcName}} + func {{$funcName}}(node *{{$node}}) {{$nodeName}} { + return {{$nodeName}}{ + node: node, + } } {{- end}} ` - -var node = nodeVariables + nodeStructs diff --git a/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go b/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go index b72b937c..c706cdd4 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go +++ b/cmd/protoc-gen-gorums/gengorums/template_quorumcall.go @@ -10,6 +10,7 @@ var commonVariables = ` {{- $unexportOutput := unexport .Method.Output.GoIdent.GoName}} {{- $service := .Method.Parent.GoName}} {{- $nodeName := printf "%sNode" $service}} +{{- $configuration := use "gorums.Configuration" .GenFile}} {{- $configurationName := printf "%sConfiguration" $service}} ` @@ -41,7 +42,7 @@ var quorumCallComment = ` var quorumCallReserve = `{{reserveMethod $configurationName $method}}` -var quorumCallSignature = `func (c *{{$configurationName}}) {{$method}}(` + +var quorumCallSignature = `func (c {{$configurationName}}) {{$method}}(` + `ctx {{$context}}, in *{{$in}}` + `{{perNodeFnType .GenFile .Method ", f"}})` + `{{$iterator}}[*{{$out}}] { @@ -66,7 +67,8 @@ var quorumCallBody = ` cd := {{$callData}}{ } {{- end}} - return gorums.QuorumCall[*{{$out}}](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*{{$out}}](responses) } ` diff --git a/cmd/protoc-gen-gorums/gengorums/template_rpc.go b/cmd/protoc-gen-gorums/gengorums/template_rpc.go index 5f829679..1bc4235f 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_rpc.go +++ b/cmd/protoc-gen-gorums/gengorums/template_rpc.go @@ -5,11 +5,12 @@ var rpcVar = ` {{- $genFile := .GenFile}} {{- $unexportMethod := unexport .Method.GoName}} {{- $context := use "context.Context" .GenFile}} + {{- $node := use "gorums.Node" .GenFile}} ` var rpcReserve = `{{reserveMethod $nodeName $method}}` -var rpcSignature = `func (n *{{$nodeName}}) {{$method}}(` + +var rpcSignature = `func (n {{$nodeName}}) {{$method}}(` + `ctx {{$context}}, in *{{$in}}` + `{{perNodeFnType .GenFile .Method ", f"}}) (resp *{{$out}}, err error) { ` @@ -25,7 +26,7 @@ var rpcBody = ` cd := {{$callData}}{ } {{- end}} - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } diff --git a/cmd/protoc-gen-gorums/gengorums/template_unicast.go b/cmd/protoc-gen-gorums/gengorums/template_unicast.go index 044a2272..6e598f1c 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_unicast.go +++ b/cmd/protoc-gen-gorums/gengorums/template_unicast.go @@ -1,8 +1,10 @@ package gengorums var unicastVar = rpcVar + ` - {{$callOpt := use "gorums.CallOption" .GenFile}} - {{$nodeName := printf "%sNode" .Method.Parent.GoName}} +{{- $callOpt := use "gorums.CallOption" .GenFile}} +{{- $node := use "gorums.Node" .GenFile}} +{{- $nodeName := printf "%sNode" .Method.Parent.GoName}} +{{- $node := use "gorums.Node" .GenFile}} ` var unicastCallComment = ` @@ -14,7 +16,7 @@ var unicastCallComment = ` {{end -}} ` -var unicastSignature = `func (n *{{$nodeName}}) {{$method}}(` + +var unicastSignature = `func (n {{$nodeName}}) {{$method}}(` + `ctx {{$context}}, in *{{$in}}, opts ...{{$callOpt}}) { ` @@ -23,7 +25,7 @@ var unicastBody = ` cd := {{$callData}}{ Method: "{{$fullName}}", } - n.RawNode.Unicast(ctx, cd, opts...) + n.node.Unicast(ctx, cd, opts...) } ` diff --git a/config.go b/config.go index ba15b4e0..b0567e2d 100644 --- a/config.go +++ b/config.go @@ -4,44 +4,50 @@ import ( "fmt" ) -// RawConfiguration represents a static set of nodes on which quorum calls may be invoked. +// Configuration represents a static set of nodes on which quorum calls may be invoked. // // NOTE: mutating the configuration is not supported. // // This type is intended to be used by generated code. // You should use the generated `Configuration` type instead. -type RawConfiguration struct { - RawNodes []*RawNode - mgr *manager +type Configuration struct { + nodes []*Node + mgr *manager } -// NewRawConfiguration returns a configuration based on the provided list of nodes. +// NewConfiguration returns a configuration based on the provided list of nodes. // Nodes can be supplied using WithNodeMap or WithNodeList. -func NewRawConfiguration(nodes NodeListOption, opts ...ManagerOption) (cfg RawConfiguration, err error) { +func NewConfiguration(nodes NodeListOption, opts ...ManagerOption) (cfg *Configuration, err error) { if nodes == nil { - return RawConfiguration{}, fmt.Errorf("config: missing required node list") + return nil, fmt.Errorf("config: missing required node list") } mgr := newManager(opts...) cfg, err = nodes.newConfig(mgr) - for _, n := range cfg.RawNodes { + if err != nil { + return nil, err + } + for _, n := range cfg.nodes { n.obtain() } return cfg, err } -// SubRawConfiguration returns a configuration from another configuration and a list of nodes. +// SubConfiguration returns a configuration from another configuration and a 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 (c *RawConfiguration) SubRawConfiguration(nodes NodeListOption) (cfg RawConfiguration, err error) { +func (c *Configuration) SubConfiguration(nodes NodeListOption) (cfg *Configuration, err error) { if nodes == nil { - return RawConfiguration{}, fmt.Errorf("config: missing required node list") + return nil, fmt.Errorf("config: missing required node list") } cfg, err = nodes.newConfig(c.mgr) - for _, n := range cfg.RawNodes { + if err != nil { + return nil, err + } + for _, n := range cfg.nodes { n.obtain() } @@ -49,28 +55,28 @@ func (c *RawConfiguration) SubRawConfiguration(nodes NodeListOption) (cfg RawCon } // CloseAll closes the configurations and all of its subconfigurations -func (c *RawConfiguration) CloseAll() error { +func (c *Configuration) CloseAll() error { c.mgr.close() return nil } // Close closes the nodes which are not part of another subconfiguration // it is only meant to be used once -func (c *RawConfiguration) Close() error { - for _, n := range c.RawNodes { +func (c *Configuration) Close() error { + for _, n := range c.nodes { err := n.release() if err != nil { return err } } - *c = RawConfiguration{} + *c = Configuration{} return nil } // NodeIDs returns a slice of this configuration's Node IDs. -func (c RawConfiguration) NodeIDs() []uint32 { - ids := make([]uint32, len(c.RawNodes)) - for i, node := range c.RawNodes { +func (c Configuration) NodeIDs() []uint32 { + ids := make([]uint32, len(c.nodes)) + for i, node := range c.nodes { ids[i] = node.ID() } return ids @@ -79,38 +85,38 @@ 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.RawNodes +func (c Configuration) Nodes() []*Node { + return c.nodes } // AllNodes returns the nodes in this configuration and all subconfigurations. -func (c RawConfiguration) AllNodeIDs() []uint32 { +func (c Configuration) AllNodeIDs() []uint32 { return c.mgr.nodeIDs() } // AllNodes returns the nodes in this configuration and all subconfigurations. -func (c RawConfiguration) AllNodes() []*RawNode { +func (c Configuration) AllNodes() []*Node { return c.mgr.getNodes() } // Size returns the number of nodes in this configuration. -func (c RawConfiguration) Size() int { - return len(c.RawNodes) +func (c Configuration) 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.RawNodes) != len(b.RawNodes) { +func (c Configuration) Equal(b Configuration) bool { + if len(c.nodes) != len(b.nodes) { return false } - for i := range c.RawNodes { - if c.RawNodes[i].ID() != b.RawNodes[i].ID() { + for i := range c.nodes { + if c.nodes[i].ID() != b.nodes[i].ID() { return false } } return true } -func (c RawConfiguration) getMsgID() uint64 { - return c.RawNodes[0].mgr.getMsgID() +func (c Configuration) getMsgID() uint64 { + return c.nodes[0].mgr.getMsgID() } diff --git a/config_opts.go b/config_opts.go index b7a6ab8c..23e0959d 100644 --- a/config_opts.go +++ b/config_opts.go @@ -10,27 +10,27 @@ type ConfigOption any // NodeListOption must be implemented by node providers. type NodeListOption interface { ConfigOption - newConfig(*manager) (RawConfiguration, error) + newConfig(*manager) (*Configuration, error) } type nodeIDMap struct { idMap map[string]uint32 } -func (o nodeIDMap) newConfig(mgr *manager) (cfg RawConfiguration, err error) { +func (o nodeIDMap) newConfig(mgr *manager) (cfg *Configuration, err error) { if len(o.idMap) == 0 { - return RawConfiguration{}, fmt.Errorf("config: missing required node map") + return nil, fmt.Errorf("config: missing required node map") } - nodes := make([]*RawNode, 0, len(o.idMap)) + nodes := make([]*Node, 0, len(o.idMap)) for naddr, id := range o.idMap { node, found := mgr.node(id) if !found { - node, err = NewRawNodeWithID(naddr, id) + node, err = NewNodeWithID(naddr, id) if err != nil { - return RawConfiguration{}, err + return nil, err } if err = mgr.addNode(node); err != nil { - return RawConfiguration{}, err + return nil, err } } nodes = append(nodes, node) @@ -39,9 +39,9 @@ func (o nodeIDMap) newConfig(mgr *manager) (cfg RawConfiguration, err error) { OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) - cfg = RawConfiguration{ - RawNodes: nodes, - mgr: mgr, + cfg = &Configuration{ + nodes: nodes, + mgr: mgr, } return cfg, nil @@ -57,19 +57,19 @@ type nodeList struct { addrsList []string } -func (o nodeList) newConfig(mgr *manager) (cfg RawConfiguration, err error) { +func (o nodeList) newConfig(mgr *manager) (cfg *Configuration, err error) { if len(o.addrsList) == 0 { - return RawConfiguration{}, fmt.Errorf("config: missing required node addresses") + return nil, fmt.Errorf("config: missing required node addresses") } - nodes := make([]*RawNode, 0, len(o.addrsList)) + nodes := make([]*Node, 0, len(o.addrsList)) for _, naddr := range o.addrsList { - node, err := NewRawNode(naddr) + node, err := NewNode(naddr) if err != nil { - return RawConfiguration{}, err + return nil, err } if n, found := mgr.node(node.ID()); !found { if err = mgr.addNode(node); err != nil { - return RawConfiguration{}, err + return nil, err } } else { node = n @@ -80,9 +80,9 @@ func (o nodeList) newConfig(mgr *manager) (cfg RawConfiguration, err error) { OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) - cfg = RawConfiguration{ - RawNodes: nodes, - mgr: mgr, + cfg = &Configuration{ + nodes: nodes, + mgr: mgr, } return cfg, nil @@ -98,16 +98,16 @@ type nodeIDs struct { nodeIDs []uint32 } -func (o nodeIDs) newConfig(mgr *manager) (cfg RawConfiguration, err error) { +func (o nodeIDs) newConfig(mgr *manager) (cfg *Configuration, err error) { if len(o.nodeIDs) == 0 { - return RawConfiguration{}, fmt.Errorf("config: missing required node IDs") + return nil, fmt.Errorf("config: missing required node IDs") } - nodes := make([]*RawNode, 0, len(o.nodeIDs)) + nodes := make([]*Node, 0, len(o.nodeIDs)) for _, id := range o.nodeIDs { node, found := mgr.node(id) if !found { // Node IDs must have been registered previously - return RawConfiguration{}, fmt.Errorf("config: node %d not found", id) + return nil, fmt.Errorf("config: node %d not found", id) } nodes = append(nodes, node) } @@ -115,9 +115,9 @@ func (o nodeIDs) newConfig(mgr *manager) (cfg RawConfiguration, err error) { OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) - cfg = RawConfiguration{ - RawNodes: nodes, - mgr: mgr, + cfg = &Configuration{ + nodes: nodes, + mgr: mgr, } return cfg, nil @@ -130,14 +130,14 @@ func WithNodeIDs(ids []uint32) NodeListOption { } type addNodes struct { - old RawConfiguration + old *Configuration new NodeListOption } -func (o addNodes) newConfig(mgr *manager) (nodes RawConfiguration, err error) { +func (o addNodes) newConfig(mgr *manager) (nodes *Configuration, err error) { newNodes, err := o.new.newConfig(mgr) if err != nil { - return RawConfiguration{}, err + return nil, err } ac := &addConfig{old: o.old, add: newNodes} return ac.newConfig(mgr) @@ -145,19 +145,19 @@ func (o addNodes) newConfig(mgr *manager) (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 { +func (c *Configuration) WithNewNodes(new NodeListOption) NodeListOption { return &addNodes{old: c, new: new} } type addConfig struct { - old RawConfiguration - add RawConfiguration + old *Configuration + add *Configuration } -func (o addConfig) newConfig(mgr *manager) (cfg RawConfiguration, err error) { - nodes := make([]*RawNode, 0, len(o.old.RawNodes)+len(o.add.RawNodes)) +func (o addConfig) newConfig(mgr *manager) (cfg *Configuration, err error) { + nodes := make([]*Node, 0, len(o.old.nodes)+len(o.add.nodes)) m := make(map[uint32]bool) - for _, n := range append(o.old.RawNodes, o.add.RawNodes...) { + for _, n := range append(o.old.nodes, o.add.nodes...) { if !m[n.id] { m[n.id] = true nodes = append(nodes, n) @@ -167,28 +167,28 @@ func (o addConfig) newConfig(mgr *manager) (cfg RawConfiguration, err error) { OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) - cfg = RawConfiguration{ - RawNodes: nodes, - mgr: mgr, + cfg = &Configuration{ + nodes: nodes, + mgr: mgr, } return cfg, 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 { +func (c *Configuration) And(d *Configuration) NodeListOption { return &addConfig{old: c, add: d} } // 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 *Configuration) WithoutNodes(ids ...uint32) NodeListOption { rmIDs := make(map[uint32]bool) for _, id := range ids { rmIDs[id] = true } - keepIDs := make([]uint32, 0, len(c.RawNodes)) - for _, cNode := range c.RawNodes { + keepIDs := make([]uint32, 0, len(c.nodes)) + for _, cNode := range c.nodes { if !rmIDs[cNode.id] { keepIDs = append(keepIDs, cNode.id) } @@ -198,13 +198,13 @@ 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 *Configuration) Except(rm *Configuration) NodeListOption { rmIDs := make(map[uint32]bool) - for _, rmNode := range rm.RawNodes { + for _, rmNode := range rm.nodes { rmIDs[rmNode.id] = true } - keepIDs := make([]uint32, 0, len(c.RawNodes)) - for _, cNode := range c.RawNodes { + keepIDs := make([]uint32, 0, len(c.nodes)) + for _, cNode := range c.nodes { if !rmIDs[cNode.id] { keepIDs = append(keepIDs, cNode.id) } diff --git a/config_test.go b/config_test.go index 13edf0fc..9692cb82 100644 --- a/config_test.go +++ b/config_test.go @@ -19,7 +19,7 @@ var ( func TestNewConfigurationEmptyNodeList(t *testing.T) { wantErr := errors.New("config: missing required node addresses") - _, err := gorums.NewRawConfiguration(gorums.WithNodeList([]string{})) + _, err := gorums.NewConfiguration(gorums.WithNodeList([]string{})) if err == nil { t.Fatalf("Expected error, got: %v, want: %v", err, wantErr) } @@ -29,7 +29,7 @@ func TestNewConfigurationEmptyNodeList(t *testing.T) { } func TestNewConfigurationNodeList(t *testing.T) { - cfg, err := gorums.NewRawConfiguration(gorums.WithNodeList(nodes)) + cfg, err := gorums.NewConfiguration(gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } @@ -37,7 +37,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 []*gorums.Node, addr string) bool { for _, node := range nodes { if addr == node.Address() { return true @@ -64,7 +64,7 @@ func TestNewConfigurationNodeList(t *testing.T) { } func TestNewConfigurationNodeMap(t *testing.T) { - cfg, err := gorums.NewRawConfiguration(gorums.WithNodeMap(nodeMap)) + cfg, err := gorums.NewConfiguration(gorums.WithNodeMap(nodeMap)) if err != nil { t.Fatal(err) } @@ -87,7 +87,7 @@ func TestNewConfigurationNodeMap(t *testing.T) { } func TestNewConfigurationNodeIDs(t *testing.T) { - c1, err := gorums.NewRawConfiguration(gorums.WithNodeList(nodes), gorums.WithNoConnect()) + c1, err := gorums.NewConfiguration(gorums.WithNodeList(nodes), gorums.WithNoConnect()) if err != nil { t.Fatal(err) } @@ -97,7 +97,7 @@ func TestNewConfigurationNodeIDs(t *testing.T) { // Identical configurations c1 == c2 nodeIDs := c1.NodeIDs() - c2, err := c1.SubRawConfiguration(gorums.WithNodeIDs(nodeIDs)) + c2, err := c1.SubConfiguration(gorums.WithNodeIDs(nodeIDs)) if err != nil { t.Fatal(err) } @@ -109,7 +109,7 @@ func TestNewConfigurationNodeIDs(t *testing.T) { } // Configuration with one less node |c3| == |c1| - 1 - c3, err := c1.SubRawConfiguration(gorums.WithNodeIDs(nodeIDs[:len(nodeIDs)-1])) + c3, err := c1.SubConfiguration(gorums.WithNodeIDs(nodeIDs[:len(nodeIDs)-1])) if err != nil { t.Fatal(err) } @@ -122,19 +122,19 @@ func TestNewConfigurationNodeIDs(t *testing.T) { } func TestNewConfigurationAnd(t *testing.T) { - c1, err := gorums.NewRawConfiguration(gorums.WithNodeList(nodes)) + c1, err := gorums.NewConfiguration(gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } c2Nodes := []string{"127.0.0.1:8080"} - c2, err := c1.SubRawConfiguration(gorums.WithNodeList(c2Nodes)) + c2, err := c1.SubConfiguration(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 := c1.SubRawConfiguration( + c3, err := c1.SubConfiguration( c1.WithNewNodes(gorums.WithNodeList(newNodes)), ) if err != nil { @@ -145,7 +145,7 @@ func TestNewConfigurationAnd(t *testing.T) { } // Combine c2 to c1, giving a new c4 with a total of 3+1 nodes - c4, err := c1.SubRawConfiguration( + c4, err := c1.SubConfiguration( c1.And(c2), ) if err != nil { @@ -158,7 +158,7 @@ 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 := c1.SubRawConfiguration( + c5, err := c1.SubConfiguration( c4.And(c2), ) if err != nil { @@ -170,11 +170,11 @@ func TestNewConfigurationAnd(t *testing.T) { } func TestNewConfigurationExcept(t *testing.T) { - c1, err := gorums.NewRawConfiguration(gorums.WithNodeList(nodes)) + c1, err := gorums.NewConfiguration(gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } - c2, err := c1.SubRawConfiguration( + c2, err := c1.SubConfiguration( c1.WithoutNodes(c1.Nodes()[0].ID()), ) if err != nil { @@ -185,13 +185,13 @@ func TestNewConfigurationExcept(t *testing.T) { } newNodes := []string{"127.0.0.1:9083", "127.0.0.1:9084"} - c3, err := c1.SubRawConfiguration( + c3, err := c1.SubConfiguration( c1.WithNewNodes(gorums.WithNodeList(newNodes)), ) if err != nil { t.Fatal(err) } - c4, err := c1.SubRawConfiguration( + c4, err := c1.SubConfiguration( c3.Except(c1), ) if err != nil { @@ -208,7 +208,7 @@ func TestConfigConcurrentAccess(t *testing.T) { }) defer teardown() - cfg, err := dummy.NewDummyConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) + cfg, err := gorums.NewConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } @@ -218,7 +218,7 @@ func TestConfigConcurrentAccess(t *testing.T) { for range 2 { wg.Add(1) go func() { - node := cfg.Nodes()[0] + node := dummy.DummyNodeRpc(cfg.Nodes()[0]) _, err := node.Test(context.Background(), &dummy.Empty{}) if err != nil { errCh <- err @@ -239,24 +239,24 @@ func TestConfigRelease(t *testing.T) { }) defer teardown() - c1, err := dummy.NewDummyConfiguration(gorums.WithNodeList(addrs[:3]), gorumsTestMgrOpts()...) + c1, err := gorums.NewConfiguration(gorums.WithNodeList(addrs[:3]), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } - c2, err := c1.SubDummyConfiguration(gorums.WithNodeList(addrs[2:8])) + c2, err := c1.SubConfiguration(gorums.WithNodeList(addrs[2:8])) if err != nil { t.Fatal(err) } - c3, err := c1.SubDummyConfiguration(gorums.WithNodeList(addrs[5:])) + c3, err := c1.SubConfiguration(gorums.WithNodeList(addrs[5:])) if err != nil { t.Fatal(err) } allNodes := c2.AllNodeIDs() - c1c2, err := c2.SubDummyConfiguration(c1.And(c2)) + c1c2, err := c2.SubConfiguration(c1.And(c2)) if err != nil { t.Fatal(err) } diff --git a/examples/storage/client.go b/examples/storage/client.go index 04e67bc0..976b4f8e 100644 --- a/examples/storage/client.go +++ b/examples/storage/client.go @@ -5,7 +5,6 @@ import ( "log" "github.com/relab/gorums" - "github.com/relab/gorums/examples/storage/proto" pb "github.com/relab/gorums/examples/storage/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -17,7 +16,7 @@ func runClient(addresses []string) error { } // create configuration containing all nodes - cfg, err := proto.NewStorageConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeList(addresses), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), // disable TLS @@ -31,7 +30,7 @@ func runClient(addresses []string) error { } // newestValue returns the reply that had the most recent timestamp -func newestValueOfTwo(v1, v2 *proto.ReadResponse) *proto.ReadResponse { +func newestValueOfTwo(v1, v2 *pb.ReadResponse) *pb.ReadResponse { if v1 == nil { return v2 } else if v2 == nil { @@ -45,7 +44,7 @@ func newestValueOfTwo(v1, v2 *proto.ReadResponse) *proto.ReadResponse { } // numUpdated returns the number of replicas that updated their value -func isUpdated(r *proto.WriteResponse) bool { +func isUpdated(r *pb.WriteResponse) bool { return r.GetNew() } @@ -58,12 +57,12 @@ func writeQF(replies gorums.Responses[*pb.WriteResponse], cfgSize int) (*pb.Writ updated++ } if updated > cfgSize/2 { - return proto.WriteResponse_builder{New: true}.Build(), nil + return pb.WriteResponse_builder{New: true}.Build(), nil } // if all replicas have responded, there must have been another write before ours // that had a newer timestamp if replyCount == cfgSize { - return proto.WriteResponse_builder{New: false}.Build(), nil + return pb.WriteResponse_builder{New: false}.Build(), nil } } return nil, errors.New("storage.writeqc: incomplete response") diff --git a/examples/storage/proto/storage_gorums.pb.go b/examples/storage/proto/storage_gorums.pb.go index 388e8e0e..6e5cdf39 100644 --- a/examples/storage/proto/storage_gorums.pb.go +++ b/examples/storage/proto/storage_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: storage/proto/storage.proto @@ -48,86 +48,13 @@ var _ StorageNodeClient = (*StorageNode)(nil) // A StorageConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type StorageConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewStorageConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewStorageConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *StorageConfiguration, err error) { - c = &StorageConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err - } - return c, nil -} - -// SubStorageConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *StorageConfiguration) SubStorageConfiguration(cfg gorums.NodeListOption) (subCfg *StorageConfiguration, err error) { - subCfg = &StorageConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// StorageConfigurationFromRaw returns a new StorageConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func StorageConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*StorageConfiguration, error) { - newCfg := &StorageConfiguration{ - RawConfiguration: rawCfg, +func StorageConfigurationRpc(cfg *gorums.Configuration) StorageConfiguration { + return StorageConfiguration{ + cfg: cfg, } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *StorageConfiguration) Nodes() []*StorageNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*StorageNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &StorageNode{n} - } - return nodes -} - -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *StorageConfiguration) AllNodes() []*StorageNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*StorageNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &StorageNode{n} - } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c StorageConfiguration) And(d *StorageConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c StorageConfiguration) Except(rm *StorageConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) } // Reference imports to suppress errors if they are not otherwise used. @@ -136,53 +63,61 @@ var _ emptypb.Empty // WriteMulticast is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *StorageConfiguration) WriteMulticast(ctx context.Context, in *WriteRequest, opts ...gorums.CallOption) { +func (c StorageConfiguration) WriteMulticast(ctx context.Context, in *WriteRequest, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "storage.Storage.WriteMulticast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // StorageNode holds the node specific methods for the Storage service. type StorageNode struct { - *gorums.RawNode + node *gorums.Node +} + +func StorageNodeRpc(node *gorums.Node) StorageNode { + return StorageNode{ + node: node, + } } // ReadQC executes the Read Quorum Call on a configuration // of Nodes and returns the most recent value. -func (c *StorageConfiguration) ReadQC(ctx context.Context, in *ReadRequest) gorums.Responses[*ReadResponse] { +func (c StorageConfiguration) ReadQC(ctx context.Context, in *ReadRequest) gorums.Responses[*ReadResponse] { cd := gorums.QuorumCallData{ Message: in, Method: "storage.Storage.ReadQC", ServerStream: false, } - return gorums.QuorumCall[*ReadResponse](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*ReadResponse](responses) } // WriteQC executes the Write Quorum Call on a configuration // of Nodes and returns true if a majority of Nodes were updated. -func (c *StorageConfiguration) WriteQC(ctx context.Context, in *WriteRequest) gorums.Responses[*WriteResponse] { +func (c StorageConfiguration) WriteQC(ctx context.Context, in *WriteRequest) gorums.Responses[*WriteResponse] { cd := gorums.QuorumCallData{ Message: in, Method: "storage.Storage.WriteQC", ServerStream: false, } - return gorums.QuorumCall[*WriteResponse](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*WriteResponse](responses) } // ReadRPC executes the Read RPC on a single Node -func (n *StorageNode) ReadRPC(ctx context.Context, in *ReadRequest) (resp *ReadResponse, err error) { +func (n StorageNode) ReadRPC(ctx context.Context, in *ReadRequest) (resp *ReadResponse, err error) { cd := gorums.CallData{ Message: in, Method: "storage.Storage.ReadRPC", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } @@ -190,13 +125,13 @@ func (n *StorageNode) ReadRPC(ctx context.Context, in *ReadRequest) (resp *ReadR } // WriteRPC executes the Write RPC on a single Node -func (n *StorageNode) WriteRPC(ctx context.Context, in *WriteRequest) (resp *WriteResponse, err error) { +func (n StorageNode) WriteRPC(ctx context.Context, in *WriteRequest) (resp *WriteResponse, err error) { cd := gorums.CallData{ Message: in, Method: "storage.Storage.WriteRPC", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } diff --git a/examples/storage/repl.go b/examples/storage/repl.go index 2305200f..ca4e0497 100644 --- a/examples/storage/repl.go +++ b/examples/storage/repl.go @@ -52,11 +52,11 @@ The command performs the write quorum call on node 0 and 2 ` type repl struct { - cfg *pb.StorageConfiguration + cfg *gorums.Configuration term *term.Terminal } -func newRepl(cfg *pb.StorageConfiguration) *repl { +func newRepl(cfg *gorums.Configuration) *repl { return &repl{ cfg: cfg, term: term.NewTerminal(struct { @@ -88,7 +88,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(cfg *pb.StorageConfiguration) error { +func Repl(cfg *gorums.Configuration) error { r := newRepl(cfg) fmt.Println(help) @@ -171,8 +171,10 @@ func (r repl) multicast(args []string) { return } + cfgRpc := pb.StorageConfigurationRpc(r.cfg) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) - r.cfg.WriteMulticast(ctx, pb.WriteRequest_builder{Key: args[0], Value: args[1]}.Build()) + cfgRpc.WriteMulticast(ctx, pb.WriteRequest_builder{Key: args[0], Value: args[1]}.Build()) cancel() fmt.Println("Multicast OK: (server output not synchronized)") } @@ -208,13 +210,14 @@ func (r repl) qcCfg(args []string) { } } -func (repl) readRPC(args []string, node *pb.StorageNode) { +func (repl) readRPC(args []string, node *gorums.Node) { if len(args) < 1 { fmt.Println("Read requires a key to read.") return } + nodeRpc := pb.StorageNodeRpc(node) ctx, cancel := context.WithTimeout(context.Background(), time.Second) - resp, err := node.ReadRPC(ctx, pb.ReadRequest_builder{Key: args[0]}.Build()) + resp, err := nodeRpc.ReadRPC(ctx, pb.ReadRequest_builder{Key: args[0]}.Build()) cancel() if err != nil { fmt.Printf("Read RPC finished with error: %v\n", err) @@ -227,13 +230,16 @@ func (repl) readRPC(args []string, node *pb.StorageNode) { fmt.Printf("%s = %s\n", args[0], resp.GetValue()) } -func (repl) writeRPC(args []string, node *pb.StorageNode) { +func (repl) writeRPC(args []string, node *gorums.Node) { if len(args) < 2 { fmt.Println("Write requires a key and a value to write.") return } + + nodeRpc := pb.StorageNodeRpc(node) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) - resp, err := node.WriteRPC(ctx, pb.WriteRequest_builder{Key: args[0], Value: args[1], Time: timestamppb.Now()}.Build()) + resp, err := nodeRpc.WriteRPC(ctx, pb.WriteRequest_builder{Key: args[0], Value: args[1], Time: timestamppb.Now()}.Build()) cancel() if err != nil { fmt.Printf("Write RPC finished with error: %v\n", err) @@ -246,15 +252,17 @@ func (repl) writeRPC(args []string, node *pb.StorageNode) { fmt.Println("Write OK") } -func (repl) readQC(args []string, cfg *pb.StorageConfiguration) { +func (repl) readQC(args []string, cfg *gorums.Configuration) { if len(args) < 1 { fmt.Println("Read requires a key to read.") return } - ctx, cancel := context.WithTimeout(context.Background(), time.Second) + cfgRpc := pb.StorageConfigurationRpc(cfg) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) req := pb.ReadRequest_builder{Key: args[0]}.Build() - resp, err := readQF(cfg.ReadQC(ctx, req), cfg.Size()/2) + resp, err := readQF(cfgRpc.ReadQC(ctx, req), cfg.Size()/2) cancel() if err != nil { @@ -268,14 +276,17 @@ func (repl) readQC(args []string, cfg *pb.StorageConfiguration) { fmt.Printf("%s = %s\n", args[0], resp.GetValue()) } -func (repl) writeQC(args []string, cfg *pb.StorageConfiguration) { +func (repl) writeQC(args []string, cfg *gorums.Configuration) { if len(args) < 2 { fmt.Println("Write requires a key and a value to write.") return } + + cfgRpc := pb.StorageConfigurationRpc(cfg) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) req := pb.WriteRequest_builder{Key: args[0], Value: args[1], Time: timestamppb.Now()}.Build() - resp, err := writeQF(cfg.WriteQC(ctx, req), cfg.Size()) + resp, err := writeQF(cfgRpc.WriteQC(ctx, req), cfg.Size()) cancel() if err != nil { fmt.Printf("Write RPC finished with error: %v\n", err) @@ -288,7 +299,7 @@ func (repl) writeQC(args []string, cfg *pb.StorageConfiguration) { fmt.Println("Write OK") } -func (r repl) parseConfiguration(cfgStr string) (cfg *pb.StorageConfiguration) { +func (r repl) parseConfiguration(cfgStr string) (cfg *gorums.Configuration) { // configuration using range syntax if i := strings.Index(cfgStr, ":"); i > -1 { var start, stop int @@ -320,7 +331,7 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *pb.StorageConfiguration) { for _, node := range r.cfg.AllNodes()[start:stop] { nodes = append(nodes, node.Address()) } - cfg, err = r.cfg.SubStorageConfiguration(gorums.WithNodeList(nodes)) + cfg, err = r.cfg.SubConfiguration(gorums.WithNodeList(nodes)) if err != nil { fmt.Printf("Failed to create configuration: %v\n", err) return nil @@ -343,7 +354,7 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *pb.StorageConfiguration) { } selectedNodes = append(selectedNodes, nodes[i].Address()) } - cfg, err := r.cfg.SubStorageConfiguration(gorums.WithNodeList(selectedNodes)) + cfg, err := r.cfg.SubConfiguration(gorums.WithNodeList(selectedNodes)) if err != nil { fmt.Printf("Failed to create configuration: %v\n", err) return nil diff --git a/iterator.go b/iterator.go index 0482de12..ffa4a048 100644 --- a/iterator.go +++ b/iterator.go @@ -91,3 +91,15 @@ func (iterator Responses[messageType]) GetQuorum(quorum int) (messageType, error var noReply messageType return noReply, errors.New("quorum not found") } + +// IterTypeCast is used by generated code to +func IterTypeCast[cast proto.Message](iterator Responses[proto.Message]) Responses[cast] { + return func(yield func(Response[cast]) bool) { + for r := range iterator { + castResp := NewResponse(r.Msg.(cast), r.Err, r.Nid) + if !yield(castResp) { + return + } + } + } +} diff --git a/mgr.go b/mgr.go index 188b3531..a471fb17 100644 --- a/mgr.go +++ b/mgr.go @@ -19,8 +19,8 @@ import ( // You should use the generated `Manager` struct instead. type manager struct { mu sync.Mutex - nodes []*RawNode - lookup map[uint32]*RawNode + nodes []*Node + lookup map[uint32]*Node closeOnce sync.Once logger *log.Logger opts managerOptions @@ -33,7 +33,7 @@ type manager struct { // You should use the `NewManager` function in the generated code instead. func newManager(opts ...ManagerOption) *manager { m := &manager{ - lookup: make(map[uint32]*RawNode), + lookup: make(map[uint32]*Node), opts: newManagerOptions(), } for _, opt := range opts { @@ -88,7 +88,7 @@ func (m *manager) nodeIDs() []uint32 { } // Node returns the node with the given identifier if present. -func (m *manager) node(id uint32) (node *RawNode, found bool) { +func (m *manager) node(id uint32) (node *Node, found bool) { m.mu.Lock() defer m.mu.Unlock() node, found = m.lookup[id] @@ -97,7 +97,7 @@ func (m *manager) node(id uint32) (node *RawNode, found bool) { // 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. -func (m *manager) getNodes() []*RawNode { +func (m *manager) getNodes() []*Node { m.mu.Lock() defer m.mu.Unlock() return m.nodes @@ -113,7 +113,7 @@ func (m *manager) size() (nodes int) { // AddNode adds the node to the manager's node pool // and establishes a connection to the node. // should only be used by tests -func (m *manager) addNode(node *RawNode) error { +func (m *manager) addNode(node *Node) error { if _, found := m.node(node.ID()); found { // Node IDs must be unique return fmt.Errorf("config: node %d (%s) already exists", node.ID(), node.Address()) @@ -135,7 +135,7 @@ func (m *manager) addNode(node *RawNode) error { } // remove a node from a manager, the node is closed seperately -func (m *manager) removeNode(node *RawNode) error { +func (m *manager) removeNode(node *Node) error { if m.logger != nil { m.logger.Printf("Removing node %s with id %d\n", node, node.id) } @@ -146,7 +146,7 @@ func (m *manager) removeNode(node *RawNode) error { delete(m.lookup, node.id) // assume nodes are sorted - i, found := slices.BinarySearchFunc(m.nodes, node, func(n1, n2 *RawNode) int { + i, found := slices.BinarySearchFunc(m.nodes, node, func(n1, n2 *Node) int { return cmp.Compare(n1.id, n2.id) }) diff --git a/mgr_internal_test.go b/mgr_internal_test.go index cc35f36c..e47b2bfb 100644 --- a/mgr_internal_test.go +++ b/mgr_internal_test.go @@ -25,7 +25,7 @@ func TestManagerLogging(t *testing.T) { } func TestManagerAddNode(t *testing.T) { - cfg, err := NewRawConfiguration(WithNodeMap(mgrTestNodeMap), WithNoConnect()) + cfg, err := NewConfiguration(WithNodeMap(mgrTestNodeMap), WithNoConnect()) mgr := cfg.mgr if err != nil { t.Fatal(err) @@ -41,7 +41,7 @@ func TestManagerAddNode(t *testing.T) { {"127.0.1.1:1234", 2, "config: node 2 (127.0.1.1:1234) already exists"}, } for _, test := range tests { - node, err := NewRawNodeWithID(test.addr, test.id) + node, err := NewNodeWithID(test.addr, test.id) if err != nil { t.Fatal(err) } @@ -59,7 +59,7 @@ func TestManagerAddNodeWithConn(t *testing.T) { }) defer teardown() - cfg, err := NewRawConfiguration(WithNodeList(addrs[:2]), + cfg, err := NewConfiguration(WithNodeList(addrs[:2]), WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), @@ -75,7 +75,7 @@ func TestManagerAddNodeWithConn(t *testing.T) { t.Errorf("mgr.Size() = %d, expected %d", mgr.size(), len(addrs)-1) } - node, err := NewRawNode(addrs[2]) + node, err := NewNode(addrs[2]) if err != nil { t.Fatal(err) } diff --git a/multicast.go b/multicast.go index 5b0fb355..3a030e66 100644 --- a/multicast.go +++ b/multicast.go @@ -12,7 +12,7 @@ import ( // before the message has been sent. // // This method should be used by generated code only. -func (c RawConfiguration) Multicast(ctx context.Context, d QuorumCallData, opts ...CallOption) { +func (c Configuration) Multicast(ctx context.Context, d QuorumCallData, opts ...CallOption) { o := getCallOptions(E_Multicast, opts) md := ordering.NewGorumsMetadata(ctx, c.getMsgID(), d.Method) sentMsgs := 0 diff --git a/node.go b/node.go index 213b2fef..2a600bb6 100644 --- a/node.go +++ b/node.go @@ -16,12 +16,12 @@ import ( const nilAngleString = "" -// RawNode encapsulates the state of a node on which a remote procedure call -// can be performed. +// Node encapsulates the state of a node on which +// a remote procedure call can be performed. // // This struct is intended to be used by generated code. // You should use the generated `Node` struct instead. -type RawNode struct { +type Node struct { // Only assigned at creation. id uint32 addr string @@ -37,34 +37,34 @@ type RawNode struct { referenceCount uint } -// NewRawNode returns a new node for the provided address. -func NewRawNode(addr string) (*RawNode, error) { +// NewNode returns a new node for the provided address. +func NewNode(addr string) (*Node, error) { tcpAddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { return nil, err } h := fnv.New32a() _, _ = h.Write([]byte(tcpAddr.String())) - return &RawNode{ + return &Node{ id: h.Sum32(), addr: tcpAddr.String(), }, nil } -// NewRawNodeWithID returns a new node for the provided address and id. -func NewRawNodeWithID(addr string, id uint32) (*RawNode, error) { +// NewNodeWithID returns a new node for the provided address and id. +func NewNodeWithID(addr string, id uint32) (*Node, error) { tcpAddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { return nil, err } - return &RawNode{ + return &Node{ id: id, addr: tcpAddr.String(), }, nil } // called when the node is added to a configuration -func (n *RawNode) obtain() { +func (n *Node) obtain() { n.mu.Lock() defer n.mu.Unlock() n.referenceCount++ @@ -72,7 +72,7 @@ func (n *RawNode) obtain() { // called when the node is removed from a configuration // if the reference count reaches zero close the node -func (n *RawNode) release() error { +func (n *Node) release() error { n.mu.Lock() n.referenceCount-- shouldClose := n.referenceCount == 0 @@ -92,7 +92,7 @@ func (n *RawNode) release() error { } // connect to this node and associate it with the manager. -func (n *RawNode) connect(mgr *manager) error { +func (n *Node) connect(mgr *manager) error { n.mgr = mgr if n.mgr.opts.noConnect { return nil @@ -105,7 +105,7 @@ func (n *RawNode) connect(mgr *manager) error { } // dial the node and close the current connection. -func (n *RawNode) dial() error { +func (n *Node) dial() error { if n.conn != nil { // close the current connection before dialing again. n.conn.Close() @@ -122,7 +122,7 @@ func (n *RawNode) dial() error { // This method must be called for each connection to ensure // fresh contexts. Reusing contexts could result in reusing // a cancelled context. -func (n *RawNode) newContext() context.Context { +func (n *Node) newContext() context.Context { md := n.mgr.opts.metadata.Copy() if n.mgr.opts.perNodeMD != nil { md = metadata.Join(md, n.mgr.opts.perNodeMD(n.id)) @@ -133,7 +133,7 @@ func (n *RawNode) newContext() context.Context { } // close this node. -func (n *RawNode) close() error { +func (n *Node) close() error { // important to cancel first to stop goroutines n.cancel() if n.conn == nil { @@ -146,7 +146,7 @@ func (n *RawNode) close() error { } // ID returns the ID of n. -func (n *RawNode) ID() uint32 { +func (n *Node) ID() uint32 { if n != nil { return n.id } @@ -154,7 +154,7 @@ func (n *RawNode) ID() uint32 { } // Address returns network address of n. -func (n *RawNode) Address() string { +func (n *Node) Address() string { if n != nil { return n.addr } @@ -162,7 +162,7 @@ func (n *RawNode) Address() string { } // Host returns the network host of n. -func (n *RawNode) Host() string { +func (n *Node) Host() string { if n == nil { return nilAngleString } @@ -171,7 +171,7 @@ func (n *RawNode) Host() string { } // Port returns network port of n. -func (n *RawNode) Port() string { +func (n *Node) Port() string { if n != nil { _, port, _ := net.SplitHostPort(n.addr) return port @@ -179,7 +179,7 @@ func (n *RawNode) Port() string { return nilAngleString } -func (n *RawNode) String() string { +func (n *Node) String() string { if n != nil { return fmt.Sprintf("addr: %s", n.addr) } @@ -188,7 +188,7 @@ func (n *RawNode) String() string { // FullString returns a more descriptive string representation of n that // includes id, network address and latency information. -func (n *RawNode) FullString() string { +func (n *Node) FullString() string { if n != nil { return fmt.Sprintf("node %d | addr: %s", n.id, n.addr) } @@ -196,26 +196,26 @@ func (n *RawNode) FullString() string { } // LastErr returns the last error encountered (if any) for this node. -func (n *RawNode) LastErr() error { +func (n *Node) LastErr() error { return n.channel.lastErr() } // Latency returns the latency between the client and this node. -func (n *RawNode) Latency() time.Duration { +func (n *Node) Latency() time.Duration { return n.channel.channelLatency() } -type lessFunc func(n1, n2 *RawNode) bool +type lessFunc func(n1, n2 *Node) bool // MultiSorter implements the Sort interface, sorting the nodes within. type MultiSorter struct { - nodes []*RawNode + nodes []*Node less []lessFunc } // Sort sorts the argument slice according to the less functions passed to // OrderedBy. -func (ms *MultiSorter) Sort(nodes []*RawNode) { +func (ms *MultiSorter) Sort(nodes []*Node) { ms.nodes = nodes sort.Sort(ms) } @@ -265,13 +265,13 @@ func (ms *MultiSorter) Less(i, j int) bool { } // ID sorts nodes by their identifier in increasing order. -var ID = func(n1, n2 *RawNode) bool { +var ID = func(n1, n2 *Node) bool { return n1.id < n2.id } // Port sorts nodes by their port number in increasing order. // Warning: This function may be removed in the future. -var Port = func(n1, n2 *RawNode) bool { +var Port = func(n1, n2 *Node) bool { p1, _ := strconv.Atoi(n1.Port()) p2, _ := strconv.Atoi(n2.Port()) return p1 < p2 @@ -279,7 +279,7 @@ var Port = func(n1, n2 *RawNode) bool { // LastNodeError sorts nodes by their LastErr() status in increasing order. A // node with LastErr() != nil is larger than a node with LastErr() == nil. -var LastNodeError = func(n1, n2 *RawNode) bool { +var LastNodeError = func(n1, n2 *Node) bool { if n1.channel.lastErr() != nil && n2.channel.lastErr() == nil { return false } diff --git a/node_test.go b/node_test.go index 33cd31b9..3a260411 100644 --- a/node_test.go +++ b/node_test.go @@ -8,7 +8,7 @@ import ( ) func TestNodeSort(t *testing.T) { - nodes := []*RawNode{ + nodes := []*Node{ { id: 100, channel: &channel{ @@ -58,7 +58,7 @@ func TestNodeSort(t *testing.T) { } } -func printNodes(t *testing.T, nodes []*RawNode) { +func printNodes(t *testing.T, nodes []*Node) { t.Helper() for i, n := range nodes { nodeStr := fmt.Sprintf( diff --git a/quorumcall.go b/quorumcall.go index 4199cbe9..a74694ce 100644 --- a/quorumcall.go +++ b/quorumcall.go @@ -22,17 +22,13 @@ type QuorumCallData struct { // QuorumCall performs a quorum call on the configuration. // // This method should be used by generated code only. -func QuorumCall[responseType proto.Message]( - ctx context.Context, - c RawConfiguration, - d QuorumCallData, -) Responses[responseType] { - nodes := len(c.RawNodes) +func (c *Configuration) QuorumCall(ctx context.Context, d QuorumCallData) Responses[proto.Message] { + nodes := len(c.nodes) md := ordering.NewGorumsMetadata(ctx, c.getMsgID(), d.Method) replyChan := make(chan response, nodes) - for _, n := range c.RawNodes { + for _, n := range c.nodes { msg := d.Message if d.PerNodeArgFn != nil { msg = d.PerNodeArgFn(d.Message, n.id) @@ -50,12 +46,12 @@ func QuorumCall[responseType proto.Message]( ) } - return func(yield func(Response[responseType]) bool) { + return func(yield func(Response[proto.Message]) bool) { replies := int(0) errors := int(0) defer close(replyChan) - for _, n := range c.RawNodes { + for _, n := range c.nodes { defer n.channel.deleteRouter(md.GetMessageID()) } @@ -75,11 +71,7 @@ func QuorumCall[responseType proto.Message]( if r.err != nil { errors++ } - var msg responseType - if r.msg != nil { - msg = r.msg.(responseType) - } - if !yield(NewResponse(msg, r.err, r.nid)) { + if !yield(NewResponse(r.msg, r.err, r.nid)) { return } case <-ctx.Done(): diff --git a/rpc.go b/rpc.go index c37070a6..ca698c6d 100644 --- a/rpc.go +++ b/rpc.go @@ -18,7 +18,7 @@ type CallData struct { // RPCCall executes a remote procedure call on the node. // // This method should be used by generated code only. -func (n *RawNode) RPCCall(ctx context.Context, d CallData) (proto.Message, error) { +func (n *Node) RPCCall(ctx context.Context, d CallData) (proto.Message, error) { md := ordering.NewGorumsMetadata(ctx, n.mgr.getMsgID(), d.Method) replyChan := make(chan response, 1) n.channel.enqueue(request{ctx: ctx, msg: &Message{Metadata: md, Message: d.Message}}, replyChan, false) diff --git a/rpc_test.go b/rpc_test.go index e02e75b1..3c0913a1 100644 --- a/rpc_test.go +++ b/rpc_test.go @@ -18,7 +18,7 @@ func TestRPCCallSuccess(t *testing.T) { }) defer teardown() - cfg, err := dummy.NewDummyConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) + cfg, err := gorums.NewConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } @@ -26,7 +26,7 @@ func TestRPCCallSuccess(t *testing.T) { node := cfg.Nodes()[0] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - response, err := node.RawNode.RPCCall(ctx, gorums.CallData{ + response, err := node.RPCCall(ctx, gorums.CallData{ Message: &dummy.Empty{}, Method: "dummy.Dummy.Test", }) @@ -43,7 +43,7 @@ func TestRPCCallDownedNode(t *testing.T) { return initServer() }) - cfg, err := dummy.NewDummyConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) + cfg, err := gorums.NewConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } @@ -53,7 +53,7 @@ func TestRPCCallDownedNode(t *testing.T) { node := cfg.Nodes()[0] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - response, err := node.RawNode.RPCCall(ctx, gorums.CallData{ + response, err := node.RPCCall(ctx, gorums.CallData{ Message: &dummy.Empty{}, Method: "dummy.Dummy.Test", }) @@ -71,7 +71,7 @@ func TestRPCCallTimedOut(t *testing.T) { }) defer teardown() - cfg, err := dummy.NewDummyConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) + cfg, err := gorums.NewConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } @@ -80,7 +80,7 @@ func TestRPCCallTimedOut(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) time.Sleep(50 * time.Millisecond) defer cancel() - response, err := node.RawNode.RPCCall(ctx, gorums.CallData{ + response, err := node.RPCCall(ctx, gorums.CallData{ Message: &dummy.Empty{}, Method: "dummy.Dummy.Test", }) diff --git a/server_internal_test.go b/server_internal_test.go index de5520ca..dfb4e575 100644 --- a/server_internal_test.go +++ b/server_internal_test.go @@ -42,7 +42,7 @@ func TestServerCallback(t *testing.T) { ) defer mgr.close() - node, err := NewRawNode(lis.Addr().String()) + node, err := NewNode(lis.Addr().String()) if err != nil { t.Fatal(err) } diff --git a/tests/config/config_gorums.pb.go b/tests/config/config_gorums.pb.go index b46b587e..95f7ea0e 100644 --- a/tests/config/config_gorums.pb.go +++ b/tests/config/config_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: config/config.proto @@ -43,104 +43,38 @@ var _ ConfigTestNodeClient = (*ConfigTestNode)(nil) // A ConfigTestConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type ConfigTestConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewConfigTestConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewConfigTestConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *ConfigTestConfiguration, err error) { - c = &ConfigTestConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func ConfigTestConfigurationRpc(cfg *gorums.Configuration) ConfigTestConfiguration { + return ConfigTestConfiguration{ + cfg: cfg, } - return c, nil } -// SubConfigTestConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *ConfigTestConfiguration) SubConfigTestConfiguration(cfg gorums.NodeListOption) (subCfg *ConfigTestConfiguration, err error) { - subCfg = &ConfigTestConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// ConfigTestConfigurationFromRaw returns a new ConfigTestConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func ConfigTestConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*ConfigTestConfiguration, error) { - newCfg := &ConfigTestConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *ConfigTestConfiguration) Nodes() []*ConfigTestNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*ConfigTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &ConfigTestNode{n} - } - return nodes +// ConfigTestNode holds the node specific methods for the ConfigTest service. +type ConfigTestNode struct { + node *gorums.Node } -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *ConfigTestConfiguration) AllNodes() []*ConfigTestNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*ConfigTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &ConfigTestNode{n} +func ConfigTestNodeRpc(node *gorums.Node) ConfigTestNode { + return ConfigTestNode{ + node: node, } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c ConfigTestConfiguration) And(d *ConfigTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c ConfigTestConfiguration) Except(rm *ConfigTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) -} - -// ConfigTestNode holds the node specific methods for the ConfigTest service. -type ConfigTestNode struct { - *gorums.RawNode } // Config is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *ConfigTestConfiguration) Config(ctx context.Context, in *Request) gorums.Responses[*Response] { +func (c ConfigTestConfiguration) Config(ctx context.Context, in *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "config.ConfigTest.Config", ServerStream: false, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // ConfigTestServer is the server-side API for the ConfigTest Service diff --git a/tests/config/config_test.go b/tests/config/config_test.go index de3c0492..f9fd3fa7 100644 --- a/tests/config/config_test.go +++ b/tests/config/config_test.go @@ -24,7 +24,7 @@ func (srv cfgSrv) Config(_ gorums.ServerCtx, req *Request) (resp *Response, err // 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, cfgSize int, mainCfg *ConfigTestConfiguration, opts ...gorums.ManagerOption) (cfg *ConfigTestConfiguration, teardown func()) { +func setup(t *testing.T, cfgSize int, mainCfg *gorums.Configuration, opts ...gorums.ManagerOption) (cfg *gorums.Configuration, teardown func()) { t.Helper() srvs := make([]*cfgSrv, cfgSize) for i := range srvs { @@ -41,9 +41,9 @@ func setup(t *testing.T, cfgSize int, mainCfg *ConfigTestConfiguration, opts ... var err error if mainCfg != nil { - cfg, err = mainCfg.SubConfigTestConfiguration(gorums.WithNodeList(addrs)) + cfg, err = mainCfg.SubConfiguration(gorums.WithNodeList(addrs)) } else { - cfg, err = NewConfigTestConfiguration(gorums.WithNodeList(addrs), opts...) + cfg, err = gorums.NewConfiguration(gorums.WithNodeList(addrs), opts...) mainCfg = cfg } if err != nil { @@ -56,10 +56,11 @@ func setup(t *testing.T, cfgSize int, mainCfg *ConfigTestConfiguration, opts ... return cfg, teardown } -func configQF(cfg *ConfigTestConfiguration, req *Request) (*Response, error) { +func configQF(cfg *gorums.Configuration, req *Request) (*Response, error) { quorum := cfg.Size()/2 + 1 replyCount := int(0) - replies := cfg.Config(context.Background(), req) + cfgRpc := ConfigTestConfigurationRpc(cfg) + replies := cfgRpc.Config(context.Background(), req) for reply := range replies.IgnoreErrors() { replyCount++ if replyCount < quorum { @@ -73,7 +74,7 @@ func configQF(cfg *ConfigTestConfiguration, req *Request) (*Response, error) { // 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 *ConfigTestConfiguration) { + callRPC := func(cfg *gorums.Configuration) { for i := range 5 { reply, err := configQF(cfg, Request_builder{Num: uint64(i)}.Build()) if err != nil { @@ -97,7 +98,7 @@ func TestConfig(t *testing.T) { callRPC(c2) newNodeList := c1.And(c2) - c3, err := c1.SubConfigTestConfiguration(newNodeList) + c3, err := c1.SubConfiguration(newNodeList) if err != nil { t.Fatal(err) } @@ -105,7 +106,7 @@ func TestConfig(t *testing.T) { callRPC(c3) rmNodeList := c3.Except(c1) - c4, err := c1.SubConfigTestConfiguration(rmNodeList) + c4, err := c1.SubConfiguration(rmNodeList) if err != nil { t.Fatal(err) } diff --git a/tests/correctable/correctable_gorums.pb.go b/tests/correctable/correctable_gorums.pb.go index 1717b046..d006e6fe 100644 --- a/tests/correctable/correctable_gorums.pb.go +++ b/tests/correctable/correctable_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: correctable/correctable.proto @@ -46,104 +46,38 @@ var _ CorrectableTestNodeClient = (*CorrectableTestNode)(nil) // A CorrectableTestConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type CorrectableTestConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewCorrectableTestConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewCorrectableTestConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *CorrectableTestConfiguration, err error) { - c = &CorrectableTestConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func CorrectableTestConfigurationRpc(cfg *gorums.Configuration) CorrectableTestConfiguration { + return CorrectableTestConfiguration{ + cfg: cfg, } - return c, nil } -// SubCorrectableTestConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *CorrectableTestConfiguration) SubCorrectableTestConfiguration(cfg gorums.NodeListOption) (subCfg *CorrectableTestConfiguration, err error) { - subCfg = &CorrectableTestConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// CorrectableTestConfigurationFromRaw returns a new CorrectableTestConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func CorrectableTestConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*CorrectableTestConfiguration, error) { - newCfg := &CorrectableTestConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *CorrectableTestConfiguration) Nodes() []*CorrectableTestNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*CorrectableTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &CorrectableTestNode{n} - } - return nodes +// CorrectableTestNode holds the node specific methods for the CorrectableTest service. +type CorrectableTestNode struct { + node *gorums.Node } -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *CorrectableTestConfiguration) AllNodes() []*CorrectableTestNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*CorrectableTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &CorrectableTestNode{n} +func CorrectableTestNodeRpc(node *gorums.Node) CorrectableTestNode { + return CorrectableTestNode{ + node: node, } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c CorrectableTestConfiguration) And(d *CorrectableTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c CorrectableTestConfiguration) Except(rm *CorrectableTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) -} - -// CorrectableTestNode holds the node specific methods for the CorrectableTest service. -type CorrectableTestNode struct { - *gorums.RawNode } // Correctable is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *CorrectableTestConfiguration) Correctable(ctx context.Context, in *CorrectableRequest) gorums.Responses[*CorrectableResponse] { +func (c CorrectableTestConfiguration) Correctable(ctx context.Context, in *CorrectableRequest) gorums.Responses[*CorrectableResponse] { cd := gorums.QuorumCallData{ Message: in, Method: "correctable.CorrectableTest.Correctable", ServerStream: false, } - return gorums.QuorumCall[*CorrectableResponse](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*CorrectableResponse](responses) } // CorrectableStream is a quorum call invoked on each node in configuration c, @@ -151,14 +85,15 @@ func (c *CorrectableTestConfiguration) Correctable(ctx context.Context, in *Corr // with the same argument in, and returns the responses as an iterator. // This is a streaming quorum call, so each can respond with any amount of responses. -func (c *CorrectableTestConfiguration) CorrectableStream(ctx context.Context, in *CorrectableRequest) gorums.Responses[*CorrectableResponse] { +func (c CorrectableTestConfiguration) CorrectableStream(ctx context.Context, in *CorrectableRequest) gorums.Responses[*CorrectableResponse] { cd := gorums.QuorumCallData{ Message: in, Method: "correctable.CorrectableTest.CorrectableStream", ServerStream: true, } - return gorums.QuorumCall[*CorrectableResponse](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*CorrectableResponse](responses) } // CorrectableTestServer is the server-side API for the CorrectableTest Service diff --git a/tests/correctable/correctable_test.go b/tests/correctable/correctable_test.go index fc69ea38..714ee3f5 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, div int, corr func(context.Context, *CorrectableTestConfiguration, int, int) *gorums.Correctable[int]) { +func run(t *testing.T, n, div int, corr func(context.Context, *gorums.Configuration, int, int) *gorums.Correctable[int]) { addrs, teardown := gorums.TestSetup(t, n, func(_ int) gorums.ServerIface { gorumsSrv := gorums.NewServer() RegisterCorrectableTestServer(gorumsSrv, &testSrv{n}) @@ -22,7 +22,7 @@ func run(t *testing.T, n, div int, corr func(context.Context, *CorrectableTestCo }) defer teardown() - cfg, err := NewCorrectableTestConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -76,16 +76,18 @@ func (q qspec) corrQF(responses gorums.Responses[*CorrectableResponse], levelSet } func TestCorrectable(t *testing.T) { - run(t, 4, 1, func(ctx context.Context, c *CorrectableTestConfiguration, n, div int) *gorums.Correctable[int] { - corr := c.Correctable(ctx, &CorrectableRequest{}) + run(t, 4, 1, func(ctx context.Context, c *gorums.Configuration, n, div int) *gorums.Correctable[int] { + cfgRpc := CorrectableTestConfigurationRpc(c) + corr := cfgRpc.Correctable(ctx, &CorrectableRequest{}) qspec := qspec{div, n} return gorums.NewCorrectable(corr, qspec.corrQF) }) } func TestCorrectableStream(t *testing.T) { - run(t, 4, 4, func(ctx context.Context, c *CorrectableTestConfiguration, n, div int) *gorums.Correctable[int] { - corr := c.CorrectableStream(ctx, &CorrectableRequest{}) + run(t, 4, 4, func(ctx context.Context, c *gorums.Configuration, n, div int) *gorums.Correctable[int] { + cfgRpc := CorrectableTestConfigurationRpc(c) + corr := cfgRpc.CorrectableStream(ctx, &CorrectableRequest{}) qspec := qspec{div, n} return gorums.NewCorrectable(corr, qspec.corrQF) }) diff --git a/tests/dummy/dummy_gorums.pb.go b/tests/dummy/dummy_gorums.pb.go index bf5896d9..5b1f1858 100644 --- a/tests/dummy/dummy_gorums.pb.go +++ b/tests/dummy/dummy_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: dummy/dummy.proto @@ -28,103 +28,36 @@ func init() { // A DummyConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type DummyConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewDummyConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewDummyConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *DummyConfiguration, err error) { - c = &DummyConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func DummyConfigurationRpc(cfg *gorums.Configuration) DummyConfiguration { + return DummyConfiguration{ + cfg: cfg, } - return c, nil } -// SubDummyConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *DummyConfiguration) SubDummyConfiguration(cfg gorums.NodeListOption) (subCfg *DummyConfiguration, err error) { - subCfg = &DummyConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// DummyConfigurationFromRaw returns a new DummyConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func DummyConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*DummyConfiguration, error) { - newCfg := &DummyConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *DummyConfiguration) Nodes() []*DummyNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*DummyNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &DummyNode{n} - } - return nodes +// DummyNode holds the node specific methods for the Dummy service. +type DummyNode struct { + node *gorums.Node } -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *DummyConfiguration) AllNodes() []*DummyNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*DummyNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &DummyNode{n} +func DummyNodeRpc(node *gorums.Node) DummyNode { + return DummyNode{ + node: node, } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c DummyConfiguration) And(d *DummyConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c DummyConfiguration) Except(rm *DummyConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) -} - -// DummyNode holds the node specific methods for the Dummy service. -type DummyNode struct { - *gorums.RawNode } // Test is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (n *DummyNode) Test(ctx context.Context, in *Empty) (resp *Empty, err error) { +func (n DummyNode) Test(ctx context.Context, in *Empty) (resp *Empty, err error) { cd := gorums.CallData{ Message: in, Method: "dummy.Dummy.Test", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } diff --git a/tests/metadata/metadata_gorums.pb.go b/tests/metadata/metadata_gorums.pb.go index 67637f5e..ad2cf7c1 100644 --- a/tests/metadata/metadata_gorums.pb.go +++ b/tests/metadata/metadata_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: metadata/metadata.proto @@ -29,101 +29,34 @@ func init() { // A MetadataTestConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type MetadataTestConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewMetadataTestConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewMetadataTestConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *MetadataTestConfiguration, err error) { - c = &MetadataTestConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func MetadataTestConfigurationRpc(cfg *gorums.Configuration) MetadataTestConfiguration { + return MetadataTestConfiguration{ + cfg: cfg, } - return c, nil } -// SubMetadataTestConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *MetadataTestConfiguration) SubMetadataTestConfiguration(cfg gorums.NodeListOption) (subCfg *MetadataTestConfiguration, err error) { - subCfg = &MetadataTestConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// MetadataTestConfigurationFromRaw returns a new MetadataTestConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func MetadataTestConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*MetadataTestConfiguration, error) { - newCfg := &MetadataTestConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *MetadataTestConfiguration) Nodes() []*MetadataTestNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*MetadataTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &MetadataTestNode{n} - } - return nodes +// MetadataTestNode holds the node specific methods for the MetadataTest service. +type MetadataTestNode struct { + node *gorums.Node } -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *MetadataTestConfiguration) AllNodes() []*MetadataTestNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*MetadataTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &MetadataTestNode{n} +func MetadataTestNodeRpc(node *gorums.Node) MetadataTestNode { + return MetadataTestNode{ + node: node, } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c MetadataTestConfiguration) And(d *MetadataTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c MetadataTestConfiguration) Except(rm *MetadataTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) -} - -// MetadataTestNode holds the node specific methods for the MetadataTest service. -type MetadataTestNode struct { - *gorums.RawNode } // IDFromMD returns the 'id' field from the metadata. -func (n *MetadataTestNode) IDFromMD(ctx context.Context, in *emptypb.Empty) (resp *NodeID, err error) { +func (n MetadataTestNode) IDFromMD(ctx context.Context, in *emptypb.Empty) (resp *NodeID, err error) { cd := gorums.CallData{ Message: in, Method: "metadata.MetadataTest.IDFromMD", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } @@ -131,13 +64,13 @@ func (n *MetadataTestNode) IDFromMD(ctx context.Context, in *emptypb.Empty) (res } // WhatIP returns the address of the client that calls it. -func (n *MetadataTestNode) WhatIP(ctx context.Context, in *emptypb.Empty) (resp *IPAddr, err error) { +func (n MetadataTestNode) WhatIP(ctx context.Context, in *emptypb.Empty) (resp *IPAddr, err error) { cd := gorums.CallData{ Message: in, Method: "metadata.MetadataTest.WhatIP", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } diff --git a/tests/metadata/metadata_test.go b/tests/metadata/metadata_test.go index 4aa2fd78..dfb66561 100644 --- a/tests/metadata/metadata_test.go +++ b/tests/metadata/metadata_test.go @@ -58,7 +58,7 @@ func TestMetadata(t *testing.T) { "id": fmt.Sprint(want), }) - cfg, err := NewMetadataTestConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeList(addrs), gorums.WithMetadata(md), gorums.WithGrpcDialOptions( @@ -69,7 +69,7 @@ func TestMetadata(t *testing.T) { t.Fatal(err) } - node := cfg.Nodes()[0] + node := MetadataTestNodeRpc(cfg.Nodes()[0]) resp, err := node.IDFromMD(context.Background(), &emptypb.Empty{}) if err != nil { t.Fatalf("RPC error: %v", err) @@ -86,7 +86,7 @@ func TestPerMessageMetadata(t *testing.T) { addrs, teardown := gorums.TestSetup(t, 1, func(_ int) gorums.ServerIface { return initServer() }) defer teardown() - cfg, err := NewMetadataTestConfiguration(gorums.WithNodeList(addrs), + cfg, err := gorums.NewConfiguration(gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), @@ -95,7 +95,7 @@ func TestPerMessageMetadata(t *testing.T) { t.Fatal(err) } - node := cfg.Nodes()[0] + node := MetadataTestNodeRpc(cfg.Nodes()[0]) md := metadata.New(map[string]string{ "id": fmt.Sprint(want), @@ -121,7 +121,7 @@ func TestPerNodeMetadata(t *testing.T) { }) } - cfg, err := NewMetadataTestConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeList(addrs), gorums.WithPerNodeMetadata(perNodeMD), gorums.WithGrpcDialOptions( @@ -133,7 +133,8 @@ func TestPerNodeMetadata(t *testing.T) { } for _, node := range cfg.Nodes() { - resp, err := node.IDFromMD(context.Background(), &emptypb.Empty{}) + nodeRpc := MetadataTestNodeRpc(node) + resp, err := nodeRpc.IDFromMD(context.Background(), &emptypb.Empty{}) if err != nil { t.Fatalf("RPC error: %v", err) } @@ -148,7 +149,7 @@ func TestCanGetPeerInfo(t *testing.T) { addrs, teardown := gorums.TestSetup(t, 1, func(_ int) gorums.ServerIface { return initServer() }) defer teardown() - cfg, err := NewMetadataTestConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -158,7 +159,7 @@ func TestCanGetPeerInfo(t *testing.T) { t.Fatal(err) } - node := cfg.Nodes()[0] + node := MetadataTestNodeRpc(cfg.Nodes()[0]) ip, err := node.WhatIP(context.Background(), &emptypb.Empty{}) if err != nil { t.Fatalf("RPC error: %v", err) diff --git a/tests/oneway/oneway_gorums.pb.go b/tests/oneway/oneway_gorums.pb.go index cabe13c5..e6588049 100644 --- a/tests/oneway/oneway_gorums.pb.go +++ b/tests/oneway/oneway_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: oneway/oneway.proto @@ -46,99 +46,26 @@ var _ OnewayTestNodeClient = (*OnewayTestNode)(nil) // A OnewayTestConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type OnewayTestConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewOnewayTestConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewOnewayTestConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *OnewayTestConfiguration, err error) { - c = &OnewayTestConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func OnewayTestConfigurationRpc(cfg *gorums.Configuration) OnewayTestConfiguration { + return OnewayTestConfiguration{ + cfg: cfg, } - return c, nil -} - -// SubOnewayTestConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *OnewayTestConfiguration) SubOnewayTestConfiguration(cfg gorums.NodeListOption) (subCfg *OnewayTestConfiguration, err error) { - subCfg = &OnewayTestConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// OnewayTestConfigurationFromRaw returns a new OnewayTestConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func OnewayTestConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*OnewayTestConfiguration, error) { - newCfg := &OnewayTestConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *OnewayTestConfiguration) Nodes() []*OnewayTestNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*OnewayTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &OnewayTestNode{n} - } - return nodes -} - -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *OnewayTestConfiguration) AllNodes() []*OnewayTestNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*OnewayTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &OnewayTestNode{n} - } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c OnewayTestConfiguration) And(d *OnewayTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c OnewayTestConfiguration) Except(rm *OnewayTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) } // Multicast is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *OnewayTestConfiguration) Multicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (c OnewayTestConfiguration) Multicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, Method: "oneway.OnewayTest.Multicast", } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // MulticastPerNode is a quorum call invoked on each node in configuration c, @@ -148,7 +75,7 @@ func (c *OnewayTestConfiguration) Multicast(ctx context.Context, in *Request, op // The per node function f receives a copy of the Request request argument and // returns a Request manipulated to be passed to the given nodeID. // The function f must be thread-safe. -func (c *OnewayTestConfiguration) MulticastPerNode(ctx context.Context, in *Request, f func(*Request, uint32) *Request, opts ...gorums.CallOption) { +func (c OnewayTestConfiguration) MulticastPerNode(ctx context.Context, in *Request, f func(*Request, uint32) *Request, opts ...gorums.CallOption) { cd := gorums.QuorumCallData{ Message: in, @@ -159,12 +86,18 @@ func (c *OnewayTestConfiguration) MulticastPerNode(ctx context.Context, in *Requ return f(req.(*Request), nid) } - c.RawConfiguration.Multicast(ctx, cd, opts...) + c.cfg.Multicast(ctx, cd, opts...) } // OnewayTestNode holds the node specific methods for the OnewayTest service. type OnewayTestNode struct { - *gorums.RawNode + node *gorums.Node +} + +func OnewayTestNodeRpc(node *gorums.Node) OnewayTestNode { + return OnewayTestNode{ + node: node, + } } // OnewayTestServer is the server-side API for the OnewayTest Service @@ -196,11 +129,11 @@ func RegisterOnewayTestServer(srv *gorums.Server, impl OnewayTestServer) { } // Unicast is a one-way call; no replies are processed. -func (n *OnewayTestNode) Unicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { +func (n OnewayTestNode) Unicast(ctx context.Context, in *Request, opts ...gorums.CallOption) { cd := gorums.CallData{ Message: in, Method: "oneway.OnewayTest.Unicast", } - n.RawNode.Unicast(ctx, cd, opts...) + n.node.Unicast(ctx, cd, opts...) } diff --git a/tests/oneway/oneway_test.go b/tests/oneway/oneway_test.go index bba2e98b..69b1aa56 100644 --- a/tests/oneway/oneway_test.go +++ b/tests/oneway/oneway_test.go @@ -42,7 +42,7 @@ func (s *onewaySrv) MulticastPerNode(_ gorums.ServerCtx, r *oneway.Request) { s.wg.Done() } -func setup(t testing.TB, cfgSize int) (cfg *oneway.OnewayTestConfiguration, srvs []*onewaySrv, teardown func()) { +func setup(t testing.TB, cfgSize int) (cfg *gorums.Configuration, srvs []*onewaySrv, teardown func()) { t.Helper() srvs = make([]*onewaySrv, cfgSize) for i := range cfgSize { @@ -59,7 +59,7 @@ func setup(t testing.TB, cfgSize int) (cfg *oneway.OnewayTestConfiguration, srvs nodeMap[addr] = uint32(i) } - cfg, err := oneway.NewOnewayTestConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeMap(nodeMap), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -94,11 +94,11 @@ func TestOnewayCalls(t *testing.T) { {name: "MulticastNoSendWaiting", calls: numCalls, servers: 9, sendWait: false}, } - f := func(c *oneway.OnewayTestConfiguration) func(context.Context, *oneway.Request, ...gorums.CallOption) { + f := func(c *gorums.Configuration) func(context.Context, *oneway.Request, ...gorums.CallOption) { if c.Size() == 1 { - return c.Nodes()[0].Unicast + return oneway.OnewayTestNodeRpc(c.Nodes()[0]).Unicast } - return c.Multicast + return oneway.OnewayTestConfigurationRpc(c).Multicast } for _, test := range tests { @@ -193,12 +193,13 @@ func TestMulticastPerNode(t *testing.T) { srvs[i].wg.Add(test.calls) } + cfgRpc := oneway.OnewayTestConfigurationRpc(cfg) for c := 1; c <= test.calls; c++ { in := oneway.Request_builder{Num: uint64(c)}.Build() if test.sendWait { - cfg.MulticastPerNode(context.Background(), in, test.f) + cfgRpc.MulticastPerNode(context.Background(), in, test.f) } else { - cfg.MulticastPerNode(context.Background(), in, test.f, gorums.WithNoSendWaiting()) + cfgRpc.MulticastPerNode(context.Background(), in, test.f, gorums.WithNoSendWaiting()) } } @@ -227,7 +228,7 @@ func BenchmarkUnicast(b *testing.B) { for _, srv := range srvs { srv.benchmark = true } - node := cfg.Nodes()[0] + node := oneway.OnewayTestNodeRpc(cfg.Nodes()[0]) in := oneway.Request_builder{Num: 0}.Build() b.Run("UnicastSendWaiting__", func(b *testing.B) { for c := 1; c <= b.N; c++ { @@ -250,16 +251,17 @@ func BenchmarkMulticast(b *testing.B) { srv.benchmark = true } in := oneway.Request_builder{Num: 0}.Build() + cfgRpc := oneway.OnewayTestConfigurationRpc(cfg) b.Run("MulticastSendWaiting__", func(b *testing.B) { for c := 1; c <= b.N; c++ { in.SetNum(uint64(c)) - cfg.Multicast(context.Background(), in) + cfgRpc.Multicast(context.Background(), in) } }) b.Run("MulticastNoSendWaiting", func(b *testing.B) { for c := 1; c <= b.N; c++ { in.SetNum(uint64(c)) - cfg.Multicast(context.Background(), in, gorums.WithNoSendWaiting()) + cfgRpc.Multicast(context.Background(), in, gorums.WithNoSendWaiting()) } }) teardown() diff --git a/tests/ordering/order_gorums.pb.go b/tests/ordering/order_gorums.pb.go index 26fe6385..812e9012 100644 --- a/tests/ordering/order_gorums.pb.go +++ b/tests/ordering/order_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: ordering/order.proto @@ -45,129 +45,64 @@ var _ GorumsTestNodeClient = (*GorumsTestNode)(nil) // A GorumsTestConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type GorumsTestConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewGorumsTestConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewGorumsTestConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *GorumsTestConfiguration, err error) { - c = &GorumsTestConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func GorumsTestConfigurationRpc(cfg *gorums.Configuration) GorumsTestConfiguration { + return GorumsTestConfiguration{ + cfg: cfg, } - return c, nil } -// SubGorumsTestConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *GorumsTestConfiguration) SubGorumsTestConfiguration(cfg gorums.NodeListOption) (subCfg *GorumsTestConfiguration, err error) { - subCfg = &GorumsTestConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// GorumsTestConfigurationFromRaw returns a new GorumsTestConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func GorumsTestConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*GorumsTestConfiguration, error) { - newCfg := &GorumsTestConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *GorumsTestConfiguration) Nodes() []*GorumsTestNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*GorumsTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &GorumsTestNode{n} - } - return nodes +// GorumsTestNode holds the node specific methods for the GorumsTest service. +type GorumsTestNode struct { + node *gorums.Node } -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *GorumsTestConfiguration) AllNodes() []*GorumsTestNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*GorumsTestNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &GorumsTestNode{n} +func GorumsTestNodeRpc(node *gorums.Node) GorumsTestNode { + return GorumsTestNode{ + node: node, } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c GorumsTestConfiguration) And(d *GorumsTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c GorumsTestConfiguration) Except(rm *GorumsTestConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) -} - -// GorumsTestNode holds the node specific methods for the GorumsTest service. -type GorumsTestNode struct { - *gorums.RawNode } // QC is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *GorumsTestConfiguration) QC(ctx context.Context, in *Request) gorums.Responses[*Response] { +func (c GorumsTestConfiguration) QC(ctx context.Context, in *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "ordering.GorumsTest.QC", ServerStream: false, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // QCAsync is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *GorumsTestConfiguration) QCAsync(ctx context.Context, in *Request) gorums.Responses[*Response] { +func (c GorumsTestConfiguration) QCAsync(ctx context.Context, in *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "ordering.GorumsTest.QCAsync", ServerStream: false, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // UnaryRPC is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (n *GorumsTestNode) UnaryRPC(ctx context.Context, in *Request) (resp *Response, err error) { +func (n GorumsTestNode) UnaryRPC(ctx context.Context, in *Request) (resp *Response, err error) { cd := gorums.CallData{ Message: in, Method: "ordering.GorumsTest.UnaryRPC", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } diff --git a/tests/ordering/order_test.go b/tests/ordering/order_test.go index 683a731f..1207cf7c 100644 --- a/tests/ordering/order_test.go +++ b/tests/ordering/order_test.go @@ -78,14 +78,14 @@ func orderQF(ctx context.Context, responses gorums.Responses[*Response], quorum return nil, errors.New("orderQF: quorum not found") } -func setup(t *testing.T, cfgSize int) (cfg *GorumsTestConfiguration, teardown func()) { +func setup(t *testing.T, cfgSize int) (cfg *gorums.Configuration, teardown func()) { t.Helper() addrs, closeServers := gorums.TestSetup(t, cfgSize, func(_ int) gorums.ServerIface { srv := gorums.NewServer() RegisterGorumsTestServer(srv, &testSrv{}) return srv }) - cfg, err := NewGorumsTestConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -105,7 +105,7 @@ func TestUnaryRPCOrdering(t *testing.T) { defer goleak.VerifyNone(t) cfg, teardown := setup(t, 1) defer teardown() - node := cfg.Nodes()[0] + node := GorumsTestNodeRpc(cfg.Nodes()[0]) // begin test stopTime := time.Now().Add(5 * time.Second) i := 1 @@ -131,11 +131,12 @@ func TestQCOrdering(t *testing.T) { // begin test stopTime := time.Now().Add(5 * time.Second) i := 1 + rpcCfg := GorumsTestConfigurationRpc(cfg) for time.Now().Before(stopTime) { i++ ctx := context.Background() req := Request_builder{Num: uint64(i)}.Build() - resp, err := orderQF(ctx, cfg.QC(ctx, req), 4) + resp, err := orderQF(ctx, rpcCfg.QC(ctx, req), 4) if err != nil { t.Fatalf("QC error: %v", err) } @@ -157,9 +158,10 @@ func TestQCAsyncOrdering(t *testing.T) { var wg sync.WaitGroup stopTime := time.Now().Add(5 * time.Second) i := 1 + rpcCfg := GorumsTestConfigurationRpc(cfg) for time.Now().Before(stopTime) { i++ - responses := cfg.QCAsync(ctx, Request_builder{Num: uint64(i)}.Build()) + responses := rpcCfg.QCAsync(ctx, Request_builder{Num: uint64(i)}.Build()) wg.Add(1) go func(responses gorums.Responses[*Response]) { defer wg.Done() @@ -190,10 +192,11 @@ func TestMixedOrdering(t *testing.T) { // begin test stopTime := time.Now().Add(5 * time.Second) i := 1 + rpcCfg := GorumsTestConfigurationRpc(cfg) for time.Now().Before(stopTime) { req := Request_builder{Num: uint64(i)}.Build() ctx := context.Background() - resp, err := orderQF(ctx, cfg.QC(ctx, req), 4) + resp, err := orderQF(ctx, rpcCfg.QC(ctx, req), 4) i++ if err != nil { t.Fatalf("QC error: %v", err) @@ -207,9 +210,10 @@ func TestMixedOrdering(t *testing.T) { var wg sync.WaitGroup wg.Add(len(nodes)) for _, node := range nodes { - go func(node *GorumsTestNode) { + go func(node *gorums.Node) { defer wg.Done() - resp, err := node.UnaryRPC(context.Background(), Request_builder{Num: uint64(i)}.Build()) + nodeRpc := GorumsTestNodeRpc(node) + resp, err := nodeRpc.UnaryRPC(context.Background(), Request_builder{Num: uint64(i)}.Build()) if err != nil { t.Errorf("RPC error: %v", err) return diff --git a/tests/qf/qf_gorums.pb.go b/tests/qf/qf_gorums.pb.go index ab96fde5..6555053d 100644 --- a/tests/qf/qf_gorums.pb.go +++ b/tests/qf/qf_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: qf/qf.proto @@ -44,117 +44,52 @@ var _ QuorumFunctionNodeClient = (*QuorumFunctionNode)(nil) // A QuorumFunctionConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type QuorumFunctionConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewQuorumFunctionConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewQuorumFunctionConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *QuorumFunctionConfiguration, err error) { - c = &QuorumFunctionConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func QuorumFunctionConfigurationRpc(cfg *gorums.Configuration) QuorumFunctionConfiguration { + return QuorumFunctionConfiguration{ + cfg: cfg, } - return c, nil } -// SubQuorumFunctionConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *QuorumFunctionConfiguration) SubQuorumFunctionConfiguration(cfg gorums.NodeListOption) (subCfg *QuorumFunctionConfiguration, err error) { - subCfg = &QuorumFunctionConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// QuorumFunctionConfigurationFromRaw returns a new QuorumFunctionConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func QuorumFunctionConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*QuorumFunctionConfiguration, error) { - newCfg := &QuorumFunctionConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *QuorumFunctionConfiguration) Nodes() []*QuorumFunctionNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*QuorumFunctionNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &QuorumFunctionNode{n} - } - return nodes +// QuorumFunctionNode holds the node specific methods for the QuorumFunction service. +type QuorumFunctionNode struct { + node *gorums.Node } -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *QuorumFunctionConfiguration) AllNodes() []*QuorumFunctionNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*QuorumFunctionNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &QuorumFunctionNode{n} +func QuorumFunctionNodeRpc(node *gorums.Node) QuorumFunctionNode { + return QuorumFunctionNode{ + node: node, } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c QuorumFunctionConfiguration) And(d *QuorumFunctionConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c QuorumFunctionConfiguration) Except(rm *QuorumFunctionConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) -} - -// QuorumFunctionNode holds the node specific methods for the QuorumFunction service. -type QuorumFunctionNode struct { - *gorums.RawNode } // UseReq is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *QuorumFunctionConfiguration) UseReq(ctx context.Context, in *Request) gorums.Responses[*Response] { +func (c QuorumFunctionConfiguration) UseReq(ctx context.Context, in *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "qf.QuorumFunction.UseReq", ServerStream: false, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // IgnoreReq is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (c *QuorumFunctionConfiguration) IgnoreReq(ctx context.Context, in *Request) gorums.Responses[*Response] { +func (c QuorumFunctionConfiguration) IgnoreReq(ctx context.Context, in *Request) gorums.Responses[*Response] { cd := gorums.QuorumCallData{ Message: in, Method: "qf.QuorumFunction.IgnoreReq", ServerStream: false, } - return gorums.QuorumCall[*Response](ctx, c.RawConfiguration, cd) + responses := c.cfg.QuorumCall(ctx, cd) + return gorums.IterTypeCast[*Response](responses) } // QuorumFunctionServer is the server-side API for the QuorumFunction Service diff --git a/tests/qf/qf_test.go b/tests/qf/qf_test.go index fd594ff6..9eda647e 100644 --- a/tests/qf/qf_test.go +++ b/tests/qf/qf_test.go @@ -268,7 +268,7 @@ func BenchmarkFullStackQF(b *testing.B) { RegisterQuorumFunctionServer(srv, &testSrv{}) return srv }) - c, err := NewQuorumFunctionConfiguration( + c, err := gorums.NewConfiguration( gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -283,10 +283,12 @@ func BenchmarkFullStackQF(b *testing.B) { quorum := n / 2 + crpc := QuorumFunctionConfigurationRpc(c) + b.Run(fmt.Sprintf("UseReq_%d", n), func(b *testing.B) { for b.Loop() { req := Request_builder{Value: int64(requestValue)}.Build() - resp, err := IterUseReq(c.UseReq(context.Background(), req), quorum, req) + resp, err := IterUseReq(crpc.UseReq(context.Background(), req), quorum, req) if err != nil { b.Fatalf("UseReq error: %v", err) } @@ -296,7 +298,7 @@ func BenchmarkFullStackQF(b *testing.B) { b.Run(fmt.Sprintf("IgnoreReq_%d", n), func(b *testing.B) { for b.Loop() { req := Request_builder{Value: int64(requestValue)}.Build() - resp, err := IterIgnoreReq(c.IgnoreReq(context.Background(), req), quorum) + resp, err := IterIgnoreReq(crpc.IgnoreReq(context.Background(), req), quorum) if err != nil { b.Fatalf("IgnoreReq error: %v", err) } diff --git a/tests/tls/tls_gorums.pb.go b/tests/tls/tls_gorums.pb.go index 00e349e6..45293a66 100644 --- a/tests/tls/tls_gorums.pb.go +++ b/tests/tls/tls_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: tls/tls.proto @@ -28,103 +28,36 @@ func init() { // A TLSConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type TLSConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewTLSConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewTLSConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *TLSConfiguration, err error) { - c = &TLSConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func TLSConfigurationRpc(cfg *gorums.Configuration) TLSConfiguration { + return TLSConfiguration{ + cfg: cfg, } - return c, nil } -// SubTLSConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *TLSConfiguration) SubTLSConfiguration(cfg gorums.NodeListOption) (subCfg *TLSConfiguration, err error) { - subCfg = &TLSConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// TLSConfigurationFromRaw returns a new TLSConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func TLSConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*TLSConfiguration, error) { - newCfg := &TLSConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *TLSConfiguration) Nodes() []*TLSNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*TLSNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &TLSNode{n} - } - return nodes +// TLSNode holds the node specific methods for the TLS service. +type TLSNode struct { + node *gorums.Node } -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *TLSConfiguration) AllNodes() []*TLSNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*TLSNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &TLSNode{n} +func TLSNodeRpc(node *gorums.Node) TLSNode { + return TLSNode{ + node: node, } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c TLSConfiguration) And(d *TLSConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c TLSConfiguration) Except(rm *TLSConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) -} - -// TLSNode holds the node specific methods for the TLS service. -type TLSNode struct { - *gorums.RawNode } // TestTLS is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (n *TLSNode) TestTLS(ctx context.Context, in *Request) (resp *Response, err error) { +func (n TLSNode) TestTLS(ctx context.Context, in *Request) (resp *Response, err error) { cd := gorums.CallData{ Message: in, Method: "tls.TLS.TestTLS", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } diff --git a/tests/tls/tls_test.go b/tests/tls/tls_test.go index bd58ec3d..72cf72b4 100644 --- a/tests/tls/tls_test.go +++ b/tests/tls/tls_test.go @@ -45,7 +45,7 @@ func TestTLS(t *testing.T) { }) defer teardown() - cfg, err := NewTLSConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(cp, "")), @@ -55,7 +55,7 @@ func TestTLS(t *testing.T) { t.Fatal(err) } - node := cfg.Nodes()[0] + node := TLSNodeRpc(cfg.Nodes()[0]) resp, err := node.TestTLS(context.Background(), &Request{}) if err != nil { t.Fatalf("RPC error: %v", err) diff --git a/tests/unresponsive/unreponsive_test.go b/tests/unresponsive/unreponsive_test.go index ec8a552d..ae92be14 100644 --- a/tests/unresponsive/unreponsive_test.go +++ b/tests/unresponsive/unreponsive_test.go @@ -28,7 +28,7 @@ func TestUnresponsive(t *testing.T) { }) defer teardown() - cfg, err := NewUnresponsiveConfiguration( + cfg, err := gorums.NewConfiguration( gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -38,7 +38,7 @@ func TestUnresponsive(t *testing.T) { t.Fatal(err) } - node := cfg.Nodes()[0] + node := UnresponsiveNodeRpc(cfg.Nodes()[0]) for range 1000 { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) _, err = node.TestUnresponsive(ctx, &Empty{}) diff --git a/tests/unresponsive/unresponsive_gorums.pb.go b/tests/unresponsive/unresponsive_gorums.pb.go index 4d129c8d..962bbdd8 100644 --- a/tests/unresponsive/unresponsive_gorums.pb.go +++ b/tests/unresponsive/unresponsive_gorums.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-gorums. DO NOT EDIT. // versions: -// protoc-gen-gorums v0.9.0-devel+a35cbf26 +// protoc-gen-gorums v0.9.0-devel+94924590 // protoc v6.30.2 // source: unresponsive/unresponsive.proto @@ -28,103 +28,36 @@ func init() { // A UnresponsiveConfiguration represents a static set of nodes on which quorum remote // procedure calls may be invoked. type UnresponsiveConfiguration struct { - gorums.RawConfiguration + cfg *gorums.Configuration } -// NewUnresponsiveConfiguration returns a configuration based on the provided list of nodes (required) -// and an optional quorum specification. The QuorumSpec is necessary for call types that -// must process replies. For configurations only used for unicast or multicast call types, -// a QuorumSpec is not needed. -// Nodes can be supplied using WithNodeMap or WithNodeList. -// Using any other type of NodeListOption will not work. -// The ManagerOption list controls how the nodes in the configuration are created. -func NewUnresponsiveConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *UnresponsiveConfiguration, err error) { - c = &UnresponsiveConfiguration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) - if err != nil { - return nil, err +func UnresponsiveConfigurationRpc(cfg *gorums.Configuration) UnresponsiveConfiguration { + return UnresponsiveConfiguration{ + cfg: cfg, } - return c, nil } -// SubUnresponsiveConfiguration allows for making a new Configuration from the -// ManagerOption list and node list of another set of configurations, -// 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 (c *UnresponsiveConfiguration) SubUnresponsiveConfiguration(cfg gorums.NodeListOption) (subCfg *UnresponsiveConfiguration, err error) { - subCfg = &UnresponsiveConfiguration{} - subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) - if err != nil { - return nil, err - } - return subCfg, nil -} - -// UnresponsiveConfigurationFromRaw returns a new UnresponsiveConfiguration from the given raw configuration. -// -// This function may for example be used to "clone" a configuration: -// -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) -func UnresponsiveConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*UnresponsiveConfiguration, error) { - newCfg := &UnresponsiveConfiguration{ - RawConfiguration: rawCfg, - } - return newCfg, nil -} - -// 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. -// -// NOTE: mutating the returned slice is not supported. -func (c *UnresponsiveConfiguration) Nodes() []*UnresponsiveNode { - rawNodes := c.RawConfiguration.Nodes() - nodes := make([]*UnresponsiveNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &UnresponsiveNode{n} - } - return nodes +// UnresponsiveNode holds the node specific methods for the Unresponsive service. +type UnresponsiveNode struct { + node *gorums.Node } -// AllNodes returns a slice of each available node of all subconfigurations. Sorted by node id. -// -// NOTE: mutating the returned slice is not supported. -func (c *UnresponsiveConfiguration) AllNodes() []*UnresponsiveNode { - rawNodes := c.RawConfiguration.AllNodes() - nodes := make([]*UnresponsiveNode, len(rawNodes)) - for i, n := range rawNodes { - nodes[i] = &UnresponsiveNode{n} +func UnresponsiveNodeRpc(node *gorums.Node) UnresponsiveNode { + return UnresponsiveNode{ + node: node, } - return nodes -} - -// And returns a NodeListOption that can be used to create a new configuration combining c and d. -func (c UnresponsiveConfiguration) And(d *UnresponsiveConfiguration) gorums.NodeListOption { - return c.RawConfiguration.And(d.RawConfiguration) -} - -// Except returns a NodeListOption that can be used to create a new configuration -// from c without the nodes in rm. -func (c UnresponsiveConfiguration) Except(rm *UnresponsiveConfiguration) gorums.NodeListOption { - return c.RawConfiguration.Except(rm.RawConfiguration) -} - -// UnresponsiveNode holds the node specific methods for the Unresponsive service. -type UnresponsiveNode struct { - *gorums.RawNode } // TestUnresponsive is a quorum call invoked on each node in configuration c, // with the same argument in, and returns the responses as an iterator. -func (n *UnresponsiveNode) TestUnresponsive(ctx context.Context, in *Empty) (resp *Empty, err error) { +func (n UnresponsiveNode) TestUnresponsive(ctx context.Context, in *Empty) (resp *Empty, err error) { cd := gorums.CallData{ Message: in, Method: "unresponsive.Unresponsive.TestUnresponsive", } - res, err := n.RawNode.RPCCall(ctx, cd) + res, err := n.node.RPCCall(ctx, cd) if err != nil { return nil, err } diff --git a/unicast.go b/unicast.go index 6b12235f..213aa3be 100644 --- a/unicast.go +++ b/unicast.go @@ -12,7 +12,7 @@ import ( // before the message has been sent. // // This method should be used by generated code only. -func (n *RawNode) Unicast(ctx context.Context, d CallData, opts ...CallOption) { +func (n *Node) Unicast(ctx context.Context, d CallData, opts ...CallOption) { o := getCallOptions(E_Unicast, opts) md := ordering.NewGorumsMetadata(ctx, n.mgr.getMsgID(), d.Method)