Make configuration values injectable rather than hardcoded
Add config structs with sensible defaults for tunable parameters: - JetStreamConfig for stream retention (1 year) and replica count (1) - HashRingConfig for virtual nodes per physical node (150) - ShardConfig for shard count (1024) and replication factor (1) Each component gets a new WithConfig constructor that accepts custom configuration, while the original constructors continue to work with defaults. Zero values in configs fall back to defaults for backward compatibility. Closes #38 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit was merged in pull request #43.
This commit is contained in:
125
cluster/config_test.go
Normal file
125
cluster/config_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultHashRingConfig(t *testing.T) {
|
||||
config := DefaultHashRingConfig()
|
||||
|
||||
if config.VirtualNodes != DefaultVirtualNodes {
|
||||
t.Errorf("expected VirtualNodes=%d, got %d", DefaultVirtualNodes, config.VirtualNodes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultShardConfig(t *testing.T) {
|
||||
config := DefaultShardConfig()
|
||||
|
||||
if config.ShardCount != DefaultNumShards {
|
||||
t.Errorf("expected ShardCount=%d, got %d", DefaultNumShards, config.ShardCount)
|
||||
}
|
||||
if config.ReplicationFactor != 1 {
|
||||
t.Errorf("expected ReplicationFactor=1, got %d", config.ReplicationFactor)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewConsistentHashRingWithConfig(t *testing.T) {
|
||||
t.Run("custom virtual nodes", func(t *testing.T) {
|
||||
config := HashRingConfig{VirtualNodes: 50}
|
||||
ring := NewConsistentHashRingWithConfig(config)
|
||||
|
||||
ring.AddNode("test-node")
|
||||
|
||||
if len(ring.sortedHashes) != 50 {
|
||||
t.Errorf("expected 50 virtual nodes, got %d", len(ring.sortedHashes))
|
||||
}
|
||||
if ring.GetVirtualNodes() != 50 {
|
||||
t.Errorf("expected GetVirtualNodes()=50, got %d", ring.GetVirtualNodes())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("zero value uses default", func(t *testing.T) {
|
||||
config := HashRingConfig{VirtualNodes: 0}
|
||||
ring := NewConsistentHashRingWithConfig(config)
|
||||
|
||||
ring.AddNode("test-node")
|
||||
|
||||
if len(ring.sortedHashes) != DefaultVirtualNodes {
|
||||
t.Errorf("expected %d virtual nodes, got %d", DefaultVirtualNodes, len(ring.sortedHashes))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("default constructor uses default config", func(t *testing.T) {
|
||||
ring := NewConsistentHashRing()
|
||||
|
||||
ring.AddNode("test-node")
|
||||
|
||||
if len(ring.sortedHashes) != DefaultVirtualNodes {
|
||||
t.Errorf("expected %d virtual nodes, got %d", DefaultVirtualNodes, len(ring.sortedHashes))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewShardManagerWithConfig(t *testing.T) {
|
||||
t.Run("custom shard count", func(t *testing.T) {
|
||||
config := ShardConfig{ShardCount: 256, ReplicationFactor: 2}
|
||||
sm := NewShardManagerWithConfig(config)
|
||||
|
||||
if sm.GetShardCount() != 256 {
|
||||
t.Errorf("expected shard count 256, got %d", sm.GetShardCount())
|
||||
}
|
||||
if sm.GetReplicationFactor() != 2 {
|
||||
t.Errorf("expected replication factor 2, got %d", sm.GetReplicationFactor())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("zero values use defaults", func(t *testing.T) {
|
||||
config := ShardConfig{ShardCount: 0, ReplicationFactor: 0}
|
||||
sm := NewShardManagerWithConfig(config)
|
||||
|
||||
if sm.GetShardCount() != DefaultNumShards {
|
||||
t.Errorf("expected shard count %d, got %d", DefaultNumShards, sm.GetShardCount())
|
||||
}
|
||||
if sm.GetReplicationFactor() != 1 {
|
||||
t.Errorf("expected replication factor 1, got %d", sm.GetReplicationFactor())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("legacy constructor still works", func(t *testing.T) {
|
||||
sm := NewShardManager(512, 3)
|
||||
|
||||
if sm.GetShardCount() != 512 {
|
||||
t.Errorf("expected shard count 512, got %d", sm.GetShardCount())
|
||||
}
|
||||
if sm.GetReplicationFactor() != 3 {
|
||||
t.Errorf("expected replication factor 3, got %d", sm.GetReplicationFactor())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestShardManagerGetShard_DifferentShardCounts(t *testing.T) {
|
||||
testCases := []struct {
|
||||
shardCount int
|
||||
}{
|
||||
{shardCount: 16},
|
||||
{shardCount: 64},
|
||||
{shardCount: 256},
|
||||
{shardCount: 1024},
|
||||
{shardCount: 4096},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run("shardCount="+string(rune(tc.shardCount)), func(t *testing.T) {
|
||||
sm := NewShardManagerWithConfig(ShardConfig{ShardCount: tc.shardCount})
|
||||
|
||||
// Verify all actor IDs map to valid shard range
|
||||
for i := 0; i < 1000; i++ {
|
||||
actorID := "actor-" + string(rune(i))
|
||||
shard := sm.GetShard(actorID)
|
||||
if shard < 0 || shard >= tc.shardCount {
|
||||
t.Errorf("shard %d out of range [0, %d)", shard, tc.shardCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user