Skip to content

Commit

Permalink
Validate Empty Servers
Browse files Browse the repository at this point in the history
  • Loading branch information
tung.tq committed Mar 6, 2023
1 parent d292126 commit 3e05477
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 1 deletion.
3 changes: 3 additions & 0 deletions proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type ServerConfig interface {
type Route interface {
// NewSelector ...
NewSelector() Selector

// AllServerIDs returns list of all possible server ids
AllServerIDs() []ServerID
}

// Selector is NOT thread safe
Expand Down
18 changes: 18 additions & 0 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package proxy

import (
"context"
"errors"
"fmt"
"github.com/QuangTung97/go-memcache/memcache"
"github.com/QuangTung97/memproxy"
)
Expand Down Expand Up @@ -43,6 +45,14 @@ func New[S ServerConfig](
newFunc func(conf S) memproxy.Memcache,
options ...MemcacheOption,
) (*Memcache, error) {
if len(conf.Servers) == 0 {
return nil, errors.New("proxy: empty server list")
}

if conf.Route == nil {
return nil, errors.New("proxy: route is nil")
}

memcacheConf := computeMemcacheConfig(options...)

clients := map[ServerID]memproxy.Memcache{}
Expand All @@ -52,6 +62,14 @@ func New[S ServerConfig](
clients[server.GetID()] = client
}

allServerIDs := conf.Route.AllServerIDs()
for _, serverID := range allServerIDs {
_, ok := clients[serverID]
if !ok {
return nil, fmt.Errorf("proxy: server id '%d' not in server list", serverID)
}
}

return &Memcache{
sessProvider: memcacheConf.sessProvider,
clients: clients,
Expand Down
39 changes: 38 additions & 1 deletion proxy/proxy_mocks_test.go

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

53 changes: 53 additions & 0 deletions proxy/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ func newPipelineTest(t *testing.T) *pipelineTest {
}

route := &RouteMock{}
route.AllServerIDsFunc = func() []ServerID {
return []ServerID{serverID1, serverID2}
}
route.NewSelectorFunc = func() Selector {
return selector
}
Expand Down Expand Up @@ -820,3 +823,53 @@ func TestPipeline__Delete(t *testing.T) {
}, p.actions)
})
}

func TestMemcache_Invalid_Servers_Empty(t *testing.T) {
mc, err := New[SimpleServerConfig](Config[SimpleServerConfig]{
Servers: []SimpleServerConfig{},
Route: nil,
}, func(conf SimpleServerConfig) memproxy.Memcache {
return nil
})
assert.Equal(t, errors.New("proxy: empty server list"), err)
assert.Nil(t, mc)
}

func TestMemcache_Invalid_Missing_Route(t *testing.T) {
server1 := SimpleServerConfig{
ID: serverID1,
Host: "localhost",
Port: 11211,
}

mc, err := New[SimpleServerConfig](Config[SimpleServerConfig]{
Servers: []SimpleServerConfig{server1},
Route: nil,
}, func(conf SimpleServerConfig) memproxy.Memcache {
return nil
})
assert.Equal(t, errors.New("proxy: route is nil"), err)
assert.Nil(t, mc)
}

func TestMemcache_Route_Possible_Server_IDs__Not_In_Server_List(t *testing.T) {
server1 := SimpleServerConfig{
ID: serverID1,
Host: "localhost",
Port: 11211,
}

route := &RouteMock{}
route.AllServerIDsFunc = func() []ServerID {
return []ServerID{41}
}

mc, err := New[SimpleServerConfig](Config[SimpleServerConfig]{
Servers: []SimpleServerConfig{server1},
Route: route,
}, func(conf SimpleServerConfig) memproxy.Memcache {
return nil
})
assert.Equal(t, errors.New("proxy: server id '41' not in server list"), err)
assert.Nil(t, mc)
}
9 changes: 9 additions & 0 deletions proxy/replicated.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ func NewReplicatedRoute(
stats ServerStats,
options ...ReplicatedRouteOption,
) Route {
if len(servers) == 0 {
panic("replicated route: servers can not be empty")
}

conf := &replicatedRouteConfig{
memScore: func(mem float64) float64 {
return mem
Expand Down Expand Up @@ -94,6 +98,11 @@ func (r *replicatedRoute) NewSelector() Selector {
return s
}

// AllServerIDs returns the list of all possible servers
func (r *replicatedRoute) AllServerIDs() []ServerID {
return r.configServers
}

func (s *replicatedRouteSelector) getFailedServers() map[ServerID]struct{} {
if s.failedServers == nil {
s.failedServers = map[ServerID]struct{}{}
Expand Down
11 changes: 11 additions & 0 deletions proxy/replicated_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,17 @@ func TestReplicatedRoute_With_Real_Rand(*testing.T) {
fmt.Println(counters)
}

func TestReplicatedRoute_With_Empty_Server_List(t *testing.T) {
stats := &ServerStatsMock{}

assert.PanicsWithValue(t, "replicated route: servers can not be empty", func() {
NewReplicatedRoute(
[]ServerID{},
stats,
)
})
}

func TestComputeWeightAccumWithMinPercent(t *testing.T) {
table := []struct {
name string
Expand Down

0 comments on commit 3e05477

Please sign in to comment.