Borrows the same logic as memberlist to avoid saturating the network for clusters over 32 nodes.
111 lines
2.9 KiB
Go
111 lines
2.9 KiB
Go
package cluster_test
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.clan.lol/clan/data-mesher/pkg/cluster"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPushPullInterval_BelowThreshold(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
as := require.New(t)
|
|
base := 30 * time.Second
|
|
|
|
// For n < 32, the interval should always equal the base interval.
|
|
for _, n := range []int{0, 1, 5, 10, 16, 31} {
|
|
actual := cluster.PushPullInterval(n, base)
|
|
as.Equal(base, actual, "n=%d: expected base interval", n)
|
|
}
|
|
}
|
|
|
|
func TestPushPullInterval_AtThreshold(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
as := require.New(t)
|
|
base := 30 * time.Second
|
|
|
|
// At n=32, log2(32)-log2(32)+1 = 1, so the interval should equal the base.
|
|
actual := cluster.PushPullInterval(32, base)
|
|
as.Equal(base, actual)
|
|
}
|
|
|
|
func TestPushPullInterval_ScalesLogarithmically(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
as := require.New(t)
|
|
base := 30 * time.Second
|
|
|
|
// Verify known values using the formula: base * (log2(n) - log2(32) + 1)
|
|
tests := []struct {
|
|
n int
|
|
multiplier float64 // expected multiplier over base
|
|
}{
|
|
{64, 2.0}, // log2(64)-log2(32)+1 = 6-5+1 = 2
|
|
{128, 3.0}, // log2(128)-log2(32)+1 = 7-5+1 = 3
|
|
{256, 4.0}, // log2(256)-log2(32)+1 = 8-5+1 = 4
|
|
{1024, 6.0}, // log2(1024)-log2(32)+1 = 10-5+1 = 6
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
actual := cluster.PushPullInterval(tt.n, base)
|
|
want := time.Duration(tt.multiplier*base.Seconds()) * time.Second
|
|
as.Equal(want, actual, "n=%d", tt.n)
|
|
}
|
|
}
|
|
|
|
func TestPushPullInterval_MonotonicallyIncreasing(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
as := require.New(t)
|
|
base := 30 * time.Second
|
|
|
|
// The interval must never decrease as n grows.
|
|
sizes := []int{1, 10, 31, 32, 50, 64, 100, 128, 256, 500, 1024, 4096, 10000}
|
|
prev := cluster.PushPullInterval(sizes[0], base)
|
|
|
|
for _, n := range sizes[1:] {
|
|
cur := cluster.PushPullInterval(n, base)
|
|
as.GreaterOrEqual(cur, prev, "interval decreased going to n=%d", n)
|
|
|
|
prev = cur
|
|
}
|
|
}
|
|
|
|
func TestPushPullInterval_SublinearGrowth(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
as := require.New(t)
|
|
base := 30 * time.Second
|
|
|
|
// Doubling n from 1024 to 2048 should add exactly 1x base (log2 property),
|
|
// not double the interval. This confirms sub-linear scaling.
|
|
i1024 := cluster.PushPullInterval(1024, base)
|
|
i2048 := cluster.PushPullInterval(2048, base)
|
|
|
|
// log2(2048)-log2(1024) = 1, so the difference should be 1x base.
|
|
diff := i2048 - i1024
|
|
as.Equal(base, diff)
|
|
|
|
// The interval at 2048 should be well under 2x the interval at 1024.
|
|
as.Less(i2048.Seconds(), 2*i1024.Seconds())
|
|
}
|
|
|
|
func TestPushPullInterval_NonPowerOfTwo(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
as := require.New(t)
|
|
base := 30 * time.Second
|
|
|
|
// For non-power-of-two sizes, verify the formula holds with floating point log2.
|
|
n := 100
|
|
multiplier := math.Log2(float64(n)) - math.Log2(32) + 1
|
|
expected := time.Duration(multiplier*base.Seconds()) * time.Second
|
|
|
|
actual := cluster.PushPullInterval(n, base)
|
|
as.Equal(expected, actual, "n=%d", n)
|
|
}
|