Files
brianmcgee 8a332200da cluster: scale the push/pull interval with cluster size
Borrows the same logic as memberlist to avoid saturating the network for clusters over 32 nodes.
2026-02-18 16:01:31 +00:00

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)
}