Skip to content

Commit

Permalink
Merge pull request #7 from hashicorp/detect-leader-via-delegate
Browse files Browse the repository at this point in the history
Detect leader via the delegate
  • Loading branch information
vishalnayak authored Apr 23, 2021
2 parents 04f58f0 + 85ecb4f commit 839ebcd
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 8 deletions.
27 changes: 19 additions & 8 deletions state.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,30 @@ func (a *Autopilot) gatherNextStateInputs(ctx context.Context) (*nextStateInputs
}
inputs.RaftConfig = raftConfig

leader := a.raft.Leader()
for _, s := range inputs.RaftConfig.Servers {
if s.Address == leader {
inputs.LeaderID = s.ID
// get the known servers which may include left/failed ones
inputs.KnownServers = a.delegate.KnownServers()

// Try to retrieve leader id from the delegate.
for id, srv := range inputs.KnownServers {
if srv.IsLeader {
inputs.LeaderID = id
break
}
}

// Delegate setting the leader information is optional. If leader detection is
// not successful, fallback on raft config to do the same.
if inputs.LeaderID == "" {
return nil, fmt.Errorf("cannot detect the current leader server id from its address: %s", leader)
leader := a.raft.Leader()
for _, s := range inputs.RaftConfig.Servers {
if s.Address == leader {
inputs.LeaderID = s.ID
break
}
}
if inputs.LeaderID == "" {
return nil, fmt.Errorf("cannot detect the current leader server id from its address: %s", leader)
}
}

// get the latest Raft index - this should be kept close to the call to
Expand All @@ -101,9 +115,6 @@ func (a *Autopilot) gatherNextStateInputs(ctx context.Context) (*nextStateInputs
return nil, ctx.Err()
}

// get the known servers which may include left/failed ones
inputs.KnownServers = a.delegate.KnownServers()

// in most cases getting the known servers should be quick but as we cannot
// account for every potential delegate and prevent them from making
// blocking network requests we should probably check the context again.
Expand Down
93 changes: 93 additions & 0 deletions state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,99 @@ func TestAliveServers(t *testing.T) {
require.Equal(t, expected, actual)
}

func TestGatherNextStateInputsLeaderFromDelegate(t *testing.T) {
mtime := NewMockTimeProvider(t)
mraft := NewMockRaft(t)
mdel := NewMockApplicationIntegration(t)

ap := New(mraft, mdel, withTimeProvider(mtime))
ap.startTime = time.Date(2020, 11, 2, 12, 0, 0, 0, time.UTC)
ap.state = &State{Healthy: false}

now := time.Date(2020, 11, 02, 12, 0, 0, 5000, time.UTC)
mtime.On("Now").Return(now).Once()

conf := &Config{
CleanupDeadServers: true,
LastContactThreshold: 200 * time.Millisecond,
MaxTrailingLogs: 200,
MinQuorum: 3,
ServerStabilizationTime: 10 * time.Second,
}

servers := map[raft.ServerID]*Server{
"7875975d-d54b-49c1-a400-9fefcc706c67": {
ID: "7875975d-d54b-49c1-a400-9fefcc706c67",
Name: "node1",
Address: "198.18.0.1:8300",
NodeStatus: NodeAlive,
Version: "1.9.0",
RaftVersion: 3,
IsLeader: true,
},
"ecfc5237-63c3-4b09-94b9-d5682d9ae5b1": {
ID: "ecfc5237-63c3-4b09-94b9-d5682d9ae5b1",
Name: "node2",
Address: "198.18.0.2:8300",
NodeStatus: NodeAlive,
Version: "1.9.0",
RaftVersion: 3,
},
"e72eb8da-604d-47cd-bd7f-69ec120ea2b7": {
ID: "e72eb8da-604d-47cd-bd7f-69ec120ea2b7",
Name: "node3",
Address: "198.18.0.3:8300",
NodeStatus: NodeAlive,
Version: "1.9.0",
RaftVersion: 3,
},
}
var lastIndex uint64 = 1024
var lastTerm uint64 = 3

serverStats := map[raft.ServerID]*ServerStats{
"7875975d-d54b-49c1-a400-9fefcc706c67": {
LastTerm: lastTerm,
LastIndex: lastIndex,
},
"ecfc5237-63c3-4b09-94b9-d5682d9ae5b1": {
LastContact: 10 * time.Millisecond,
LastTerm: lastTerm,
LastIndex: 1000,
},
"e72eb8da-604d-47cd-bd7f-69ec120ea2b7": {
LastContact: 15 * time.Millisecond,
LastTerm: lastTerm,
LastIndex: 999,
},
}

var leaderID raft.ServerID = "7875975d-d54b-49c1-a400-9fefcc706c67"

mdel.On("AutopilotConfig").Return(conf).Once()
mraft.On("GetConfiguration").Return(&raftConfigFuture{config: test3VoterRaftConfiguration}).Once()
mdel.On("KnownServers").Return(servers).Once()
mraft.On("LastIndex").Return(lastIndex).Once()
mraft.On("Stats").Return(map[string]string{"last_log_term": "3"}).Once()
mdel.On("FetchServerStats", mock.Anything, servers).Return(serverStats).Once()

expected := &nextStateInputs{
Now: now,
StartTime: ap.startTime,
Config: conf,
RaftConfig: &test3VoterRaftConfiguration,
KnownServers: servers,
LatestIndex: lastIndex,
LastTerm: lastTerm,
FetchedStats: serverStats,
LeaderID: leaderID,
}

actual, err := ap.gatherNextStateInputs(context.Background())
require.NoError(t, err)
require.Equal(t, expected, actual)
}

func TestGatherNextStateInputs(t *testing.T) {
mtime := NewMockTimeProvider(t)
mraft := NewMockRaft(t)
Expand Down
3 changes: 3 additions & 0 deletions testdata/non-voter/state.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -34,6 +35,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -57,6 +59,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand Down
3 changes: 3 additions & 0 deletions testdata/one-failed/state.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -34,6 +35,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -57,6 +59,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand Down
3 changes: 3 additions & 0 deletions testdata/staging/state.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -34,6 +35,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -57,6 +59,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand Down
3 changes: 3 additions & 0 deletions testdata/state-overrides/state.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -34,6 +35,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -57,6 +59,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand Down
3 changes: 3 additions & 0 deletions testdata/typical/state.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -34,6 +35,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand All @@ -57,6 +59,7 @@
"Version": "1.9.0",
"Meta": null,
"RaftVersion": 3,
"IsLeader": false,
"NodeType": "voter",
"Ext": null
},
Expand Down
1 change: 1 addition & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type Server struct {
Version string
Meta map[string]string
RaftVersion int
IsLeader bool

// The remaining fields are those that the promoter
// will fill in
Expand Down

0 comments on commit 839ebcd

Please sign in to comment.