Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/benchmark/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,14 @@ func main() {
options.QuorumSize = *qSize
}

mgrOpts := []gorums.ManagerOption{
dialOpts := []gorums.DialOption{
gorums.WithDialOptions(
grpc.WithTransportCredentials(insecure.NewCredentials()),
),
gorums.WithSendBufferSize(*sendBuffer),
}

mgr := benchmark.NewManager(mgrOpts...)
mgr := benchmark.NewManager(dialOpts...)
defer mgr.Close()

cfg, err := benchmark.NewConfiguration(mgr, gorums.WithNodeList(remotes[:options.NumNodes]))
Expand Down
6 changes: 3 additions & 3 deletions cmd/protoc-gen-gorums/dev/aliases.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ var (
)

// NewManager returns a new Manager for managing connection to nodes added
// to the manager. This function accepts manager options used to configure
// to the manager. This function accepts dial options used to configure
// various aspects of the manager.
func NewManager(opts ...gorums.ManagerOption) *Manager {
func NewManager(opts ...gorums.DialOption) *Manager {
return gorums.NewManager(opts...)
}

Expand All @@ -38,7 +38,7 @@ func NewConfiguration(mgr *Manager, opt gorums.NodeListOption) (Configuration, e
}

// NewConfig returns a new [Configuration] based on the provided [gorums.Option]s.
// It accepts exactly one [gorums.NodeListOption] and multiple [gorums.ManagerOption]s.
// It accepts exactly one [gorums.NodeListOption] and multiple [gorums.DialOption]s.
// You may use this function to create the initial configuration for a new manager.
//
// Example:
Expand Down
4 changes: 2 additions & 2 deletions cmd/protoc-gen-gorums/gengorums/template_static.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func NewConfiguration(mgr *Manager, opt NodeListOption) (nodes Configuration, er
}

// NewConfig returns a new [Configuration] based on the provided [gorums.Option]s.
// It accepts exactly one [gorums.NodeListOption] and multiple [gorums.ManagerOption]s.
// It accepts exactly one [gorums.NodeListOption] and multiple [gorums.DialOption]s.
// You may use this function to create the initial configuration for a new manager.
//
// Example:
Expand All @@ -70,7 +70,7 @@ func NewConfiguration(mgr *Manager, opt NodeListOption) (nodes Configuration, er
// creates a new manager; if a manager already exists, use [NewConfiguration]
// instead, and provide the existing manager as the first argument.
func NewConfig(opts ...Option) (Configuration, error) {
serverOptions, managerOptions, nodeListOption, err := splitOptions(opts)
serverOptions, dialOpts, nodeListOption, err := splitOptions(opts)
if err != nil {
return nil, err
}
Expand All @@ -80,7 +80,7 @@ func NewConfig(opts ...Option) (Configuration, error) {
if nodeListOption == nil {
return nil, fmt.Errorf("gorums: missing required NodeListOption")
}
mgr := NewManager(managerOptions...)
mgr := NewManager(dialOpts...)
return NewConfiguration(mgr, nodeListOption)
}

Expand Down
8 changes: 4 additions & 4 deletions mgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ type Manager struct {
lookup map[uint32]*Node
closeOnce sync.Once
logger *log.Logger
opts managerOptions
opts dialOptions
nextMsgID uint64
}

// NewManager returns a new Manager for managing connection to nodes added
// to the manager. This function accepts manager options used to configure
// to the manager. This function accepts dial options used to configure
// various aspects of the manager.
func NewManager(opts ...ManagerOption) *Manager {
func NewManager(opts ...DialOption) *Manager {
m := &Manager{
lookup: make(map[uint32]*Node),
opts: newManagerOptions(),
opts: newDialOptions(),
}
for _, opt := range opts {
opt(&m.opts)
Expand Down
71 changes: 38 additions & 33 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ type Option interface {
isOption()
}

// ManagerOption provides a way to set different options on a new Manager.
type ManagerOption func(*managerOptions)
// DialOption provides a way to set different options on a new configuration.
type DialOption func(*dialOptions)

func (ManagerOption) isOption() {}
func (DialOption) isOption() {}

type managerOptions struct {
// ManagerOption is a deprecated alias for [DialOption].
//
// Deprecated: Use [DialOption] instead.
type ManagerOption = DialOption

type dialOptions struct {
grpcDialOpts []grpc.DialOption
logger *log.Logger
backoff backoff.Config
Expand All @@ -33,91 +38,91 @@ type managerOptions struct {
localNodeID uint32 // if non-zero, skip setting handler on this node ID
}

func newManagerOptions() managerOptions {
return managerOptions{
func newDialOptions() dialOptions {
return dialOptions{
backoff: backoff.DefaultConfig,
sendBuffer: 0,
}
}

// WithDialOptions returns a ManagerOption which sets any gRPC dial options
// the Manager should use when initially connecting to each node in its pool.
func WithDialOptions(opts ...grpc.DialOption) ManagerOption {
return func(o *managerOptions) {
// WithDialOptions returns a DialOption which sets any gRPC dial options
// the client should use when initially connecting to each node in its pool.
func WithDialOptions(opts ...grpc.DialOption) DialOption {
return func(o *dialOptions) {
o.grpcDialOpts = append(o.grpcDialOpts, opts...)
}
}

// WithLogger returns a ManagerOption which sets an optional error logger for
// the Manager.
func WithLogger(logger *log.Logger) ManagerOption {
return func(o *managerOptions) {
// WithLogger returns a DialOption which sets an optional error logger for
// the configuration.
func WithLogger(logger *log.Logger) DialOption {
return func(o *dialOptions) {
o.logger = logger
}
}

// WithBackoff allows for changing the backoff delays used by Gorums.
func WithBackoff(backoff backoff.Config) ManagerOption {
return func(o *managerOptions) {
func WithBackoff(backoff backoff.Config) DialOption {
return func(o *dialOptions) {
o.backoff = backoff
}
}

// WithSendBufferSize allows for changing the size of the send buffer used by Gorums.
// A larger buffer might achieve higher throughput for asynchronous calltypes, but at
// the cost of latency.
func WithSendBufferSize(size uint) ManagerOption {
return func(o *managerOptions) {
func WithSendBufferSize(size uint) DialOption {
return func(o *dialOptions) {
o.sendBuffer = size
}
}

// WithMetadata returns a ManagerOption that sets the metadata that is sent to each node
// WithMetadata returns a DialOption that sets the metadata that is sent to each node
// when the connection is initially established. This metadata can be retrieved from the
// server-side method handlers.
func WithMetadata(md metadata.MD) ManagerOption {
return func(o *managerOptions) {
func WithMetadata(md metadata.MD) DialOption {
return func(o *dialOptions) {
o.metadata = md
}
}

// WithPerNodeMetadata returns a ManagerOption that allows you to set metadata for each
// WithPerNodeMetadata returns a DialOption that allows you to set metadata for each
// node individually.
func WithPerNodeMetadata(f func(uint32) metadata.MD) ManagerOption {
return func(o *managerOptions) {
func WithPerNodeMetadata(f func(uint32) metadata.MD) DialOption {
return func(o *dialOptions) {
o.perNodeMD = f
}
}

// withRequestHandler returns a ManagerOption that sets the RequestHandler used to
// withRequestHandler returns a DialOption that sets the RequestHandler used to
// dispatch server-initiated requests arriving on the bidirectional back-channel,
// and records localID as this node's own NodeID. The localID is included in this
// node's outgoing metadata for each connection, enabling the server to identify
// this replica. The handler is not installed for the self-connection (if any) to
// avoid deadlocks in symmetric configurations.
func withRequestHandler(handler stream.RequestHandler, localID uint32) ManagerOption {
return func(o *managerOptions) {
func withRequestHandler(handler stream.RequestHandler, localID uint32) DialOption {
return func(o *dialOptions) {
o.handler = handler
o.localNodeID = localID
o.metadata = metadata.Join(o.metadata, metadataWithNodeID(localID))
}
}

// splitOptions separates a slice of [Option]s into [ServerOption]s, [ManagerOption]s,
// splitOptions separates a slice of [Option]s into [ServerOption]s, [DialOption]s,
// and a single [NodeListOption]. It returns an error if more than one [NodeListOption]
// is provided or if an unsupported option type is encountered.
func splitOptions(opts []Option) (srvOpts []ServerOption, mgrOpts []ManagerOption, nodeListOpt NodeListOption, err error) {
func splitOptions(opts []Option) (srvOpts []ServerOption, dialOpts []DialOption, nodeListOpt NodeListOption, err error) {
for _, opt := range opts {
// A typed interface value (e.g. ManagerOption(nil)) is not equal to nil, so we need
// A typed interface value (e.g. DialOption(nil)) is not equal to nil, so we need
// to also check if the underlying value is actually nil to avoid panics.
if opt == nil || reflect.ValueOf(opt).IsNil() {
continue
}
switch o := opt.(type) {
case ServerOption:
srvOpts = append(srvOpts, o)
case ManagerOption:
mgrOpts = append(mgrOpts, o)
case DialOption:
dialOpts = append(dialOpts, o)
case NodeListOption:
if nodeListOpt != nil {
return nil, nil, nil, fmt.Errorf("gorums: multiple NodeListOptions provided")
Expand All @@ -127,5 +132,5 @@ func splitOptions(opts []Option) (srvOpts []ServerOption, mgrOpts []ManagerOptio
return nil, nil, nil, fmt.Errorf("gorums: unsupported option type: %T", opt)
}
}
return srvOpts, mgrOpts, nodeListOpt, nil
return srvOpts, dialOpts, nodeListOpt, nil
}
24 changes: 12 additions & 12 deletions opts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,49 @@ import "testing"

// TestSplitOptionsTypedNil verifies that splitOptions correctly handles typed
// nils. In Go, an interface can be non-nil while wrapping a nil concrete value
// (e.g. ManagerOption(nil)), which the simple "opt == nil" check does not
// (e.g. DialOption(nil)), which the simple "opt == nil" check does not
// catch. Without a robust check, these typed nils would pass through and cause
// a panic when the caller invokes the nil function.
func TestSplitOptionsTypedNil(t *testing.T) {
tests := []struct {
name string
opts []Option
wantSrvLen int
wantMgrLen int
wantDialLen int
}{
{
name: "UntypedNil",
opts: []Option{nil},
wantSrvLen: 0,
wantMgrLen: 0,
wantDialLen: 0,
},
{
name: "NilManagerOption",
opts: []Option{ManagerOption(nil)},
name: "NilDialOption",
opts: []Option{DialOption(nil)},
wantSrvLen: 0,
wantMgrLen: 0,
wantDialLen: 0,
},
{
name: "NilServerOption",
opts: []Option{ServerOption(nil)},
wantSrvLen: 0,
wantMgrLen: 0,
wantDialLen: 0,
},
{
name: "MixedNilAndValid",
opts: []Option{
ManagerOption(nil),
DialOption(nil),
WithSendBufferSize(0),
ServerOption(nil),
WithReceiveBufferSize(0),
},
wantSrvLen: 1,
wantMgrLen: 1,
wantDialLen: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
srvOpts, mgrOpts, nodeListOpt, err := splitOptions(tt.opts)
srvOpts, dialOpts, nodeListOpt, err := splitOptions(tt.opts)
if err != nil {
t.Fatalf("splitOptions() unexpected error: %v", err)
}
Expand All @@ -56,8 +56,8 @@ func TestSplitOptionsTypedNil(t *testing.T) {
if got := len(srvOpts); got != tt.wantSrvLen {
t.Errorf("len(srvOpts) = %d, want %d", got, tt.wantSrvLen)
}
if got := len(mgrOpts); got != tt.wantMgrLen {
t.Errorf("len(mgrOpts) = %d, want %d", got, tt.wantMgrLen)
if got := len(dialOpts); got != tt.wantDialLen {
t.Errorf("len(dialOpts) = %d, want %d", got, tt.wantDialLen)
}
})
}
Expand Down
16 changes: 8 additions & 8 deletions system.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ type System struct {
}

// NewSystem creates a new Gorums System listening on the specified address.
// Accepts any mix of [ServerOption], [ManagerOption], and [NodeListOption].
// Accepts any mix of [ServerOption], [DialOption], and [NodeListOption].
// If a [NodeListOption] is provided, an outbound [Configuration] is created
// automatically and can be accessed via [System.OutboundConfig].
// Returns an error if more than one [NodeListOption] is provided.
func NewSystem(addr string, opts ...Option) (*System, error) {
srvOpts, mgrOpts, nodeListOpt, err := splitOptions(opts)
srvOpts, dialOpts, nodeListOpt, err := splitOptions(opts)
if err != nil {
return nil, err
}
Expand All @@ -35,7 +35,7 @@ func NewSystem(addr string, opts ...Option) (*System, error) {
lis: lis,
}
if nodeListOpt != nil {
cfg, err := sys.newOutboundConfig(nodeListOpt, mgrOpts...)
cfg, err := sys.newOutboundConfig(nodeListOpt, dialOpts...)
if err != nil {
_ = lis.Close()
return nil, fmt.Errorf("gorums: failed to create outbound config: %w", err)
Expand All @@ -53,7 +53,7 @@ func NewSystem(addr string, opts ...Option) (*System, error) {
// [Configuration] is created automatically for each system and is available via
// [System.OutboundConfig].
//
// The opts may contain any mix of [ServerOption] and [ManagerOption], but not [NodeListOption].
// The opts may contain any mix of [ServerOption] and [DialOption], but not [NodeListOption].
//
// The returned systems are not started. Call [System.Serve] after registering
// any services. The returned stop function stops all systems and should be
Expand All @@ -62,7 +62,7 @@ func NewSystem(addr string, opts ...Option) (*System, error) {
// If system creation fails, all resources acquired by this function are
// released before returning the error.
func NewLocalSystems(n int, opts ...Option) ([]*System, func(), error) {
srvOpts, mgrOpts, nodeListOpt, err := splitOptions(opts)
srvOpts, dialOpts, nodeListOpt, err := splitOptions(opts)
if err != nil {
return nil, nil, err
}
Expand All @@ -82,7 +82,7 @@ func NewLocalSystems(n int, opts ...Option) ([]*System, func(), error) {
srv: NewServer(sysSrvOpts...),
lis: listeners[i],
}
cfg, err := sys.newOutboundConfig(nodeList, mgrOpts...)
cfg, err := sys.newOutboundConfig(nodeList, dialOpts...)
if err != nil {
for j := range i {
_ = systems[j].Stop()
Expand Down Expand Up @@ -126,9 +126,9 @@ func allocateListeners(n int) ([]net.Listener, NodeListOption, error) {
// It always prepends a [withRequestHandler] so that the remote server can dispatch
// server-initiated requests back through the bidirectional connection, regardless of
// whether this system has peer tracking configured.
func (s *System) newOutboundConfig(nodeList NodeListOption, mgrOpts ...ManagerOption) (Configuration, error) {
func (s *System) newOutboundConfig(nodeList NodeListOption, dialOpts ...DialOption) (Configuration, error) {
opts := []Option{withRequestHandler(s.srv, s.srv.NodeID()), nodeList}
for _, o := range mgrOpts {
for _, o := range dialOpts {
opts = append(opts, o)
}
return NewConfig(opts...)
Expand Down
2 changes: 1 addition & 1 deletion testing_bufconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (to *testOptions) getOrCreateManager(t testing.TB) *Manager {
grpc.WithContextDialer(bufconnDialer),
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
mgrOpts := append([]ManagerOption{WithDialOptions(dialOpts...)}, to.managerOpts...)
mgrOpts := append([]DialOption{WithDialOptions(dialOpts...)}, to.managerOpts...)
mgr := NewManager(mgrOpts...)
t.Cleanup(func() { Closer(t, mgr)() })
return mgr
Expand Down
Loading
Loading