2828
2929
3030class TopologyDescription (object ):
31- def __init__ (self , topology_type , server_descriptions , replica_set_name ):
31+ def __init__ (
32+ self ,
33+ topology_type ,
34+ server_descriptions ,
35+ replica_set_name ,
36+ max_election_id ):
3237 """Represent a topology of servers.
3338
3439 :Parameters:
3540 - `topology_type`: initial type
3641 - `server_descriptions`: dict of (address, ServerDescription) for
3742 all seeds
3843 - `replica_set_name`: replica set name or None
44+ - `max_election_id`: greatest electionId seen from a primary, or None
3945 """
4046 self ._topology_type = topology_type
4147 self ._replica_set_name = replica_set_name
4248 self ._server_descriptions = server_descriptions
49+ self ._max_election_id = max_election_id
4350
4451 # Is PyMongo compatible with all servers' wire protocols?
4552 self ._incompatible_err = None
@@ -96,7 +103,11 @@ def reset(self):
96103 sds = dict ((address , ServerDescription (address ))
97104 for address in self ._server_descriptions )
98105
99- return TopologyDescription (topology_type , sds , self ._replica_set_name )
106+ return TopologyDescription (
107+ topology_type ,
108+ sds ,
109+ self ._replica_set_name ,
110+ self ._max_election_id )
100111
101112 def server_descriptions (self ):
102113 """Dict of (address, ServerDescription)."""
@@ -111,6 +122,11 @@ def replica_set_name(self):
111122 """The replica set name."""
112123 return self ._replica_set_name
113124
125+ @property
126+ def max_election_id (self ):
127+ """Greatest electionId seen from a primary, or None."""
128+ return self ._max_election_id
129+
114130 @property
115131 def known_servers (self ):
116132 """List of Servers of types besides Unknown."""
@@ -146,6 +162,7 @@ def updated_topology_description(topology_description, server_description):
146162 # TopologyDescription.
147163 topology_type = topology_description .topology_type
148164 set_name = topology_description .replica_set_name
165+ max_election_id = topology_description .max_election_id
149166 server_type = server_description .server_type
150167
151168 # Don't mutate the original dict of server descriptions; copy it.
@@ -156,7 +173,11 @@ def updated_topology_description(topology_description, server_description):
156173
157174 if topology_type == TOPOLOGY_TYPE .Single :
158175 # Single type never changes.
159- return TopologyDescription (TOPOLOGY_TYPE .Single , sds , set_name )
176+ return TopologyDescription (
177+ TOPOLOGY_TYPE .Single ,
178+ sds ,
179+ set_name ,
180+ max_election_id )
160181
161182 if topology_type == TOPOLOGY_TYPE .Unknown :
162183 if server_type == SERVER_TYPE .Standalone :
@@ -174,8 +195,8 @@ def updated_topology_description(topology_description, server_description):
174195 sds .pop (address )
175196
176197 elif server_type == SERVER_TYPE .RSPrimary :
177- topology_type , set_name = _update_rs_from_primary (
178- sds , set_name , server_description )
198+ topology_type , set_name , max_election_id = _update_rs_from_primary (
199+ sds , set_name , server_description , max_election_id )
179200
180201 elif server_type in (
181202 SERVER_TYPE .RSSecondary ,
@@ -190,8 +211,8 @@ def updated_topology_description(topology_description, server_description):
190211 topology_type = _check_has_primary (sds )
191212
192213 elif server_type == SERVER_TYPE .RSPrimary :
193- topology_type , set_name = _update_rs_from_primary (
194- sds , set_name , server_description )
214+ topology_type , set_name , max_election_id = _update_rs_from_primary (
215+ sds , set_name , server_description , max_election_id )
195216
196217 elif server_type in (
197218 SERVER_TYPE .RSSecondary ,
@@ -205,16 +226,21 @@ def updated_topology_description(topology_description, server_description):
205226 topology_type = _check_has_primary (sds )
206227
207228 # Return updated copy.
208- return TopologyDescription (topology_type , sds , set_name )
229+ return TopologyDescription (topology_type , sds , set_name , max_election_id )
209230
210231
211- def _update_rs_from_primary (sds , replica_set_name , server_description ):
232+ def _update_rs_from_primary (
233+ sds ,
234+ replica_set_name ,
235+ server_description ,
236+ max_election_id ):
212237 """Update topology description from a primary's ismaster response.
213238
214- Pass in a dict of ServerDescriptions, current replica set name, and the
215- ServerDescription we are processing.
239+ Pass in a dict of ServerDescriptions, current replica set name, the
240+ ServerDescription we are processing, and the TopologyDescription's
241+ max_election_id if any.
216242
217- Returns (new topology type, new replica_set_name).
243+ Returns (new topology type, new replica_set_name, new max_election_id ).
218244 """
219245 if replica_set_name is None :
220246 replica_set_name = server_description .replica_set_name
@@ -223,7 +249,16 @@ def _update_rs_from_primary(sds, replica_set_name, server_description):
223249 # We found a primary but it doesn't have the replica_set_name
224250 # provided by the user.
225251 sds .pop (server_description .address )
226- return _check_has_primary (sds ), replica_set_name
252+ return _check_has_primary (sds ), replica_set_name , max_election_id
253+
254+ if server_description .election_id is not None :
255+ if max_election_id and max_election_id > server_description .election_id :
256+ # Stale primary, set to type Unknown.
257+ address = server_description .address
258+ sds [address ] = ServerDescription (address )
259+ return _check_has_primary (sds ), replica_set_name , max_election_id
260+
261+ max_election_id = server_description .election_id
227262
228263 # We've heard from the primary. Is it the same primary as before?
229264 for server in sds .values ():
@@ -247,7 +282,7 @@ def _update_rs_from_primary(sds, replica_set_name, server_description):
247282
248283 # If the host list differs from the seed list, we may not have a primary
249284 # after all.
250- return _check_has_primary (sds ), replica_set_name
285+ return _check_has_primary (sds ), replica_set_name , max_election_id
251286
252287
253288def _update_rs_with_primary_from_member (
0 commit comments