Skip to content

Commit aeac33f

Browse files
committed
Merge pull request #740 from estolfo/RUBY-1077-set-version
RUBY-1077 Consider setVersion and electionId to detect stale primaries
2 parents 768ddaf + 633b7da commit aeac33f

11 files changed

+594
-9
lines changed

lib/mongo/cluster/topology/replica_set.rb

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def elect_primary(description, servers)
6868
end
6969
end
7070
update_max_election_id(description)
71+
update_max_set_version(description)
7172
end
7273
else
7374
log_warn(
@@ -88,7 +89,8 @@ def elect_primary(description, servers)
8889
# @since 2.0.0
8990
def initialize(options, seeds = [])
9091
@options = options
91-
@max_election_id = 0
92+
@max_election_id = nil
93+
@max_set_version = nil
9294
end
9395

9496
# A replica set topology is a replica set.
@@ -222,14 +224,29 @@ def standalone_discovered; self; end
222224
private
223225

224226
def update_max_election_id(description)
225-
if description.election_id && description.election_id > @max_election_id
227+
if description.election_id &&
228+
(@max_election_id.nil? ||
229+
description.election_id > @max_election_id)
226230
@max_election_id = description.election_id
227231
end
228232
end
229233

234+
def update_max_set_version(description)
235+
if description.set_version &&
236+
(@max_set_version.nil? ||
237+
description.set_version > @max_set_version)
238+
@max_set_version = description.set_version
239+
end
240+
end
241+
230242
def detect_stale_primary!(description)
231-
if description.election_id && description.election_id < @max_election_id
232-
description.unknown!
243+
if description.election_id && description.set_version
244+
if @max_set_version && @max_election_id &&
245+
(description.set_version < @max_set_version ||
246+
(description.set_version == @max_set_version &&
247+
description.election_id < @max_election_id))
248+
description.unknown!
249+
end
233250
end
234251
end
235252

lib/mongo/server/description.rb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,16 @@ class Description
129129
# @since 2.0.0
130130
TAGS = 'tags'.freeze
131131

132-
# Constant for reading electionID info from config.
132+
# Constant for reading electionId info from config.
133133
#
134134
# @since 2.1.0
135135
ELECTION_ID = 'electionId'.freeze
136136

137+
# Constant for reading setVersion info from config.
138+
#
139+
# @since 2.2.2
140+
SET_VERSION = 'setVersion'.freeze
141+
137142
# Constant for reading localTime info from config.
138143
#
139144
# @since 2.1.0
@@ -142,7 +147,7 @@ class Description
142147
# Fields to exclude when comparing two descriptions.
143148
#
144149
# @since 2.0.6
145-
EXCLUDE_FOR_COMPARISON = [ LOCAL_TIME, ELECTION_ID ].freeze
150+
EXCLUDE_FOR_COMPARISON = [ LOCAL_TIME, ELECTION_ID, SET_VERSION ].freeze
146151

147152
# @return [ Address ] address The server's address.
148153
attr_reader :address
@@ -343,6 +348,18 @@ def election_id
343348
config[ELECTION_ID]
344349
end
345350

351+
# Get the setVersion from the config.
352+
#
353+
# @example Get the setVersion.
354+
# description.set_version
355+
#
356+
# @return [ Integer ] The set version.
357+
#
358+
# @since 2.2.2
359+
def set_version
360+
config[SET_VERSION]
361+
end
362+
346363
# Is the server a mongos?
347364
#
348365
# @example Is the server a mongos?

spec/support/sdam/rs/equal_electionids.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ phases: [
1212
ismaster: true,
1313
hosts: ["a:27017", "b:27017"],
1414
setName: "rs",
15+
setVersion: 1,
1516
electionId: {"$oid": "000000000000000000000001"}
1617
}],
1718
["b:27017", {
1819
ok: 1,
1920
ismaster: true,
2021
hosts: ["a:27017", "b:27017"],
2122
setName: "rs",
23+
setVersion: 1,
2224
electionId: {"$oid": "000000000000000000000001"}
2325
}]
2426
],
@@ -29,11 +31,13 @@ phases: [
2931
"a:27017": {
3032
type: "Unknown",
3133
setName: ,
34+
setVersion: ,
3235
electionId:
3336
},
3437
"b:27017": {
3538
type: "RSPrimary",
3639
setName: "rs",
40+
setVersion: 1,
3741
electionId: {"$oid": "000000000000000000000001"}
3842
}
3943
},

spec/support/sdam/rs/new_primary_new_electionid.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
description: "New primary with greater electionId"
1+
description: "New primary with greater setVersion and electionId"
22

33
uri: "mongodb://a/?replicaSet=rs"
44

