diff --git a/nodeconf/configuration.go b/nodeconf/configuration.go index af8e5f33..c3b511b9 100644 --- a/nodeconf/configuration.go +++ b/nodeconf/configuration.go @@ -1,7 +1,10 @@ //go:generate mockgen -destination mock_nodeconf/mock_nodeconf.go github.com/anytypeio/any-sync/nodeconf Service,Configuration package nodeconf -import "github.com/anytypeio/go-chash" +import ( + "github.com/anytypeio/go-chash" + "strings" +) type Configuration interface { // Id returns current nodeconf id @@ -18,6 +21,8 @@ type Configuration interface { Addresses() map[string][]string // CHash returns nodes consistent table CHash() chash.CHash + // Partition returns partition number by spaceId + Partition(spaceId string) (part int) } type configuration struct { @@ -34,7 +39,7 @@ func (c *configuration) Id() string { } func (c *configuration) NodeIds(spaceId string) []string { - members := c.chash.GetMembers(spaceId) + members := c.chash.GetMembers(ReplKey(spaceId)) res := make([]string, 0, len(members)) for _, m := range members { if m.Id() != c.accountId { @@ -45,7 +50,7 @@ func (c *configuration) NodeIds(spaceId string) []string { } func (c *configuration) IsResponsible(spaceId string) bool { - for _, m := range c.chash.GetMembers(spaceId) { + for _, m := range c.chash.GetMembers(ReplKey(spaceId)) { if m.Id() == c.accountId { return true } @@ -72,3 +77,14 @@ func (c *configuration) Addresses() map[string][]string { func (c *configuration) CHash() chash.CHash { return c.chash } + +func (c *configuration) Partition(spaceId string) (part int) { + return c.chash.GetPartition(ReplKey(spaceId)) +} + +func ReplKey(spaceId string) (replKey string) { + if i := strings.LastIndex(spaceId, "."); i != -1 { + return spaceId[i+1:] + } + return spaceId +} diff --git a/nodeconf/configuration_test.go b/nodeconf/configuration_test.go new file mode 100644 index 00000000..757a5bc3 --- /dev/null +++ b/nodeconf/configuration_test.go @@ -0,0 +1,71 @@ +package nodeconf + +import ( + "fmt" + "github.com/anytypeio/go-chash" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "math/rand" + "testing" +) + +func TestReplKey(t *testing.T) { + assert.Equal(t, "repl", ReplKey("id.repl")) + assert.Equal(t, "repl", ReplKey("repl")) + assert.Equal(t, "", ReplKey(".")) +} + +func TestConfiguration_NodeIds(t *testing.T) { + ch, err := chash.New(chash.Config{ + PartitionCount: partitionCount, + ReplicationFactor: replicationFactor, + }) + require.NoError(t, err) + conf := &configuration{ + id: "last", + accountId: "1", + chash: ch, + } + for i := 0; i < 10; i++ { + require.NoError(t, conf.chash.AddMembers(testMember(fmt.Sprint(i+1)))) + } + + t.Run("random keys", func(t *testing.T) { + for i := 0; i < 10; i++ { + spaceId := fmt.Sprintf("%d.%d", rand.Int(), rand.Int()) + members := conf.NodeIds(spaceId) + if conf.IsResponsible(spaceId) { + assert.Len(t, members, 2) + } else { + assert.Len(t, members, 3) + } + } + }) + t.Run("same repl key", func(t *testing.T) { + var prevMemb []string + for i := 0; i < 10; i++ { + spaceId := fmt.Sprintf("%d.%d", rand.Int(), 42) + members := conf.NodeIds(spaceId) + if conf.IsResponsible(spaceId) { + assert.Len(t, members, 2) + } else { + assert.Len(t, members, 3) + } + if i != 0 { + assert.Equal(t, prevMemb, members) + } + prevMemb = members + } + }) + +} + +type testMember string + +func (t testMember) Id() string { + return string(t) +} + +func (t testMember) Capacity() float64 { + return 1 +}