Skip to content
Merged
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
25 changes: 21 additions & 4 deletions lib/mongo/cluster/topology/replica_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def elect_primary(description, servers)
end
end
update_max_election_id(description)
update_max_set_version(description)
end
else
log_warn(
Expand All @@ -88,7 +89,8 @@ def elect_primary(description, servers)
# @since 2.0.0
def initialize(options, seeds = [])
@options = options
@max_election_id = 0
@max_election_id = nil
@max_set_version = nil
end

# A replica set topology is a replica set.
Expand Down Expand Up @@ -222,14 +224,29 @@ def standalone_discovered; self; end
private

def update_max_election_id(description)
if description.election_id && description.election_id > @max_election_id
if description.election_id &&
(@max_election_id.nil? ||
description.election_id > @max_election_id)
@max_election_id = description.election_id
end
end

def update_max_set_version(description)
if description.set_version &&
(@max_set_version.nil? ||
description.set_version > @max_set_version)
@max_set_version = description.set_version
end
end

def detect_stale_primary!(description)
if description.election_id && description.election_id < @max_election_id
description.unknown!
if description.election_id && description.set_version
if @max_set_version && @max_election_id &&
(description.set_version < @max_set_version ||
(description.set_version == @max_set_version &&
description.election_id < @max_election_id))
description.unknown!
end
end
end

Expand Down
21 changes: 19 additions & 2 deletions lib/mongo/server/description.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,16 @@ class Description
# @since 2.0.0
TAGS = 'tags'.freeze

# Constant for reading electionID info from config.
# Constant for reading electionId info from config.
#
# @since 2.1.0
ELECTION_ID = 'electionId'.freeze

# Constant for reading setVersion info from config.
#
# @since 2.2.2
SET_VERSION = 'setVersion'.freeze

# Constant for reading localTime info from config.
#
# @since 2.1.0
Expand All @@ -142,7 +147,7 @@ class Description
# Fields to exclude when comparing two descriptions.
#
# @since 2.0.6
EXCLUDE_FOR_COMPARISON = [ LOCAL_TIME, ELECTION_ID ].freeze
EXCLUDE_FOR_COMPARISON = [ LOCAL_TIME, ELECTION_ID, SET_VERSION ].freeze

# @return [ Address ] address The server's address.
attr_reader :address
Expand Down Expand Up @@ -343,6 +348,18 @@ def election_id
config[ELECTION_ID]
end

# Get the setVersion from the config.
#
# @example Get the setVersion.
# description.set_version
#
# @return [ Integer ] The set version.
#
# @since 2.2.2
def set_version
config[SET_VERSION]
end

# Is the server a mongos?
#
# @example Is the server a mongos?
Expand Down
4 changes: 4 additions & 0 deletions spec/support/sdam/rs/equal_electionids.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ phases: [
ismaster: true,
hosts: ["a:27017", "b:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
}],
["b:27017", {
ok: 1,
ismaster: true,
hosts: ["a:27017", "b:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
}]
],
Expand All @@ -29,11 +31,13 @@ phases: [
"a:27017": {
type: "Unknown",
setName: ,
setVersion: ,
electionId:
},
"b:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
}
},
Expand Down
8 changes: 7 additions & 1 deletion spec/support/sdam/rs/new_primary_new_electionid.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
description: "New primary with greater electionId"
description: "New primary with greater setVersion and electionId"

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

Expand All @@ -12,6 +12,7 @@ phases: [
ismaster: true,
hosts: ["a:27017", "b:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
}]
],
Expand All @@ -21,6 +22,7 @@ phases: [
"a:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
},
"b:27017": {
Expand All @@ -42,6 +44,7 @@ phases: [
ismaster: true,
hosts: ["a:27017", "b:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000002"}
}]
],
Expand All @@ -56,6 +59,7 @@ phases: [
"b:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000002"}
}
},
Expand All @@ -72,6 +76,7 @@ phases: [
ismaster: true,
hosts: ["a:27017", "b:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
}]
],
Expand All @@ -85,6 +90,7 @@ phases: [
"b:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000002"}
}
},
Expand Down
101 changes: 101 additions & 0 deletions spec/support/sdam/rs/new_primary_new_setversion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
description: "New primary with greater setVersion"

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

phases: [

# Primary A is discovered and tells us about B.
{
responses: [
["a:27017", {
ok: 1,
ismaster: true,
hosts: ["a:27017", "b:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
}]
],

outcome: {
servers: {
"a:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
},
"b:27017": {
type: "Unknown",
setName: ,
electionId:
}
},
topologyType: "ReplicaSetWithPrimary",
setName: "rs",
}
},

# RS is reconfigured and B is elected.
{
responses: [
["b:27017", {
ok: 1,
ismaster: true,
hosts: ["a:27017", "b:27017"],
setName: "rs",
setVersion: 2,
electionId: {"$oid": "000000000000000000000001"}
}]
],

outcome: {
servers: {
"a:27017": {
type: "Unknown",
setName: ,
electionId:
},
"b:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 2,
electionId: {"$oid": "000000000000000000000001"}
}
},
topologyType: "ReplicaSetWithPrimary",
setName: "rs",
}
},

# A still claims to be primary but it's ignored.
{
responses: [
["a:27017", {
ok: 1,
ismaster: true,
hosts: ["a:27017", "b:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
}]
],
outcome: {
servers: {
"a:27017": {
type: "Unknown",
setName: ,
electionId:
},
"b:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 2,
electionId: {"$oid": "000000000000000000000002"}
}
},
topologyType: "ReplicaSetWithPrimary",
setName: "rs",
}
}
]
9 changes: 8 additions & 1 deletion spec/support/sdam/rs/null_election_id.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ phases: [
ok: 1,
ismaster: true,
hosts: ["a:27017", "b:27017", "c:27017"],
setVersion: 1,
setName: "rs"
}]
],
Expand All @@ -20,6 +21,7 @@ phases: [
"a:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 1,
electionId:
},
"b:27017": {
Expand All @@ -46,6 +48,7 @@ phases: [
ismaster: true,
hosts: ["a:27017", "b:27017", "c:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000002"}
}]
],
Expand All @@ -60,6 +63,7 @@ phases: [
"b:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000002"}
},
"c:27017": {
Expand All @@ -80,6 +84,7 @@ phases: [
ok: 1,
ismaster: true,
hosts: ["a:27017", "b:27017", "c:27017"],
setVersion: 1,
setName: "rs"
}]
],
Expand All @@ -88,6 +93,7 @@ phases: [
"a:27017": {
type: "RSPrimary",
setName: "rs",
setVersion: 1,
electionId:
},
"b:27017": {
Expand All @@ -106,7 +112,7 @@ phases: [
}
},

# But we remember A's electionId, so when we finally hear from C
# But we remember B's electionId, so when we finally hear from C
# claiming it is primary, we ignore it due to its outdated electionId
{
responses: [
Expand All @@ -115,6 +121,7 @@ phases: [
ismaster: true,
hosts: ["a:27017", "b:27017", "c:27017"],
setName: "rs",
setVersion: 1,
electionId: {"$oid": "000000000000000000000001"}
}]
],
Expand Down
Loading