Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion ring/replication_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@
SupportsExpandedReplication() bool
}

type defaultReplicationStrategy struct{}
// ReplicationStrategyWithConsistency is an optional interface for creating a copy of the ReplicationStrategy with a different consistency level.
type ReplicationStrategyWithConsistency interface {
WithConsistency(consistencyLevel ConsistencyLevel) (ReplicationStrategy, error)
}

type defaultReplicationStrategy struct {
consistencyLevel ConsistencyLevel
}

func NewDefaultReplicationStrategy() ReplicationStrategy {
return &defaultReplicationStrategy{}
Expand All @@ -39,6 +46,9 @@
}

minSuccess := (replicationFactor / 2) + 1
if s.consistencyLevel == ConsistencyAny {
minSuccess = 1
}
now := time.Now()

// Skip those that have not heartbeated in a while. NB these are still
Expand All @@ -54,6 +64,13 @@
}
}

if len(instances) < minSuccess && s.consistencyLevel == ConsistencyRelaxedQuorum {
minSuccess = len(instances)
if len(instances) == 0 {
minSuccess = 1
}
}

// This is just a shortcut - if there are not minSuccess available instances,
// after filtering out dead ones, don't even bother trying.
if len(instances) < minSuccess {
Expand Down Expand Up @@ -83,6 +100,10 @@
return false
}

func (s *defaultReplicationStrategy) WithConsistency(consistencyLevel ConsistencyLevel) (ReplicationStrategy, error) {
return &defaultReplicationStrategy{consistencyLevel: consistencyLevel}, nil
}

type ignoreUnhealthyInstancesReplicationStrategy struct{}

func NewIgnoreUnhealthyInstancesReplicationStrategy() ReplicationStrategy {
Expand Down Expand Up @@ -118,6 +139,10 @@
return true
}

func (r *ignoreUnhealthyInstancesReplicationStrategy) WithConsistency(consistencyLevel ConsistencyLevel) (ReplicationStrategy, error) {
return r, nil
}

func (r *Ring) IsHealthy(instance *InstanceDesc, op Operation, now time.Time) bool {
return instance.IsHealthy(op, r.cfg.HeartbeatTimeout, now)
}
Expand All @@ -126,3 +151,12 @@
func (r *Ring) ReplicationFactor() int {
return r.cfg.ReplicationFactor
}

func withConsistency(strategy ReplicationStrategy, consistencyLevel ConsistencyLevel) (ReplicationStrategy, error) {
withConsistency, hasConsistency := strategy.(ReplicationStrategyWithConsistency)
if !hasConsistency {
return nil, fmt.Errorf("Replication strategy does not support required consistency level: %d", consistencyLevel)

Check failure on line 158 in ring/replication_strategy.go

View workflow job for this annotation

GitHub Actions / Check

ST1005: error strings should not be capitalized (staticcheck)
}

return withConsistency.WithConsistency(consistencyLevel)
}
73 changes: 72 additions & 1 deletion ring/replication_strategy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestRingReplicationStrategy(t *testing.T) {
for i, tc := range []struct {
replicationFactor, liveIngesters, deadIngesters int
consistencyLevel ConsistencyLevel
expectedMaxFailure int
expectedError string
}{
Expand All @@ -26,20 +28,38 @@ func TestRingReplicationStrategy(t *testing.T) {
deadIngesters: 1,
expectedError: "at least 1 live replicas required, could only find 0 - unhealthy instances: dead1",
},
{
replicationFactor: 1,
deadIngesters: 1,
consistencyLevel: ConsistencyRelaxedQuorum,
expectedError: "at least 1 live replicas required, could only find 0 - unhealthy instances: dead1",
},

// Ensure it works for RF=3 and 2 ingesters.
{
replicationFactor: 3,
liveIngesters: 2,
expectedMaxFailure: 0,
},
{
replicationFactor: 3,
liveIngesters: 2,
consistencyLevel: ConsistencyAny,
expectedMaxFailure: 1,
},

// Ensure it works for the default production config.
{
replicationFactor: 3,
liveIngesters: 3,
expectedMaxFailure: 1,
},
{
replicationFactor: 3,
liveIngesters: 3,
consistencyLevel: ConsistencyAny,
expectedMaxFailure: 2,
},

{
replicationFactor: 3,
Expand All @@ -54,6 +74,41 @@ func TestRingReplicationStrategy(t *testing.T) {
deadIngesters: 2,
expectedError: "at least 2 live replicas required, could only find 1 - unhealthy instances: dead1,dead2",
},
{
replicationFactor: 3,
liveIngesters: 1,
deadIngesters: 2,
consistencyLevel: ConsistencyRelaxedQuorum,
expectedMaxFailure: 0,
},
{
replicationFactor: 3,
liveIngesters: 1,
deadIngesters: 2,
consistencyLevel: ConsistencyAny,
expectedMaxFailure: 0,
},

{
replicationFactor: 3,
liveIngesters: 0,
deadIngesters: 3,
expectedError: "at least 2 live replicas required, could only find 0 - unhealthy instances: dead1,dead2,dead3",
},
{
replicationFactor: 3,
liveIngesters: 0,
deadIngesters: 3,
consistencyLevel: ConsistencyRelaxedQuorum,
expectedError: "at least 1 live replicas required, could only find 0 - unhealthy instances: dead1,dead2,dead3",
},
{
replicationFactor: 3,
liveIngesters: 0,
deadIngesters: 3,
consistencyLevel: ConsistencyAny,
expectedError: "at least 1 live replicas required, could only find 0 - unhealthy instances: dead1,dead2,dead3",
},

// Ensure it works when adding / removing nodes.

Expand All @@ -77,6 +132,20 @@ func TestRingReplicationStrategy(t *testing.T) {
deadIngesters: 2,
expectedError: "at least 3 live replicas required, could only find 2 - unhealthy instances: dead1,dead2",
},
{
replicationFactor: 3,
liveIngesters: 2,
deadIngesters: 2,
consistencyLevel: ConsistencyRelaxedQuorum,
expectedMaxFailure: 0,
},
{
replicationFactor: 3,
liveIngesters: 2,
deadIngesters: 2,
consistencyLevel: ConsistencyAny,
expectedMaxFailure: 1,
},
} {
ingesters := []InstanceDesc{}
for i := 0; i < tc.liveIngesters; i++ {
Expand All @@ -89,7 +158,9 @@ func TestRingReplicationStrategy(t *testing.T) {
}

t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) {
strategy := NewDefaultReplicationStrategy()
defaultStrategy := NewDefaultReplicationStrategy()
strategy, err := withConsistency(defaultStrategy, tc.consistencyLevel)
require.NoError(t, err)
liveIngesters, maxFailure, err := strategy.Filter(ingesters, Read, tc.replicationFactor, 100*time.Second, false)
if tc.expectedError == "" {
assert.NoError(t, err)
Expand Down
Loading
Loading