-
-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
make LBSelector interface and implement all the current methods plus …
…roundrobin
- Loading branch information
Showing
6 changed files
with
184 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package proxy | ||
|
||
import ( | ||
"math/rand" | ||
"sync" | ||
) | ||
|
||
// RoundRobinSelector is a simple round-robin selector, thread-safe | ||
type RoundRobinSelector struct { | ||
lastSelected int | ||
mu sync.Mutex | ||
} | ||
|
||
// Select returns next backend index | ||
func (r *RoundRobinSelector) Select(n int) int { | ||
r.mu.Lock() | ||
defer r.mu.Unlock() | ||
selected := r.lastSelected | ||
r.lastSelected = (r.lastSelected + 1) % n | ||
return selected | ||
} | ||
|
||
// RandomSelector is a random selector, thread-safe | ||
type RandomSelector struct{} | ||
|
||
// Select returns random backend index | ||
func (r *RandomSelector) Select(n int) int { | ||
return rand.Intn(n) //nolint:gosec // no need for crypto/rand here | ||
} | ||
|
||
// FailoverSelector is a selector with failover, thread-safe | ||
type FailoverSelector struct{} | ||
|
||
// Select returns next backend index | ||
func (r *FailoverSelector) Select(_ int) int { | ||
return 0 // dead server won't be in the list, we can safely pick the first one | ||
} | ||
|
||
// LBSelectorFunc is a functional adapted for LBSelector to select backend from the list | ||
type LBSelectorFunc func(n int) int | ||
|
||
// Select returns backend index | ||
func (f LBSelectorFunc) Select(n int) int { | ||
return f(n) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package proxy | ||
|
||
import ( | ||
"sync" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestRoundRobinSelector_Select(t *testing.T) { | ||
selector := &RoundRobinSelector{} | ||
|
||
testCases := []struct { | ||
name string | ||
len int | ||
expected int | ||
}{ | ||
{"First call", 3, 0}, | ||
{"Second call", 3, 1}, | ||
{"Third call", 3, 2}, | ||
{"Back to zero", 3, 0}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
result := selector.Select(tc.len) | ||
assert.Equal(t, tc.expected, result) | ||
}) | ||
} | ||
} | ||
|
||
func TestRoundRobinSelector_SelectConcurrent(t *testing.T) { | ||
selector := &RoundRobinSelector{} | ||
l := 3 | ||
numGoroutines := 1000 | ||
|
||
var wg sync.WaitGroup | ||
wg.Add(numGoroutines) | ||
|
||
results := &sync.Map{} | ||
|
||
for i := 0; i < numGoroutines; i++ { | ||
go func() { | ||
defer wg.Done() | ||
result := selector.Select(l) | ||
results.Store(result, struct{}{}) | ||
}() | ||
} | ||
|
||
wg.Wait() | ||
|
||
// check that all possible results are present in the map. | ||
for i := 0; i < l; i++ { | ||
_, ok := results.Load(i) | ||
assert.True(t, ok, "expected to find %d in the results", i) | ||
} | ||
} | ||
|
||
func TestRandomSelector_Select(t *testing.T) { | ||
selector := &RandomSelector{} | ||
|
||
testCases := []struct { | ||
name string | ||
len int | ||
}{ | ||
{"First call", 5}, | ||
{"Second call", 5}, | ||
{"Third call", 5}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
result := selector.Select(tc.len) | ||
assert.True(t, result >= 0 && result < tc.len) | ||
}) | ||
} | ||
} | ||
|
||
func TestFailoverSelector_Select(t *testing.T) { | ||
selector := &FailoverSelector{} | ||
|
||
testCases := []struct { | ||
name string | ||
len int | ||
expected int | ||
}{ | ||
{"First call", 5, 0}, | ||
{"Second call", 5, 0}, | ||
{"Third call", 5, 0}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
result := selector.Select(tc.len) | ||
assert.Equal(t, tc.expected, result) | ||
}) | ||
} | ||
} | ||
|
||
func TestLBSelectorFunc_Select(t *testing.T) { | ||
selector := LBSelectorFunc(func(n int) int { | ||
return n - 1 // simple selection logic for testing | ||
}) | ||
|
||
testCases := []struct { | ||
name string | ||
len int | ||
expected int | ||
}{ | ||
{"First call", 5, 4}, | ||
{"Second call", 3, 2}, | ||
{"Third call", 1, 0}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
result := selector.Select(tc.len) | ||
assert.Equal(t, tc.expected, result) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters