refactor: phase 3 integrated new dialing pattern into pool
This commit is contained in:
@@ -178,19 +178,12 @@ func NewPool(ctx context.Context, config *PoolConfig, handler slog.Handler,
|
|||||||
logger = slog.New(handler).With(slog.Any("component", c))
|
logger = slog.New(handler).With(slog.Any("component", c))
|
||||||
}
|
}
|
||||||
|
|
||||||
var dialer types.Dialer
|
|
||||||
// if config.ConnectionConfig.Dialer != nil {
|
|
||||||
// dialer = config.ConnectionConfig.Dialer
|
|
||||||
// } else {
|
|
||||||
// dialer = transport.NewDialer()
|
|
||||||
// }
|
|
||||||
|
|
||||||
return &Pool{
|
return &Pool{
|
||||||
peers: make(map[string]*Peer),
|
peers: make(map[string]*Peer),
|
||||||
inbox: make(chan types.InboxMessage, config.InboxBufferSize),
|
inbox: make(chan types.InboxMessage, config.InboxBufferSize),
|
||||||
events: make(chan PoolEvent, config.EventsBufferSize),
|
events: make(chan PoolEvent, config.EventsBufferSize),
|
||||||
|
|
||||||
dialer: dialer,
|
dialer: transport.NewDialer(),
|
||||||
config: config,
|
config: config,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@@ -296,14 +289,14 @@ func (p *Pool) Close() {
|
|||||||
type ConnectOption func(*connectOptions)
|
type ConnectOption func(*connectOptions)
|
||||||
|
|
||||||
type connectOptions struct {
|
type connectOptions struct {
|
||||||
dialer types.Dialer
|
dialFn func(context.Context) (types.Socket, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDialer returns a ConnectOption that overrides the pool dialer for this
|
// WithDialFunc returns a ConnectOption that overrides the dial function for
|
||||||
// connection only.
|
// this connection.
|
||||||
func WithDialer(d types.Dialer) ConnectOption {
|
func WithDialFunc(f func(context.Context) (types.Socket, error)) ConnectOption {
|
||||||
return func(o *connectOptions) {
|
return func(o *connectOptions) {
|
||||||
o.dialer = d
|
o.dialFn = f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,23 +321,26 @@ func (p *Pool) Connect(id string, opts ...ConnectOption) error {
|
|||||||
return ErrPeerExists
|
return ErrPeerExists
|
||||||
}
|
}
|
||||||
|
|
||||||
wc := p.config.WorkerConfig
|
|
||||||
worker, err := NewWorker(p.ctx, id, nil, &wc, p.handler)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
o := &connectOptions{}
|
o := &connectOptions{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(o)
|
opt(o)
|
||||||
}
|
}
|
||||||
// effectiveDialer := p.dialer
|
|
||||||
// if o.dialer != nil {
|
|
||||||
// effectiveDialer = o.dialer
|
|
||||||
// }
|
|
||||||
|
|
||||||
// cc := p.config.ConnectionConfig.Clone()
|
// TODO: pass headers
|
||||||
// cc.Dialer = effectiveDialer
|
dialFn := func(ctx context.Context) (types.Socket, error) {
|
||||||
|
socket, _, err := p.dialer.DialContext(ctx, id, nil)
|
||||||
|
return socket, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.dialFn != nil {
|
||||||
|
dialFn = o.dialFn
|
||||||
|
}
|
||||||
|
|
||||||
|
wc := p.config.WorkerConfig
|
||||||
|
worker, err := NewWorker(p.ctx, id, dialFn, &wc, p.handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
pool := PoolPlugin{
|
pool := PoolPlugin{
|
||||||
Inbox: p.inbox,
|
Inbox: p.inbox,
|
||||||
|
|||||||
+40
-39
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
|
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
|
||||||
"git.wisehodl.dev/jay/go-honeybee/transport"
|
|
||||||
"git.wisehodl.dev/jay/go-honeybee/types"
|
"git.wisehodl.dev/jay/go-honeybee/types"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -17,14 +16,15 @@ import (
|
|||||||
func setupPool(t *testing.T) (*Pool, *honeybeetest.MockDialer) {
|
func setupPool(t *testing.T) (*Pool, *honeybeetest.MockDialer) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
dialer := &honeybeetest.MockDialer{
|
dialer := &honeybeetest.MockDialer{
|
||||||
DialContextFunc: func(context.Context, string, http.Header) (types.Socket, *http.Response, error) {
|
DialContextFunc: func(
|
||||||
|
context.Context, string, http.Header,
|
||||||
|
) (types.Socket, *http.Response, error) {
|
||||||
return honeybeetest.NewMockSocket(), nil, nil
|
return honeybeetest.NewMockSocket(), nil, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cc, _ := transport.NewConnectionConfig(transport.WithConnectionDialer(dialer))
|
config, _ := NewPoolConfig()
|
||||||
config, _ := NewPoolConfig(WithConnectionConfig(*cc))
|
pool, _ := NewPool(context.Background(), config, nil)
|
||||||
pool, err := NewPool(context.Background(), config, nil)
|
pool.dialer = dialer
|
||||||
assert.NoError(t, err)
|
|
||||||
return pool, dialer
|
return pool, dialer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,29 +86,28 @@ func TestPoolConnect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPoolConnectWithDialer(t *testing.T) {
|
func TestPoolConnectWithDialer(t *testing.T) {
|
||||||
t.Run("per-call dialer is used instead of pool dialer", func(t *testing.T) {
|
t.Run("per-call dial function is used instead of pool dialer", func(t *testing.T) {
|
||||||
|
config, _ := NewPoolConfig()
|
||||||
|
pool, err := NewPool(context.Background(), config, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
perCallUsed := false
|
perCallUsed := false
|
||||||
perCallDialer := &honeybeetest.MockDialer{
|
dialFn := func(_ context.Context) (types.Socket, error) {
|
||||||
DialContextFunc: func(ctx context.Context, url string, h http.Header) (types.Socket, *http.Response, error) {
|
perCallUsed = true
|
||||||
perCallUsed = true
|
return honeybeetest.NewMockSocket(), nil
|
||||||
return honeybeetest.NewMockSocket(), nil, nil
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pool dialer should NOT be called
|
// pool dialer should NOT be called
|
||||||
poolDialer := &honeybeetest.MockDialer{
|
pool.dialer = &honeybeetest.MockDialer{
|
||||||
DialContextFunc: func(context.Context, string, http.Header) (types.Socket, *http.Response, error) {
|
DialContextFunc: func(
|
||||||
|
context.Context, string, http.Header,
|
||||||
|
) (types.Socket, *http.Response, error) {
|
||||||
t.Error("pool dialer should not be called when per-call dialer is provided")
|
t.Error("pool dialer should not be called when per-call dialer is provided")
|
||||||
return nil, nil, fmt.Errorf("unexpected call")
|
return nil, nil, fmt.Errorf("unexpected call")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cc, _ := transport.NewConnectionConfig(transport.WithConnectionDialer(poolDialer))
|
err = pool.Connect("wss://test", WithDialFunc(dialFn))
|
||||||
config, _ := NewPoolConfig(WithConnectionConfig(*cc))
|
|
||||||
pool, err := NewPool(context.Background(), config, nil)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = pool.Connect("wss://test", WithDialer(perCallDialer))
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
honeybeetest.Eventually(t, func() bool {
|
honeybeetest.Eventually(t, func() bool {
|
||||||
@@ -182,25 +181,25 @@ func TestPoolRemove(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPoolDialFailed(t *testing.T) {
|
func TestPoolDialFailed(t *testing.T) {
|
||||||
t.Run("EventDialFailed surfaces with correct peer ID, non-nil Err, and non-zero At", func(t *testing.T) {
|
t.Run("EventDialFailed received after failed dial", func(t *testing.T) {
|
||||||
dialErr := fmt.Errorf("connection refused")
|
wc, _ := NewWorkerConfig(WithRetryDisabled())
|
||||||
failingDialer := &honeybeetest.MockDialer{
|
|
||||||
DialContextFunc: func(context.Context, string, http.Header) (types.Socket, *http.Response, error) {
|
|
||||||
return nil, nil, dialErr
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cc, _ := transport.NewConnectionConfig(
|
|
||||||
transport.WithConnectionDialer(failingDialer),
|
|
||||||
transport.WithRetryDisabled(),
|
|
||||||
)
|
|
||||||
poolCfg, _ := NewPoolConfig(
|
poolCfg, _ := NewPoolConfig(
|
||||||
WithInboxBufferSize(256),
|
WithInboxBufferSize(256),
|
||||||
WithEventsBufferSize(10),
|
WithEventsBufferSize(10),
|
||||||
WithConnectionConfig(*cc),
|
WithWorkerConfig(*wc),
|
||||||
)
|
)
|
||||||
pool, err := NewPool(context.Background(), poolCfg, nil)
|
pool, err := NewPool(context.Background(), poolCfg, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
dialErr := fmt.Errorf("connection refused")
|
||||||
|
pool.dialer = &honeybeetest.MockDialer{
|
||||||
|
DialContextFunc: func(
|
||||||
|
context.Context, string, http.Header,
|
||||||
|
) (types.Socket, *http.Response, error) {
|
||||||
|
return nil, nil, dialErr
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
err = pool.Connect("wss://test")
|
err = pool.Connect("wss://test")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -245,17 +244,19 @@ func TestPoolSend(t *testing.T) {
|
|||||||
outgoingData <- honeybeetest.MockOutgoingData{MsgType: msgType, Data: data}
|
outgoingData <- honeybeetest.MockOutgoingData{MsgType: msgType, Data: data}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mockDialer := &honeybeetest.MockDialer{
|
|
||||||
DialContextFunc: func(context.Context, string, http.Header) (types.Socket, *http.Response, error) {
|
config, _ := NewPoolConfig()
|
||||||
|
pool, err := NewPool(context.Background(), config, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
pool.dialer = &honeybeetest.MockDialer{
|
||||||
|
DialContextFunc: func(
|
||||||
|
context.Context, string, http.Header,
|
||||||
|
) (types.Socket, *http.Response, error) {
|
||||||
return mockSocket, nil, nil
|
return mockSocket, nil, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cc, _ := transport.NewConnectionConfig(transport.WithConnectionDialer(mockDialer))
|
|
||||||
config, _ := NewPoolConfig(WithConnectionConfig(*cc))
|
|
||||||
pool, err := NewPool(context.Background(), config, nil)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = pool.Connect("wss://test")
|
err = pool.Connect("wss://test")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
expectEvent(t, pool.events, "wss://test", EventConnected)
|
expectEvent(t, pool.events, "wss://test", EventConnected)
|
||||||
|
|||||||
+10
-2
@@ -85,14 +85,22 @@ func DialWithRetry(
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
if logger != nil {
|
if logger != nil {
|
||||||
logger.Debug("dialing", "attempt", mgr.RetryCount()+1)
|
if mgr == nil {
|
||||||
|
logger.Debug("dialing")
|
||||||
|
} else {
|
||||||
|
logger.Debug("dialing", "attempt", mgr.RetryCount()+1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dial
|
// dial
|
||||||
socket, err := dialFn(ctx)
|
socket, err := dialFn(ctx)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if logger != nil {
|
if logger != nil {
|
||||||
logger.Debug("dial successful", "attempt", mgr.RetryCount()+1)
|
if mgr == nil {
|
||||||
|
logger.Debug("dial successful")
|
||||||
|
} else {
|
||||||
|
logger.Debug("dial successful", "attempt", mgr.RetryCount()+1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return socket, nil
|
return socket, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
|
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
|
||||||
"git.wisehodl.dev/jay/go-honeybee/types"
|
"git.wisehodl.dev/jay/go-honeybee/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -252,8 +253,13 @@ func TestDialWithRetry_DialErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mu sync.Mutex
|
||||||
var gotErrors []error
|
var gotErrors []error
|
||||||
onError := func(err error) { gotErrors = append(gotErrors, err) }
|
onError := func(err error) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
gotErrors = append(gotErrors, err)
|
||||||
|
}
|
||||||
|
|
||||||
retryCfg, _ := NewRetryConfig(
|
retryCfg, _ := NewRetryConfig(
|
||||||
WithMaxRetries(3),
|
WithMaxRetries(3),
|
||||||
@@ -269,9 +275,13 @@ func TestDialWithRetry_DialErrors(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
honeybeetest.Eventually(t, func() bool {
|
honeybeetest.Eventually(t, func() bool {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
return len(gotErrors) == 2
|
return len(gotErrors) == 2
|
||||||
}, "expected errors")
|
}, "expected errors")
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
assert.Equal(t, dialErr1, gotErrors[0])
|
assert.Equal(t, dialErr1, gotErrors[0])
|
||||||
assert.Equal(t, dialErr2, gotErrors[1])
|
assert.Equal(t, dialErr2, gotErrors[1])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ func WithRetryConfig(value transport.RetryConfig) WorkerOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithRetryDisabled(value bool) WorkerOption {
|
func WithRetryDisabled() WorkerOption {
|
||||||
return func(c *WorkerConfig) {
|
return func(c *WorkerConfig) {
|
||||||
c.RetryDisabled = value
|
c.RetryDisabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,13 +224,8 @@ func (w *DefaultWorker) runSession(ctx context.Context, pool PoolPlugin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setup new connection
|
// setup new connection
|
||||||
conn, err := transport.NewConnection(
|
conn, _ := transport.NewConnection(
|
||||||
ctx, socket, &w.config.ConnectionConfig, w.handler)
|
ctx, socket, &w.config.ConnectionConfig, w.handler)
|
||||||
if err != nil {
|
|
||||||
// TODO: cleanup worker? surface error?
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.conn.Store(conn)
|
w.conn.Store(conn)
|
||||||
pool.Events <- PoolEvent{ID: w.url, Kind: EventConnected, At: time.Now()}
|
pool.Events <- PoolEvent{ID: w.url, Kind: EventConnected, At: time.Now()}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user