diff --git a/benchmark/benchmark_gorums.pb.go b/benchmark/benchmark_gorums.pb.go index 135ca1bcc..4060d82b5 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: benchmark/benchmark.proto @@ -26,12 +26,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -39,13 +53,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -68,48 +112,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/channel_test.go b/channel_test.go index 471072081..302ebfe46 100644 --- a/channel_test.go +++ b/channel_test.go @@ -17,8 +17,8 @@ func (mockSrv) Test(_ ServerCtx, _ *mock.Request) (*mock.Response, error) { return nil, nil } -func dummyMgr() *RawManager { - return NewRawManager( +func dummyMgr() *manager { + return newManager( WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), @@ -27,7 +27,7 @@ func dummyMgr() *RawManager { var handlerName = "mock.Server.Test" -func dummySrv() *Server { +func newDummySrv() *Server { mockSrv := &mockSrv{} srv := NewServer() srv.RegisterHandler(handlerName, func(ctx ServerCtx, in *Message, finished chan<- *Message) { @@ -66,20 +66,20 @@ func TestChannelCreation(t *testing.T) { func TestChannelSuccessfulConnection(t *testing.T) { addrs, teardown := TestSetup(t, 1, func(_ int) ServerIface { - return dummySrv() + return newDummySrv() }) defer teardown() mgr := dummyMgr() - defer mgr.Close() + defer mgr.close() node, err := NewRawNode(addrs[0]) if err != nil { t.Fatal(err) } - if err = mgr.AddNode(node); err != nil { + if err = mgr.addNode(node); err != nil { t.Fatal(err) } - if len(mgr.Nodes()) < 1 { + if len(mgr.getNodes()) < 1 { t.Fatal("the node was not added to the configuration") } if !node.channel.isConnected() { @@ -92,7 +92,7 @@ func TestChannelSuccessfulConnection(t *testing.T) { func TestChannelUnsuccessfulConnection(t *testing.T) { mgr := dummyMgr() - defer mgr.Close() + defer mgr.close() // no servers are listening on the given address node, err := NewRawNode("127.0.0.1:5000") if err != nil { @@ -100,10 +100,10 @@ func TestChannelUnsuccessfulConnection(t *testing.T) { } // the node should still be added to the configuration - if err = mgr.AddNode(node); err != nil { + if err = mgr.addNode(node); err != nil { t.Fatal(err) } - if len(mgr.Nodes()) < 1 { + if len(mgr.getNodes()) < 1 { t.Fatal("the node was not added to the configuration") } if node.conn == nil { @@ -114,7 +114,7 @@ func TestChannelUnsuccessfulConnection(t *testing.T) { func TestChannelReconnection(t *testing.T) { srvAddr := "127.0.0.1:5000" // wait to start the server - startServer, stopServer := testServerSetup(t, srvAddr, dummySrv()) + startServer, stopServer := testServerSetup(t, srvAddr, newDummySrv()) node, err := NewRawNode(srvAddr) if err != nil { t.Fatal(err) diff --git a/cmd/benchmark/main.go b/cmd/benchmark/main.go index 164043b3d..bbb204ac3 100644 --- a/cmd/benchmark/main.go +++ b/cmd/benchmark/main.go @@ -196,11 +196,9 @@ func main() { gorums.WithSendBufferSize(*sendBuffer), } - mgr := benchmark.NewManager(mgrOpts...) - defer mgr.Close() - - cfg, err := mgr.NewConfiguration(gorums.WithNodeList(remotes[:options.NumNodes])) + cfg, err := benchmark.NewConfiguration(gorums.WithNodeList(remotes[:options.NumNodes]), mgrOpts...) checkf("Failed to create configuration: %v", err) + defer cfg.Close() results, err := benchmark.RunBenchmarks(benchReg, options, cfg, options.QuorumSize) checkf("Error running benchmarks: %v", err) diff --git a/cmd/protoc-gen-gorums/dev/config.go b/cmd/protoc-gen-gorums/dev/config.go index fa96aa8d4..80d522697 100644 --- a/cmd/protoc-gen-gorums/dev/config.go +++ b/cmd/protoc-gen-gorums/dev/config.go @@ -10,12 +10,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -23,13 +37,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes diff --git a/cmd/protoc-gen-gorums/dev/mgr.go b/cmd/protoc-gen-gorums/dev/mgr.go index 3766435cc..758c12e4b 100644 --- a/cmd/protoc-gen-gorums/dev/mgr.go +++ b/cmd/protoc-gen-gorums/dev/mgr.go @@ -10,45 +10,3 @@ func init() { encoding.RegisterCodec(gorums.NewCodec()) } } - -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} 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 11cd7b2f6..ae11117ab 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: zorums.proto 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 3aafa55f1..cba8e7262 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: zorums.proto 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 6d110d1bc..89cf4e31a 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: zorums.proto 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 4c42f4a34..5458b5475 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: zorums.proto 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 ab0b0ee71..4465ae0fb 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // 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 5953f236e..e7d0b38f0 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: zorums.proto diff --git a/cmd/protoc-gen-gorums/gengorums/template_static.go b/cmd/protoc-gen-gorums/gengorums/template_static.go index a782b6682..133664161 100644 --- a/cmd/protoc-gen-gorums/gengorums/template_static.go +++ b/cmd/protoc-gen-gorums/gengorums/template_static.go @@ -9,7 +9,7 @@ var pkgIdentMap = map[string]string{"github.com/relab/gorums": "ContentSubtype", // reservedIdents holds the set of Gorums reserved identifiers. // These identifiers cannot be used to define message types in a proto file. -var reservedIdents = []string{"Configuration", "Manager", "Node"} +var reservedIdents = []string{"Configuration", "Node"} var staticCode = `// A Configuration represents a static set of nodes on which quorum remote // procedure calls may be invoked. @@ -17,12 +17,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -30,13 +44,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -59,48 +103,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/config.go b/config.go index 274836bee..ba15b4e0c 100644 --- a/config.go +++ b/config.go @@ -1,6 +1,8 @@ package gorums -import "fmt" +import ( + "fmt" +) // RawConfiguration represents a static set of nodes on which quorum calls may be invoked. // @@ -8,23 +10,67 @@ import "fmt" // // This type is intended to be used by generated code. // You should use the generated `Configuration` type instead. -type RawConfiguration []*RawNode +type RawConfiguration struct { + RawNodes []*RawNode + mgr *manager +} // NewRawConfiguration 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) { + if nodes == nil { + return RawConfiguration{}, fmt.Errorf("config: missing required node list") + } + mgr := newManager(opts...) + + cfg, err = nodes.newConfig(mgr) + for _, n := range cfg.RawNodes { + n.obtain() + } + + return cfg, err +} + +// SubRawConfiguration 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 NewRawConfiguration(mgr *RawManager, opt NodeListOption) (nodes RawConfiguration, err error) { - if opt == nil { - return nil, fmt.Errorf("config: missing required node list") +func (c *RawConfiguration) SubRawConfiguration(nodes NodeListOption) (cfg RawConfiguration, err error) { + if nodes == nil { + return RawConfiguration{}, fmt.Errorf("config: missing required node list") + } + + cfg, err = nodes.newConfig(c.mgr) + for _, n := range cfg.RawNodes { + n.obtain() + } + + return cfg, err +} + +// CloseAll closes the configurations and all of its subconfigurations +func (c *RawConfiguration) 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 { + err := n.release() + if err != nil { + return err + } } - return opt.newConfig(mgr) + *c = RawConfiguration{} + return nil } // NodeIDs returns a slice of this configuration's Node IDs. func (c RawConfiguration) NodeIDs() []uint32 { - ids := make([]uint32, len(c)) - for i, node := range c { + ids := make([]uint32, len(c.RawNodes)) + for i, node := range c.RawNodes { ids[i] = node.ID() } return ids @@ -34,21 +80,31 @@ func (c RawConfiguration) NodeIDs() []uint32 { // // NOTE: mutating the returned slice is not supported. func (c RawConfiguration) Nodes() []*RawNode { - return c + return c.RawNodes +} + +// AllNodes returns the nodes in this configuration and all subconfigurations. +func (c RawConfiguration) AllNodeIDs() []uint32 { + return c.mgr.nodeIDs() +} + +// AllNodes returns the nodes in this configuration and all subconfigurations. +func (c RawConfiguration) AllNodes() []*RawNode { + return c.mgr.getNodes() } // Size returns the number of nodes in this configuration. func (c RawConfiguration) Size() int { - return len(c) + return len(c.RawNodes) } // Equal returns true if configurations b and c have the same set of nodes. func (c RawConfiguration) Equal(b RawConfiguration) bool { - if len(c) != len(b) { + if len(c.RawNodes) != len(b.RawNodes) { return false } - for i := range c { - if c[i].ID() != b[i].ID() { + for i := range c.RawNodes { + if c.RawNodes[i].ID() != b.RawNodes[i].ID() { return false } } @@ -56,5 +112,5 @@ func (c RawConfiguration) Equal(b RawConfiguration) bool { } func (c RawConfiguration) getMsgID() uint64 { - return c[0].mgr.getMsgID() + return c.RawNodes[0].mgr.getMsgID() } diff --git a/config_opts.go b/config_opts.go index 66c3bb50e..b7a6ab8cd 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(*RawManager) (RawConfiguration, error) + newConfig(*manager) (RawConfiguration, error) } type nodeIDMap struct { idMap map[string]uint32 } -func (o nodeIDMap) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { +func (o nodeIDMap) newConfig(mgr *manager) (cfg RawConfiguration, err error) { if len(o.idMap) == 0 { - return nil, fmt.Errorf("config: missing required node map") + return RawConfiguration{}, fmt.Errorf("config: missing required node map") } - nodes = make(RawConfiguration, 0, len(o.idMap)) + nodes := make([]*RawNode, 0, len(o.idMap)) for naddr, id := range o.idMap { - node, found := mgr.Node(id) + node, found := mgr.node(id) if !found { node, err = NewRawNodeWithID(naddr, id) if err != nil { - return nil, err + return RawConfiguration{}, err } - if err = mgr.AddNode(node); err != nil { - return nil, err + if err = mgr.addNode(node); err != nil { + return RawConfiguration{}, err } } nodes = append(nodes, node) @@ -38,7 +38,13 @@ func (o nodeIDMap) newConfig(mgr *RawManager) (nodes RawConfiguration, err error // Sort nodes to ensure deterministic iteration. OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) - return nodes, nil + + cfg = RawConfiguration{ + RawNodes: nodes, + mgr: mgr, + } + + return cfg, nil } // WithNodeMap returns a NodeListOption containing the provided @@ -51,19 +57,19 @@ type nodeList struct { addrsList []string } -func (o nodeList) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { +func (o nodeList) newConfig(mgr *manager) (cfg RawConfiguration, err error) { if len(o.addrsList) == 0 { - return nil, fmt.Errorf("config: missing required node addresses") + return RawConfiguration{}, fmt.Errorf("config: missing required node addresses") } - nodes = make(RawConfiguration, 0, len(o.addrsList)) + nodes := make([]*RawNode, 0, len(o.addrsList)) for _, naddr := range o.addrsList { node, err := NewRawNode(naddr) if err != nil { - return nil, err + return RawConfiguration{}, err } - if n, found := mgr.Node(node.ID()); !found { - if err = mgr.AddNode(node); err != nil { - return nil, err + if n, found := mgr.node(node.ID()); !found { + if err = mgr.addNode(node); err != nil { + return RawConfiguration{}, err } } else { node = n @@ -73,11 +79,17 @@ func (o nodeList) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) // Sort nodes to ensure deterministic iteration. OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) - return nodes, nil + + cfg = RawConfiguration{ + RawNodes: nodes, + mgr: mgr, + } + + return cfg, nil } // WithNodeList returns a NodeListOption containing the provided list of node addresses. -// With this option, node IDs are generated by the Manager. +// With this option, node IDs are generated by the manager. func WithNodeList(addrsList []string) NodeListOption { return &nodeList{addrsList: addrsList} } @@ -86,23 +98,29 @@ type nodeIDs struct { nodeIDs []uint32 } -func (o nodeIDs) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { +func (o nodeIDs) newConfig(mgr *manager) (cfg RawConfiguration, err error) { if len(o.nodeIDs) == 0 { - return nil, fmt.Errorf("config: missing required node IDs") + return RawConfiguration{}, fmt.Errorf("config: missing required node IDs") } - nodes = make(RawConfiguration, 0, len(o.nodeIDs)) + nodes := make([]*RawNode, 0, len(o.nodeIDs)) for _, id := range o.nodeIDs { - node, found := mgr.Node(id) + node, found := mgr.node(id) if !found { // Node IDs must have been registered previously - return nil, fmt.Errorf("config: node %d not found", id) + return RawConfiguration{}, fmt.Errorf("config: node %d not found", id) } nodes = append(nodes, node) } // Sort nodes to ensure deterministic iteration. OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) - return nodes, nil + + cfg = RawConfiguration{ + RawNodes: nodes, + mgr: mgr, + } + + return cfg, nil } // WithNodeIDs returns a NodeListOption containing a list of node IDs. @@ -116,10 +134,10 @@ type addNodes struct { new NodeListOption } -func (o addNodes) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { +func (o addNodes) newConfig(mgr *manager) (nodes RawConfiguration, err error) { newNodes, err := o.new.newConfig(mgr) if err != nil { - return nil, err + return RawConfiguration{}, err } ac := &addConfig{old: o.old, add: newNodes} return ac.newConfig(mgr) @@ -136,10 +154,10 @@ type addConfig struct { add RawConfiguration } -func (o addConfig) newConfig(mgr *RawManager) (nodes RawConfiguration, err error) { - nodes = make(RawConfiguration, 0, len(o.old)+len(o.add)) +func (o addConfig) newConfig(mgr *manager) (cfg RawConfiguration, err error) { + nodes := make([]*RawNode, 0, len(o.old.RawNodes)+len(o.add.RawNodes)) m := make(map[uint32]bool) - for _, n := range append(o.old, o.add...) { + for _, n := range append(o.old.RawNodes, o.add.RawNodes...) { if !m[n.id] { m[n.id] = true nodes = append(nodes, n) @@ -148,7 +166,13 @@ func (o addConfig) newConfig(mgr *RawManager) (nodes RawConfiguration, err error // Sort nodes to ensure deterministic iteration. OrderedBy(ID).Sort(mgr.nodes) OrderedBy(ID).Sort(nodes) - return nodes, err + + cfg = RawConfiguration{ + RawNodes: nodes, + mgr: mgr, + } + + return cfg, err } // And returns a NodeListOption that can be used to create a new configuration combining c and d. @@ -163,8 +187,8 @@ func (c RawConfiguration) WithoutNodes(ids ...uint32) NodeListOption { for _, id := range ids { rmIDs[id] = true } - keepIDs := make([]uint32, 0, len(c)) - for _, cNode := range c { + keepIDs := make([]uint32, 0, len(c.RawNodes)) + for _, cNode := range c.RawNodes { if !rmIDs[cNode.id] { keepIDs = append(keepIDs, cNode.id) } @@ -176,11 +200,11 @@ func (c RawConfiguration) WithoutNodes(ids ...uint32) NodeListOption { // from c without the nodes in rm. func (c RawConfiguration) Except(rm RawConfiguration) NodeListOption { rmIDs := make(map[uint32]bool) - for _, rmNode := range rm { + for _, rmNode := range rm.RawNodes { rmIDs[rmNode.id] = true } - keepIDs := make([]uint32, 0, len(c)) - for _, cNode := range c { + keepIDs := make([]uint32, 0, len(c.RawNodes)) + for _, cNode := range c.RawNodes { if !rmIDs[cNode.id] { keepIDs = append(keepIDs, cNode.id) } diff --git a/config_test.go b/config_test.go index 9f7b5d371..279ce4a72 100644 --- a/config_test.go +++ b/config_test.go @@ -3,6 +3,7 @@ package gorums_test import ( "context" "errors" + "reflect" "sync" "testing" @@ -11,10 +12,14 @@ import ( "github.com/relab/gorums/tests/dummy" ) +var ( + nodes = []string{"127.0.0.1:9080", "127.0.0.1:9081", "127.0.0.1:9082"} + nodeMap = map[string]uint32{"127.0.0.1:9080": 1, "127.0.0.1:9081": 2, "127.0.0.1:9082": 3, "127.0.0.1:9083": 4} +) + func TestNewConfigurationEmptyNodeList(t *testing.T) { wantErr := errors.New("config: missing required node addresses") - mgr := gorums.NewRawManager(gorums.WithNoConnect()) - _, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList([]string{})) + _, err := gorums.NewRawConfiguration(gorums.WithNodeList([]string{})) if err == nil { t.Fatalf("Expected error, got: %v, want: %v", err, wantErr) } @@ -24,8 +29,7 @@ func TestNewConfigurationEmptyNodeList(t *testing.T) { } func TestNewConfigurationNodeList(t *testing.T) { - mgr := gorums.NewRawManager(gorums.WithNoConnect()) - cfg, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(nodes)) + cfg, err := gorums.NewRawConfiguration(gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } @@ -48,10 +52,10 @@ func TestNewConfigurationNodeList(t *testing.T) { } } - if mgr.Size() != len(nodes) { - t.Errorf("mgr.Size() = %d, expected %d", mgr.Size(), len(nodes)) + if len(cfg.AllNodeIDs()) != len(nodes) { + t.Errorf("mgr.Size() = %d, expected %d", len(cfg.AllNodeIDs()), len(nodes)) } - mgrNodes := cfg.Nodes() + mgrNodes := cfg.AllNodes() for _, n := range nodes { if !contains(mgrNodes, n) { t.Errorf("mgr.Nodes() = %v, expected %s", mgrNodes, n) @@ -60,8 +64,7 @@ func TestNewConfigurationNodeList(t *testing.T) { } func TestNewConfigurationNodeMap(t *testing.T) { - mgr := gorums.NewRawManager(gorums.WithNoConnect()) - cfg, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeMap(nodeMap)) + cfg, err := gorums.NewRawConfiguration(gorums.WithNodeMap(nodeMap)) if err != nil { t.Fatal(err) } @@ -73,10 +76,10 @@ func TestNewConfigurationNodeMap(t *testing.T) { t.Errorf("cfg.Nodes()[%s] = %d, expected %d", node.Address(), node.ID(), nodeMap[node.Address()]) } } - if mgr.Size() != len(nodeMap) { - t.Errorf("mgr.Size() = %d, expected %d", mgr.Size(), len(nodeMap)) + if len(cfg.AllNodeIDs()) != len(nodeMap) { + t.Errorf("mgr.Size() = %d, expected %d", len(cfg.AllNodeIDs()), len(nodeMap)) } - for _, node := range mgr.Nodes() { + for _, node := range cfg.AllNodes() { if nodeMap[node.Address()] != node.ID() { t.Errorf("mgr.Nodes()[%s] = %d, expected %d", node.Address(), node.ID(), nodeMap[node.Address()]) } @@ -84,8 +87,7 @@ func TestNewConfigurationNodeMap(t *testing.T) { } func TestNewConfigurationNodeIDs(t *testing.T) { - mgr := gorums.NewRawManager(gorums.WithNoConnect()) - c1, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(nodes)) + c1, err := gorums.NewRawConfiguration(gorums.WithNodeList(nodes), gorums.WithNoConnect()) if err != nil { t.Fatal(err) } @@ -95,7 +97,7 @@ func TestNewConfigurationNodeIDs(t *testing.T) { // Identical configurations c1 == c2 nodeIDs := c1.NodeIDs() - c2, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeIDs(nodeIDs)) + c2, err := c1.SubRawConfiguration(gorums.WithNodeIDs(nodeIDs)) if err != nil { t.Fatal(err) } @@ -107,7 +109,7 @@ func TestNewConfigurationNodeIDs(t *testing.T) { } // Configuration with one less node |c3| == |c1| - 1 - c3, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeIDs(nodeIDs[:len(nodeIDs)-1])) + c3, err := c1.SubRawConfiguration(gorums.WithNodeIDs(nodeIDs[:len(nodeIDs)-1])) if err != nil { t.Fatal(err) } @@ -120,21 +122,19 @@ func TestNewConfigurationNodeIDs(t *testing.T) { } func TestNewConfigurationAnd(t *testing.T) { - mgr := gorums.NewRawManager(gorums.WithNoConnect()) - c1, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(nodes)) + c1, err := gorums.NewRawConfiguration(gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } c2Nodes := []string{"127.0.0.1:8080"} - c2, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(c2Nodes)) + c2, err := c1.SubRawConfiguration(gorums.WithNodeList(c2Nodes)) if err != nil { t.Fatal(err) } // Add newNodes to c1, giving a new c3 with a total of 3+2 nodes newNodes := []string{"127.0.0.1:9083", "127.0.0.1:9084"} - c3, err := gorums.NewRawConfiguration( - mgr, + c3, err := c1.SubRawConfiguration( c1.WithNewNodes(gorums.WithNodeList(newNodes)), ) if err != nil { @@ -145,8 +145,7 @@ func TestNewConfigurationAnd(t *testing.T) { } // Combine c2 to c1, giving a new c4 with a total of 3+1 nodes - c4, err := gorums.NewRawConfiguration( - mgr, + c4, err := c1.SubRawConfiguration( c1.And(c2), ) if err != nil { @@ -159,8 +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 := gorums.NewRawConfiguration( - mgr, + c5, err := c1.SubRawConfiguration( c4.And(c2), ) if err != nil { @@ -172,14 +170,12 @@ func TestNewConfigurationAnd(t *testing.T) { } func TestNewConfigurationExcept(t *testing.T) { - mgr := gorums.NewRawManager(gorums.WithNoConnect()) - c1, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(nodes)) + c1, err := gorums.NewRawConfiguration(gorums.WithNodeList(nodes)) if err != nil { t.Fatal(err) } - c2, err := gorums.NewRawConfiguration( - mgr, - c1.WithoutNodes(c1[0].ID()), + c2, err := c1.SubRawConfiguration( + c1.WithoutNodes(c1.Nodes()[0].ID()), ) if err != nil { t.Fatal(err) @@ -189,15 +185,13 @@ func TestNewConfigurationExcept(t *testing.T) { } newNodes := []string{"127.0.0.1:9083", "127.0.0.1:9084"} - c3, err := gorums.NewRawConfiguration( - mgr, + c3, err := c1.SubRawConfiguration( c1.WithNewNodes(gorums.WithNodeList(newNodes)), ) if err != nil { t.Fatal(err) } - c4, err := gorums.NewRawConfiguration( - mgr, + c4, err := c1.SubRawConfiguration( c3.Except(c1), ) if err != nil { @@ -214,8 +208,7 @@ func TestConfigConcurrentAccess(t *testing.T) { }) defer teardown() - mgr := gorumsTestMgr() - cfg, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) + cfg, err := dummy.NewConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } @@ -239,3 +232,48 @@ func TestConfigConcurrentAccess(t *testing.T) { t.Error(err) } } + +func TestConfigRelease(t *testing.T) { + addrs, teardown := gorums.TestSetup(t, 10, func(_ int) gorums.ServerIface { + return initServer() + }) + defer teardown() + + c1, err := dummy.NewConfiguration(gorums.WithNodeList(addrs[:3]), gorumsTestMgrOpts()...) + if err != nil { + t.Fatal(err) + } + + c2, err := c1.SubConfiguration(gorums.WithNodeList(addrs[2:8])) + if err != nil { + t.Fatal(err) + } + + c3, err := c1.SubConfiguration(gorums.WithNodeList(addrs[5:])) + if err != nil { + t.Fatal(err) + } + + allNodes := c2.AllNodeIDs() + + c1c2, err := c2.SubConfiguration(c1.And(c2)) + if err != nil { + t.Fatal(err) + } + targetNodes := c1c2.NodeIDs() + + c3.Close() + + remainingNodes := c2.AllNodeIDs() + + hasReleased := reflect.DeepEqual(remainingNodes, targetNodes) + hasAll := reflect.DeepEqual(remainingNodes, allNodes) + + if hasAll { + t.Fatal("c3.Close() did not close any nodes") + } + + if !hasReleased { + t.Fatal("There are not closed nodes which don't belong to any not closed configuration") + } +} diff --git a/examples/storage/client.go b/examples/storage/client.go index 16b7e9127..688d40418 100644 --- a/examples/storage/client.go +++ b/examples/storage/client.go @@ -16,19 +16,18 @@ func runClient(addresses []string) error { log.Fatalln("No addresses provided!") } - // init gorums manager - mgr := proto.NewManager( + // create configuration containing all nodes + cfg, err := proto.NewConfiguration( + gorums.WithNodeList(addresses), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), // disable TLS ), ) - // create configuration containing all nodes - cfg, err := mgr.NewConfiguration(gorums.WithNodeList(addresses)) if err != nil { log.Fatal(err) } - return Repl(mgr, cfg) + return Repl(cfg) } // newestValue returns the reply that had the most recent timestamp diff --git a/examples/storage/proto/storage_gorums.pb.go b/examples/storage/proto/storage_gorums.pb.go index de53eb179..91619606f 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+e5c1c4aa +// protoc-gen-gorums v0.9.0-devel+abcec912 // protoc v6.30.2 // source: storage/proto/storage.proto @@ -26,12 +26,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -39,13 +53,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -68,48 +112,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/examples/storage/repl.go b/examples/storage/repl.go index 59ed01176..8ec34234b 100644 --- a/examples/storage/repl.go +++ b/examples/storage/repl.go @@ -52,14 +52,12 @@ The command performs the write quorum call on node 0 and 2 ` type repl struct { - mgr *pb.Manager cfg *pb.Configuration term *term.Terminal } -func newRepl(mgr *pb.Manager, cfg *pb.Configuration) *repl { +func newRepl(cfg *pb.Configuration) *repl { return &repl{ - mgr: mgr, cfg: cfg, term: term.NewTerminal(struct { io.Reader @@ -90,8 +88,8 @@ func (r repl) ReadLine() (string, error) { // Repl runs an interactive Read-eval-print loop, that allows users to run commands that perform // RPCs and quorum calls using the manager and configuration. -func Repl(mgr *pb.Manager, defaultCfg *pb.Configuration) error { - r := newRepl(mgr, defaultCfg) +func Repl(cfg *pb.Configuration) error { + r := newRepl(cfg) fmt.Println(help) for { @@ -131,7 +129,7 @@ func Repl(mgr *pb.Manager, defaultCfg *pb.Configuration) error { r.multicast(args[1:]) case "nodes": fmt.Println("Nodes: ") - for i, n := range mgr.Nodes() { + for i, n := range cfg.AllNodes() { fmt.Printf("%d: %s\n", i, n.Address()) } default: @@ -295,7 +293,7 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *pb.Configuration) { if i := strings.Index(cfgStr, ":"); i > -1 { var start, stop int var err error - numNodes := r.mgr.Size() + numNodes := len(r.cfg.AllNodes()) if i == 0 { start = 0 } else { @@ -319,10 +317,10 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *pb.Configuration) { return nil } nodes := make([]string, 0) - for _, node := range r.mgr.Nodes()[start:stop] { + for _, node := range r.cfg.AllNodes()[start:stop] { nodes = append(nodes, node.Address()) } - cfg, err = r.mgr.NewConfiguration(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 @@ -332,7 +330,7 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *pb.Configuration) { // configuration using list of indices if indices := strings.Split(cfgStr, ","); len(indices) > 0 { selectedNodes := make([]string, 0, len(indices)) - nodes := r.mgr.Nodes() + nodes := r.cfg.AllNodes() for _, index := range indices { i, err := strconv.Atoi(index) if err != nil { @@ -345,7 +343,7 @@ func (r repl) parseConfiguration(cfgStr string) (cfg *pb.Configuration) { } selectedNodes = append(selectedNodes, nodes[i].Address()) } - cfg, err := r.mgr.NewConfiguration(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/mgr.go b/mgr.go index ffd15ebb8..188b3531b 100644 --- a/mgr.go +++ b/mgr.go @@ -1,8 +1,10 @@ package gorums import ( + "cmp" "fmt" "log" + "slices" "sync" "sync/atomic" @@ -10,12 +12,12 @@ import ( "google.golang.org/grpc/backoff" ) -// RawManager maintains a connection pool of nodes on +// manager maintains a connection pool of nodes on // which quorum calls can be performed. // // This struct is intended to be used by generated code. // You should use the generated `Manager` struct instead. -type RawManager struct { +type manager struct { mu sync.Mutex nodes []*RawNode lookup map[uint32]*RawNode @@ -25,12 +27,12 @@ type RawManager struct { nextMsgID uint64 } -// NewRawManager returns a new RawManager for managing connection to nodes added +// newManager returns a new manager for managing connection to nodes added // to the manager. This function accepts manager options used to configure // various aspects of the manager. This function is meant for internal use. // You should use the `NewManager` function in the generated code instead. -func NewRawManager(opts ...ManagerOption) *RawManager { - m := &RawManager{ +func newManager(opts ...ManagerOption) *manager { + m := &manager{ lookup: make(map[uint32]*RawNode), opts: newManagerOptions(), } @@ -54,7 +56,7 @@ func NewRawManager(opts ...ManagerOption) *RawManager { return m } -func (m *RawManager) closeNodeConns() { +func (m *manager) closeNodeConns() { for _, node := range m.nodes { err := node.close() if err != nil && m.logger != nil { @@ -64,7 +66,7 @@ func (m *RawManager) closeNodeConns() { } // Close closes all node connections and any client streams. -func (m *RawManager) Close() { +func (m *manager) close() { m.closeOnce.Do(func() { if m.logger != nil { m.logger.Printf("closing") @@ -75,7 +77,7 @@ func (m *RawManager) Close() { // NodeIDs returns the identifier of each available node. IDs are returned in // the same order as they were provided in the creation of the Manager. -func (m *RawManager) NodeIDs() []uint32 { +func (m *manager) nodeIDs() []uint32 { m.mu.Lock() defer m.mu.Unlock() ids := make([]uint32, 0, len(m.nodes)) @@ -86,7 +88,7 @@ func (m *RawManager) NodeIDs() []uint32 { } // Node returns the node with the given identifier if present. -func (m *RawManager) Node(id uint32) (node *RawNode, found bool) { +func (m *manager) node(id uint32) (node *RawNode, found bool) { m.mu.Lock() defer m.mu.Unlock() node, found = m.lookup[id] @@ -95,14 +97,14 @@ func (m *RawManager) 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 *RawManager) Nodes() []*RawNode { +func (m *manager) getNodes() []*RawNode { m.mu.Lock() defer m.mu.Unlock() return m.nodes } // Size returns the number of nodes in the Manager. -func (m *RawManager) Size() (nodes int) { +func (m *manager) size() (nodes int) { m.mu.Lock() defer m.mu.Unlock() return len(m.nodes) @@ -110,8 +112,9 @@ func (m *RawManager) Size() (nodes int) { // AddNode adds the node to the manager's node pool // and establishes a connection to the node. -func (m *RawManager) AddNode(node *RawNode) error { - if _, found := m.Node(node.ID()); found { +// should only be used by tests +func (m *manager) addNode(node *RawNode) 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()) } @@ -131,7 +134,32 @@ func (m *RawManager) AddNode(node *RawNode) error { return nil } +// remove a node from a manager, the node is closed seperately +func (m *manager) removeNode(node *RawNode) error { + if m.logger != nil { + m.logger.Printf("Removing node %s with id %d\n", node, node.id) + } + + m.mu.Lock() + defer m.mu.Unlock() + + delete(m.lookup, node.id) + + // assume nodes are sorted + i, found := slices.BinarySearchFunc(m.nodes, node, func(n1, n2 *RawNode) int { + return cmp.Compare(n1.id, n2.id) + }) + + if found { + // keep nodes sorted + m.nodes = append(m.nodes[:i], m.nodes[i+1:]...) + return nil + } + + return fmt.Errorf("RemodeNode: Node %d (%s) not found", node.id, node) +} + // getMsgID returns a unique message ID. -func (m *RawManager) getMsgID() uint64 { +func (m *manager) getMsgID() uint64 { return atomic.AddUint64(&m.nextMsgID, 1) } diff --git a/mgr_internal_test.go b/mgr_internal_test.go new file mode 100644 index 000000000..cc35f36ca --- /dev/null +++ b/mgr_internal_test.go @@ -0,0 +1,89 @@ +package gorums + +import ( + "bytes" + "log" + "testing" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +var mgrTestNodeMap = map[string]uint32{"127.0.0.1:9080": 1, "127.0.0.1:9081": 2, "127.0.0.1:9082": 3, "127.0.0.1:9083": 4} + +func TestManagerLogging(t *testing.T) { + var ( + buf bytes.Buffer + logger = log.New(&buf, "logger: ", log.Lshortfile) + ) + buf.WriteString("\n") + _ = newManager( + WithNoConnect(), + WithLogger(logger), + ) + t.Log(buf.String()) +} + +func TestManagerAddNode(t *testing.T) { + cfg, err := NewRawConfiguration(WithNodeMap(mgrTestNodeMap), WithNoConnect()) + mgr := cfg.mgr + if err != nil { + t.Fatal(err) + } + tests := []struct { + addr string + id uint32 + err string + }{ + {"127.0.1.1:1234", 1, "config: node 1 (127.0.1.1:1234) already exists"}, + {"127.0.1.1:1234", 5, ""}, + {"127.0.1.1:1234", 6, ""}, // The same addr:port can have different IDs + {"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) + if err != nil { + t.Fatal(err) + } + err = mgr.addNode(node) + if err != nil && err.Error() != test.err { + t.Errorf("mgr.addNode(Node(%s, %d)) = %s, expected %s", test.addr, test.id, err.Error(), test.err) + } + } +} + +func TestManagerAddNodeWithConn(t *testing.T) { + addrs, teardown := TestSetup(t, 3, func(_ int) ServerIface { + srv := NewServer() + return srv + }) + defer teardown() + + cfg, err := NewRawConfiguration(WithNodeList(addrs[:2]), + WithGrpcDialOptions( + grpc.WithTransportCredentials(insecure.NewCredentials()), + ), + ) + if err != nil { + t.Fatal(err) + } + defer cfg.Close() + + mgr := cfg.mgr + + if mgr.size() != len(addrs)-1 { + t.Errorf("mgr.Size() = %d, expected %d", mgr.size(), len(addrs)-1) + } + + node, err := NewRawNode(addrs[2]) + if err != nil { + t.Fatal(err) + } + err = mgr.addNode(node) + if err != nil { + t.Errorf("mgr.AddNode(%s) = %q, expected %q", addrs[2], err.Error(), "") + } + if mgr.size() != len(addrs) { + t.Errorf("mgr.Size() = %d, expected %d", mgr.size(), len(addrs)) + } +} diff --git a/mgr_test.go b/mgr_test.go deleted file mode 100644 index 6939d204e..000000000 --- a/mgr_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package gorums_test - -import ( - "bytes" - "log" - "testing" - - "github.com/relab/gorums" - "github.com/relab/gorums/tests/dummy" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -var ( - nodes = []string{"127.0.0.1:9080", "127.0.0.1:9081", "127.0.0.1:9082"} - nodeMap = map[string]uint32{"127.0.0.1:9080": 1, "127.0.0.1:9081": 2, "127.0.0.1:9082": 3, "127.0.0.1:9083": 4} -) - -func TestManagerLogging(t *testing.T) { - var ( - buf bytes.Buffer - logger = log.New(&buf, "logger: ", log.Lshortfile) - ) - buf.WriteString("\n") - _ = gorums.NewRawManager( - gorums.WithNoConnect(), - gorums.WithLogger(logger), - ) - t.Log(buf.String()) -} - -func TestManagerAddNode(t *testing.T) { - mgr := gorums.NewRawManager(gorums.WithNoConnect()) - _, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeMap(nodeMap)) - if err != nil { - t.Fatal(err) - } - tests := []struct { - addr string - id uint32 - err string - }{ - {"127.0.1.1:1234", 1, "config: node 1 (127.0.1.1:1234) already exists"}, - {"127.0.1.1:1234", 5, ""}, - {"127.0.1.1:1234", 6, ""}, // The same addr:port can have different IDs - {"127.0.1.1:1234", 2, "config: node 2 (127.0.1.1:1234) already exists"}, - } - for _, test := range tests { - node, err := gorums.NewRawNodeWithID(test.addr, test.id) - if err != nil { - t.Fatal(err) - } - err = mgr.AddNode(node) - if err != nil && err.Error() != test.err { - t.Errorf("mgr.AddNode(Node(%s, %d)) = %s, expected %s", test.addr, test.id, err.Error(), test.err) - } - } -} - -// Proto definition in tests/dummy/dummy.proto -type dummySrv struct{} - -func (dummySrv) Test(ctx gorums.ServerCtx, _ *dummy.Empty) (resp *dummy.Empty, err error) { - return nil, nil -} - -func TestManagerAddNodeWithConn(t *testing.T) { - addrs, teardown := gorums.TestSetup(t, 3, func(_ int) gorums.ServerIface { - srv := gorums.NewServer() - dummy.RegisterDummyServer(srv, &dummySrv{}) - return srv - }) - defer teardown() - mgr := gorums.NewRawManager( - gorums.WithGrpcDialOptions( - grpc.WithTransportCredentials(insecure.NewCredentials()), - ), - ) - defer mgr.Close() - - _, err := gorums.NewRawConfiguration(mgr, gorums.WithNodeList(addrs[:2])) - if err != nil { - t.Fatal(err) - } - if mgr.Size() != len(addrs)-1 { - t.Errorf("mgr.Size() = %d, expected %d", mgr.Size(), len(addrs)-1) - } - - node, err := gorums.NewRawNode(addrs[2]) - if err != nil { - t.Fatal(err) - } - err = mgr.AddNode(node) - if err != nil { - t.Errorf("mgr.AddNode(%s) = %q, expected %q", addrs[2], err.Error(), "") - } - if mgr.Size() != len(addrs) { - t.Errorf("mgr.Size() = %d, expected %d", mgr.Size(), len(addrs)) - } -} diff --git a/multicast.go b/multicast.go index 1ef603a13..5b0fb3550 100644 --- a/multicast.go +++ b/multicast.go @@ -19,9 +19,9 @@ func (c RawConfiguration) Multicast(ctx context.Context, d QuorumCallData, opts var replyChan chan response if !o.noSendWaiting { - replyChan = make(chan response, len(c)) + replyChan = make(chan response, c.Size()) } - for _, n := range c { + for _, n := range c.Nodes() { msg := d.Message if d.PerNodeArgFn != nil { msg = d.PerNodeArgFn(d.Message, n.id) diff --git a/node.go b/node.go index 9f0869840..213b2fef4 100644 --- a/node.go +++ b/node.go @@ -7,6 +7,7 @@ import ( "net" "sort" "strconv" + "sync" "time" "google.golang.org/grpc" @@ -26,10 +27,14 @@ type RawNode struct { addr string conn *grpc.ClientConn cancel func() - mgr *RawManager + mgr *manager // the default channel channel *channel + + // reference conting, use a mutex just in case + mu sync.Mutex + referenceCount uint } // NewRawNode returns a new node for the provided address. @@ -58,8 +63,36 @@ func NewRawNodeWithID(addr string, id uint32) (*RawNode, error) { }, nil } +// called when the node is added to a configuration +func (n *RawNode) obtain() { + n.mu.Lock() + defer n.mu.Unlock() + n.referenceCount++ +} + +// called when the node is removed from a configuration +// if the reference count reaches zero close the node +func (n *RawNode) release() error { + n.mu.Lock() + n.referenceCount-- + shouldClose := n.referenceCount == 0 + n.mu.Unlock() + + if shouldClose { + err := n.mgr.removeNode(n) + if err != nil { + return err + } + err = n.close() + if err != nil { + return err + } + } + return nil +} + // connect to this node and associate it with the manager. -func (n *RawNode) connect(mgr *RawManager) error { +func (n *RawNode) connect(mgr *manager) error { n.mgr = mgr if n.mgr.opts.noConnect { return nil diff --git a/quorumcall.go b/quorumcall.go index d1482bad2..4199cbe9e 100644 --- a/quorumcall.go +++ b/quorumcall.go @@ -27,12 +27,12 @@ func QuorumCall[responseType proto.Message]( c RawConfiguration, d QuorumCallData, ) Responses[responseType] { - nodes := len(c) + nodes := len(c.RawNodes) md := ordering.NewGorumsMetadata(ctx, c.getMsgID(), d.Method) replyChan := make(chan response, nodes) - for _, n := range c { + for _, n := range c.RawNodes { msg := d.Message if d.PerNodeArgFn != nil { msg = d.PerNodeArgFn(d.Message, n.id) @@ -55,7 +55,7 @@ func QuorumCall[responseType proto.Message]( errors := int(0) defer close(replyChan) - for _, n := range c { + for _, n := range c.RawNodes { defer n.channel.deleteRouter(md.GetMessageID()) } diff --git a/rpc_test.go b/rpc_test.go index e5b709258..824c693ed 100644 --- a/rpc_test.go +++ b/rpc_test.go @@ -18,17 +18,15 @@ func TestRPCCallSuccess(t *testing.T) { }) defer teardown() - mgr := gorumsTestMgr() - - _, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) + cfg, err := dummy.NewConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } - node := mgr.Nodes()[0] + node := cfg.Nodes()[0] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - response, err := node.RPCCall(ctx, gorums.CallData{ + response, err := node.RawNode.RPCCall(ctx, gorums.CallData{ Message: &dummy.Empty{}, Method: "dummy.Dummy.Test", }) @@ -44,19 +42,18 @@ func TestRPCCallDownedNode(t *testing.T) { addrs, teardown := gorums.TestSetup(t, 1, func(_ int) gorums.ServerIface { return initServer() }) - mgr := gorumsTestMgr() - _, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) + cfg, err := dummy.NewConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } teardown() // stop all servers on purpose time.Sleep(300 * time.Millisecond) // servers are not stopped immediately - node := mgr.Nodes()[0] + node := cfg.Nodes()[0] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - response, err := node.RPCCall(ctx, gorums.CallData{ + response, err := node.RawNode.RPCCall(ctx, gorums.CallData{ Message: &dummy.Empty{}, Method: "dummy.Dummy.Test", }) @@ -74,18 +71,16 @@ func TestRPCCallTimedOut(t *testing.T) { }) defer teardown() - mgr := gorumsTestMgr() - - _, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) + cfg, err := dummy.NewConfiguration(gorums.WithNodeList(addrs), gorumsTestMgrOpts()...) if err != nil { t.Fatal(err) } - node := mgr.Nodes()[0] + node := cfg.Nodes()[0] ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) time.Sleep(50 * time.Millisecond) defer cancel() - response, err := node.RPCCall(ctx, gorums.CallData{ + response, err := node.RawNode.RPCCall(ctx, gorums.CallData{ Message: &dummy.Empty{}, Method: "dummy.Dummy.Test", }) @@ -103,13 +98,11 @@ func initServer() *gorums.Server { return srv } -func gorumsTestMgr() *dummy.Manager { - mgr := dummy.NewManager( - gorums.WithGrpcDialOptions( - grpc.WithTransportCredentials(insecure.NewCredentials()), - ), - ) - return mgr +func gorumsTestMgrOpts() []gorums.ManagerOption { + opts := []gorums.ManagerOption{gorums.WithGrpcDialOptions( + grpc.WithTransportCredentials(insecure.NewCredentials()), + )} + return opts } type testSrv struct{} diff --git a/server_test.go b/server_internal_test.go similarity index 72% rename from server_test.go rename to server_internal_test.go index 42117ada1..de5520cac 100644 --- a/server_test.go +++ b/server_internal_test.go @@ -1,4 +1,4 @@ -package gorums_test +package gorums import ( "context" @@ -6,7 +6,6 @@ import ( "testing" "time" - "github.com/relab/gorums" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" @@ -16,7 +15,7 @@ func TestServerCallback(t *testing.T) { var message string signal := make(chan struct{}) - srv := gorums.NewServer(gorums.WithConnectCallback(func(ctx context.Context) { + srv := NewServer(WithConnectCallback(func(ctx context.Context) { m, ok := metadata.FromIncomingContext(ctx) if !ok { return @@ -35,20 +34,20 @@ func TestServerCallback(t *testing.T) { md := metadata.New(map[string]string{"message": "hello"}) - mgr := gorums.NewRawManager( - gorums.WithMetadata(md), - gorums.WithGrpcDialOptions( + mgr := newManager( + WithMetadata(md), + WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - defer mgr.Close() + defer mgr.close() - node, err := gorums.NewRawNode(lis.Addr().String()) + node, err := NewRawNode(lis.Addr().String()) if err != nil { t.Fatal(err) } - if err = mgr.AddNode(node); err != nil { + if err = mgr.addNode(node); err != nil { t.Fatal(err) } diff --git a/tests/config/config_gorums.pb.go b/tests/config/config_gorums.pb.go index 2b4be31d9..7e491a4f5 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: config/config.proto @@ -25,12 +25,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -38,13 +52,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -67,48 +111,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/tests/config/config_test.go b/tests/config/config_test.go index fb3274c48..98f7c0084 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, mgr *Manager, cfgSize int) (cfg *Configuration, teardown func()) { +func setup(t *testing.T, cfgSize int, mainCfg *Configuration, opts ...gorums.ManagerOption) (cfg *Configuration, teardown func()) { t.Helper() srvs := make([]*cfgSrv, cfgSize) for i := range srvs { @@ -38,12 +38,19 @@ func setup(t *testing.T, mgr *Manager, cfgSize int) (cfg *Configuration, teardow for i := range srvs { srvs[i].name = addrs[i] } - cfg, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) + + var err error + if mainCfg != nil { + cfg, err = mainCfg.SubConfiguration(gorums.WithNodeList(addrs)) + } else { + cfg, err = NewConfiguration(gorums.WithNodeList(addrs), opts...) + mainCfg = cfg + } if err != nil { t.Fatal(err) } teardown = func() { - mgr.Close() + mainCfg.Close() closeServers() } return cfg, teardown @@ -77,23 +84,20 @@ func TestConfig(t *testing.T) { } } } - mgr := NewManager( - gorums.WithGrpcDialOptions( - grpc.WithTransportCredentials(insecure.NewCredentials()), - ), - ) - c1, teardown1 := setup(t, mgr, 4) + + opts := gorums.WithGrpcDialOptions(grpc.WithTransportCredentials(insecure.NewCredentials())) + c1, teardown1 := setup(t, 4, nil, opts) defer teardown1() fmt.Println("--- c1 ", c1.Nodes()) callRPC(c1) - c2, teardown2 := setup(t, mgr, 2) + c2, teardown2 := setup(t, 2, c1) defer teardown2() fmt.Println("--- c2 ", c2.Nodes()) callRPC(c2) newNodeList := c1.And(c2) - c3, err := mgr.NewConfiguration(newNodeList) + c3, err := c1.SubConfiguration(newNodeList) if err != nil { t.Fatal(err) } @@ -101,7 +105,7 @@ func TestConfig(t *testing.T) { callRPC(c3) rmNodeList := c3.Except(c1) - c4, err := mgr.NewConfiguration(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 db318d8c7..06484517a 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: correctable/correctable.proto @@ -27,12 +27,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -40,13 +54,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -69,48 +113,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/tests/correctable/correctable_test.go b/tests/correctable/correctable_test.go index 3eaa08846..276ca9837 100644 --- a/tests/correctable/correctable_test.go +++ b/tests/correctable/correctable_test.go @@ -22,13 +22,12 @@ func run(t *testing.T, n, div int, corr func(context.Context, *Configuration, in }) defer teardown() - mgr := NewManager( + cfg, err := NewConfiguration( + gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - - cfg, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) if err != nil { t.Fatal(err) } diff --git a/tests/dummy/dummy_gorums.pb.go b/tests/dummy/dummy_gorums.pb.go index f957096a4..31e9da486 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: dummy/dummy.proto @@ -25,12 +25,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -38,13 +52,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -67,48 +111,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/tests/metadata/metadata_gorums.pb.go b/tests/metadata/metadata_gorums.pb.go index 01caa7090..e24761b6c 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: metadata/metadata.proto @@ -26,12 +26,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -39,13 +53,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -68,48 +112,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/tests/metadata/metadata_test.go b/tests/metadata/metadata_test.go index 429d52809..2a762418b 100644 --- a/tests/metadata/metadata_test.go +++ b/tests/metadata/metadata_test.go @@ -58,18 +58,18 @@ func TestMetadata(t *testing.T) { "id": fmt.Sprint(want), }) - mgr := NewManager( + cfg, err := NewConfiguration( + gorums.WithNodeList(addrs), gorums.WithMetadata(md), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - _, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) if err != nil { t.Fatal(err) } - node := mgr.Nodes()[0] + node := cfg.Nodes()[0] resp, err := node.IDFromMD(context.Background(), &emptypb.Empty{}) if err != nil { t.Fatalf("RPC error: %v", err) @@ -86,17 +86,16 @@ func TestPerMessageMetadata(t *testing.T) { addrs, teardown := gorums.TestSetup(t, 1, func(_ int) gorums.ServerIface { return initServer() }) defer teardown() - mgr := NewManager( + cfg, err := NewConfiguration(gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - _, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) if err != nil { t.Fatal(err) } - node := mgr.Nodes()[0] + node := cfg.Nodes()[0] md := metadata.New(map[string]string{ "id": fmt.Sprint(want), @@ -122,18 +121,18 @@ func TestPerNodeMetadata(t *testing.T) { }) } - mgr := NewManager( + cfg, err := NewConfiguration( + gorums.WithNodeList(addrs), gorums.WithPerNodeMetadata(perNodeMD), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - _, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) if err != nil { t.Fatal(err) } - for _, node := range mgr.Nodes() { + for _, node := range cfg.Nodes() { resp, err := node.IDFromMD(context.Background(), &emptypb.Empty{}) if err != nil { t.Fatalf("RPC error: %v", err) @@ -149,17 +148,17 @@ func TestCanGetPeerInfo(t *testing.T) { addrs, teardown := gorums.TestSetup(t, 1, func(_ int) gorums.ServerIface { return initServer() }) defer teardown() - mgr := NewManager( + cfg, err := NewConfiguration( + gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - _, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) if err != nil { t.Fatal(err) } - node := mgr.Nodes()[0] + node := 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 560a62e81..d253b125b 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: oneway/oneway.proto @@ -26,12 +26,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -39,13 +53,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -68,48 +112,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/tests/oneway/oneway_test.go b/tests/oneway/oneway_test.go index 4604c9fab..f88ff03fa 100644 --- a/tests/oneway/oneway_test.go +++ b/tests/oneway/oneway_test.go @@ -59,17 +59,17 @@ func setup(t testing.TB, cfgSize int) (cfg *oneway.Configuration, srvs []*oneway nodeMap[addr] = uint32(i) } - mgr := oneway.NewManager( + cfg, err := oneway.NewConfiguration( + gorums.WithNodeMap(nodeMap), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - cfg, err := mgr.NewConfiguration(gorums.WithNodeMap(nodeMap)) if err != nil { t.Fatal(err) } teardown = func() { - mgr.Close() + cfg.Close() closeServers() } return cfg, srvs, teardown diff --git a/tests/ordering/order_gorums.pb.go b/tests/ordering/order_gorums.pb.go index ea482af07..094283bb3 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: ordering/order.proto @@ -25,12 +25,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -38,13 +52,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -67,48 +111,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/tests/ordering/order_test.go b/tests/ordering/order_test.go index d690ba0cc..a38bf9b1c 100644 --- a/tests/ordering/order_test.go +++ b/tests/ordering/order_test.go @@ -85,17 +85,17 @@ func setup(t *testing.T, cfgSize int) (cfg *Configuration, teardown func()) { RegisterGorumsTestServer(srv, &testSrv{}) return srv }) - mgr := NewManager( + cfg, err := NewConfiguration( + gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - cfg, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) if err != nil { t.Fatal(err) } teardown = func() { - mgr.Close() + cfg.Close() closeServers() } return cfg, teardown diff --git a/tests/qf/qf_gorums.pb.go b/tests/qf/qf_gorums.pb.go index 1551e8cf0..07e125045 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: qf/qf.proto @@ -25,12 +25,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -38,13 +52,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -67,48 +111,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/tests/qf/qf_test.go b/tests/qf/qf_test.go index 6ffddcddf..cb5c9340e 100644 --- a/tests/qf/qf_test.go +++ b/tests/qf/qf_test.go @@ -268,14 +268,12 @@ func BenchmarkFullStackQF(b *testing.B) { RegisterQuorumFunctionServer(srv, &testSrv{}) return srv }) - mgr := NewManager( + c, err := NewConfiguration( + gorums.WithNodeList(addrs), // dummy node list; won't actually be used in test gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - c, err := mgr.NewConfiguration( - gorums.WithNodeList(addrs), - ) if err != nil { b.Fatal(err) } @@ -305,9 +303,9 @@ func BenchmarkFullStackQF(b *testing.B) { _ = resp.GetResult() } }) - // close manager and stop gRPC servers; + // close configuration and stop gRPC servers; // must be done for each iteration - mgr.Close() + c.Close() stop() } } diff --git a/tests/tls/tls_gorums.pb.go b/tests/tls/tls_gorums.pb.go index 175b22d95..4becf6ca8 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: tls/tls.proto @@ -25,12 +25,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -38,13 +52,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -67,48 +111,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct { diff --git a/tests/tls/tls_test.go b/tests/tls/tls_test.go index cd34f0aed..b877da018 100644 --- a/tests/tls/tls_test.go +++ b/tests/tls/tls_test.go @@ -45,17 +45,17 @@ func TestTLS(t *testing.T) { }) defer teardown() - mgr := NewManager( + cfg, err := NewConfiguration( + gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(cp, "")), ), ) - _, err = mgr.NewConfiguration(gorums.WithNodeList(addrs)) if err != nil { t.Fatal(err) } - node := mgr.Nodes()[0] + node := 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 71eb0b3a4..2bd3db540 100644 --- a/tests/unresponsive/unreponsive_test.go +++ b/tests/unresponsive/unreponsive_test.go @@ -28,18 +28,17 @@ func TestUnresponsive(t *testing.T) { }) defer teardown() - mgr := NewManager( + cfg, err := NewConfiguration( + gorums.WithNodeList(addrs), gorums.WithGrpcDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) - _, err := mgr.NewConfiguration(gorums.WithNodeList(addrs)) if err != nil { t.Fatal(err) } - node := mgr.Nodes()[0] - + node := 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 0c3dda950..c4fca0b49 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+09c177d2 +// protoc-gen-gorums v0.9.0-devel+967e3a70 // protoc v6.30.2 // source: unresponsive/unresponsive.proto @@ -25,12 +25,26 @@ type Configuration struct { gorums.RawConfiguration } -// ConfigurationFromRaw returns a new Configuration from the given raw configuration and QuorumSpec. -// -// This function may for example be used to "clone" a configuration but install a different QuorumSpec: +// NewConfiguration 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 NewConfiguration(cfg gorums.NodeListOption, opts ...gorums.ManagerOption) (c *Configuration, err error) { + c = &Configuration{} + c.RawConfiguration, err = gorums.NewRawConfiguration(cfg, opts...) + if err != nil { + return nil, err + } + return c, nil +} + +// ConfigurationFromRaw returns a new configuration from the given raw configuration. // -// cfg1, err := mgr.NewConfiguration(qspec1, opts...) -// cfg2 := ConfigurationFromRaw(cfg1.RawConfig, qspec2) +// cfg1, err := pb.NewConfiguration(nodeList, opts...) +// cfg2 := ConfigurationFromRaw(cfg1.RawConfig) func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error) { newCfg := &Configuration{ RawConfiguration: rawCfg, @@ -38,13 +52,43 @@ func ConfigurationFromRaw(rawCfg gorums.RawConfiguration) (*Configuration, error 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. +// SubConfiguration allows for making a new Configuration from the ManagerOption list and +// node list of another configuration, +// 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 *Configuration) SubConfiguration(cfg gorums.NodeListOption) (subCfg *Configuration, err error) { + subCfg = &Configuration{} + subCfg.RawConfiguration, err = c.SubRawConfiguration(cfg) + if err != nil { + return nil, err + } + return subCfg, nil +} + +// Close closes the nodes which aren't used by other subconfigurations +func (c *Configuration) Close() error { + return c.RawConfiguration.Close() +} + +// Nodes returns a slice of the configuration nodes. Sorted by node id. // // NOTE: mutating the returned slice is not supported. func (c *Configuration) Nodes() []*Node { nodes := make([]*Node, c.Size()) - for i, n := range c.RawConfiguration { + for i, n := range c.RawConfiguration.RawNodes { + nodes[i] = &Node{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 *Configuration) AllNodes() []*Node { + rawNodes := c.RawConfiguration.AllNodes() + nodes := make([]*Node, len(rawNodes)) + for i, n := range rawNodes { nodes[i] = &Node{n} } return nodes @@ -67,48 +111,6 @@ func init() { } } -// Manager maintains a connection pool of nodes on -// which quorum calls can be performed. -type Manager struct { - *gorums.RawManager -} - -// NewManager returns a new Manager for managing connection to nodes added -// to the manager. This function accepts manager options used to configure -// various aspects of the manager. -func NewManager(opts ...gorums.ManagerOption) *Manager { - return &Manager{ - RawManager: gorums.NewRawManager(opts...), - } -} - -// NewConfiguration 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. The QuorumSpec interface is also a ConfigOption. -// Nodes can be supplied using WithNodeMap or WithNodeList, or WithNodeIDs. -// A new configuration can also be created from an existing configuration, -// using the And, WithNewNodes, Except, and WithoutNodes methods. -func (m *Manager) NewConfiguration(nodeList gorums.NodeListOption) (c *Configuration, err error) { - c = &Configuration{} - c.RawConfiguration, err = gorums.NewRawConfiguration(m.RawManager, nodeList) - if err != nil { - return nil, err - } - return c, nil -} - -// Nodes returns a slice of available nodes on this manager. -// IDs are returned in the order they were added at creation of the manager. -func (m *Manager) Nodes() []*Node { - gorumsNodes := m.RawManager.Nodes() - nodes := make([]*Node, len(gorumsNodes)) - for i, n := range gorumsNodes { - nodes[i] = &Node{n} - } - return nodes -} - // Node encapsulates the state of a node on which a remote procedure call // can be performed. type Node struct {