@@ -12,6 +12,7 @@ phases: [
1212
ismaster: true,
1313
hosts: ["a:27017", "b:27017"],
1414
setName: "rs",
15+
setVersion: 1,
1516
electionId: {"$oid": "000000000000000000000001"}
1617
}]
1718
],
@@ -21,6 +22,7 @@ phases: [
2122
"a:27017": {
2223
type: "RSPrimary",
2324
setName: "rs",
25+
setVersion: 1,
2426
electionId: {"$oid": "000000000000000000000001"}
2527
},
2628
"b:27017": {
@@ -42,6 +44,7 @@ phases: [
4244
ismaster: true,
4345
hosts: ["a:27017", "b:27017"],
4446
setName: "rs",
47+
setVersion: 1,
4548
electionId: {"$oid": "000000000000000000000002"}
4649
}]
4750
],
@@ -56,6 +59,7 @@ phases: [
5659
"b:27017": {
5760
type: "RSPrimary",
5861
setName: "rs",
62+
setVersion: 1,
5963
electionId: {"$oid": "000000000000000000000002"}
6064
}
6165
},
@@ -72,6 +76,7 @@ phases: [
7276
ismaster: true,
7377
hosts: ["a:27017", "b:27017"],
7478
setName: "rs",
79+
setVersion: 1,
7580
electionId: {"$oid": "000000000000000000000001"}
7681
}]
7782
],
@@ -85,6 +90,7 @@ phases: [
8590
"b:27017": {
8691
type: "RSPrimary",
8792
setName: "rs",
93+
setVersion: 1,
8894
electionId: {"$oid": "000000000000000000000002"}
8995
}
9096
},
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
description: "New primary with greater setVersion"
2+
3+
uri: "mongodb://a/?replicaSet=rs"
4+
5+
phases: [
6+
7+
# Primary A is discovered and tells us about B.
8+
{
9+
responses: [
10+
["a:27017", {
11+
ok: 1,
12+
ismaster: true,
13+
hosts: ["a:27017", "b:27017"],
14+
setName: "rs",
15+
setVersion: 1,
16+
electionId: {"$oid": "000000000000000000000001"}
17+
}]
18+
],
19+
20+
outcome: {
21+
servers: {
22+
"a:27017": {
23+
type: "RSPrimary",
24+
setName: "rs",
25+
setVersion: 1,
26+
electionId: {"$oid": "000000000000000000000001"}
27+
},
28+
"b:27017": {
29+
type: "Unknown",
30+
setName: ,
31+
electionId:
32+
}
33+
},
34+
topologyType: "ReplicaSetWithPrimary",
35+
setName: "rs",
36+
}
37+
},
38+
39+
# RS is reconfigured and B is elected.
40+
{
41+
responses: [
42+
["b:27017", {
43+
ok: 1,
44+
ismaster: true,
45+
hosts: ["a:27017", "b:27017"],
46+
setName: "rs",
47+
setVersion: 2,
48+
electionId: {"$oid": "000000000000000000000001"}
49+
}]
50+
],
51+
52+
outcome: {
53+
servers: {
54+
"a:27017": {
55+
type: "Unknown",
56+
setName: ,
57+
electionId:
58+
},
59+
"b:27017": {
60+
type: "RSPrimary",
61+
setName: "rs",
62+
setVersion: 2,
63+
electionId: {"$oid": "000000000000000000000001"}
64+
}
65+
},
66+
topologyType: "ReplicaSetWithPrimary",
67+
setName: "rs",
68+
}
69+
},
70+
71+
# A still claims to be primary but it's ignored.
72+
{
73+
responses: [
74+
["a:27017", {
75+
ok: 1,
76+
ismaster: true,
77+
hosts: ["a:27017", "b:27017"],
78+
setName: "rs",
79+
setVersion: 1,
80+
electionId: {"$oid": "000000000000000000000001"}
81+
}]
82+
],
83+
outcome: {
84+
servers: {
85+
"a:27017": {
86+
type: "Unknown",
87+
setName: ,
88+
electionId:
89+
},
90+
"b:27017": {
91+
type: "RSPrimary",
92+
setName: "rs",
93+
setVersion: 2,
94+
electionId: {"$oid": "000000000000000000000002"}
95+
}
96+
},
97+
topologyType: "ReplicaSetWithPrimary",
98+
setName: "rs",
99+
}
100+
}
101+
]

spec/support/sdam/rs/null_election_id.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ phases: [
1111
ok: 1,
1212
ismaster: true,
1313
hosts: ["a:27017", "b:27017", "c:27017"],
14+
setVersion: 1,
1415
setName: "rs"
1516
}]
1617
],
@@ -20,6 +21,7 @@ phases: [
2021
"a:27017": {
2122
type: "RSPrimary",
2223
setName: "rs",
24+
setVersion: 1,
2325
electionId:
2426
},
2527
"b:27017": {
@@ -46,6 +48,7 @@ phases: [
4648
ismaster: true,
4749
hosts: ["a:27017", "b:27017", "c:27017"],
4850
setName: "rs",
51+
setVersion: 1,
4952
electionId: {"$oid": "000000000000000000000002"}
5053
}]
5154
],
@@ -60,6 +63,7 @@ phases: [
6063
"b:27017": {
6164
type: "RSPrimary",
6265
setName: "rs",
66+
setVersion: 1,
6367
electionId: {"$oid": "000000000000000000000002"}
6468
},
6569
"c:27017": {
@@ -80,6 +84,7 @@ phases: [
8084
ok: 1,
8185
ismaster: true,
8286
hosts: ["a:27017", "b:27017", "c:27017"],
87+
setVersion: 1,
8388
setName: "rs"
8489
}]
8590
],
@@ -88,6 +93,7 @@ phases: [
8893
"a:27017": {
8994
type: "RSPrimary",
9095
setName: "rs",
96+
setVersion: 1,
9197
electionId:
9298
},
9399
"b:27017": {
@@ -106,7 +112,7 @@ phases: [
106112
}
107113
},
108114

109-
# But we remember A's electionId, so when we finally hear from C
115+
# But we remember B's electionId, so when we finally hear from C
110116
# claiming it is primary, we ignore it due to its outdated electionId
111117
{
112118
responses: [
@@ -115,6 +121,7 @@ phases: [
115121
ismaster: true,
116122
hosts: ["a:27017", "b:27017", "c:27017"],
117123
setName: "rs",
124+
setVersion: 1,
118125
electionId: {"$oid": "000000000000000000000001"}
119126
}]
120127
],

0 commit comments

Comments
 (0)