github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/x/mongo/driver/topology/fsm.go (about) 1 // Copyright (C) MongoDB, Inc. 2017-present. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7 package topology 8 9 import ( 10 "bytes" 11 "fmt" 12 "sync/atomic" 13 14 "go.mongodb.org/mongo-driver/bson/primitive" 15 "go.mongodb.org/mongo-driver/mongo/address" 16 "go.mongodb.org/mongo-driver/mongo/description" 17 ) 18 19 var ( 20 // MinSupportedMongoDBVersion is the version string for the lowest MongoDB version supported by the driver. 21 MinSupportedMongoDBVersion = "3.6" 22 23 // SupportedWireVersions is the range of wire versions supported by the driver. 24 SupportedWireVersions = description.NewVersionRange(6, 21) 25 ) 26 27 type fsm struct { 28 description.Topology 29 maxElectionID primitive.ObjectID 30 maxSetVersion uint32 31 compatible atomic.Value 32 compatibilityErr error 33 } 34 35 func newFSM() *fsm { 36 f := fsm{} 37 f.compatible.Store(true) 38 return &f 39 } 40 41 // apply takes a new server description and modifies the FSM's topology description based on it. It returns the 42 // updated topology description as well as a server description. The returned server description is either the same 43 // one that was passed in, or a new one in the case that it had to be changed. 44 // 45 // apply should operation on immutable descriptions so we don't have to lock for the entire time we're applying the 46 // server description. 47 func (f *fsm) apply(s description.Server) (description.Topology, description.Server) { 48 newServers := make([]description.Server, len(f.Servers)) 49 copy(newServers, f.Servers) 50 51 oldMinutes := f.SessionTimeoutMinutes 52 f.Topology = description.Topology{ 53 Kind: f.Kind, 54 Servers: newServers, 55 SetName: f.SetName, 56 } 57 58 // For data bearing servers, set SessionTimeoutMinutes to the lowest among them 59 if oldMinutes == 0 { 60 // If timeout currently 0, check all servers to see if any still don't have a timeout 61 // If they all have timeout, pick the lowest. 62 timeout := s.SessionTimeoutMinutes 63 for _, server := range f.Servers { 64 if server.DataBearing() && server.SessionTimeoutMinutes < timeout { 65 timeout = server.SessionTimeoutMinutes 66 } 67 } 68 f.SessionTimeoutMinutes = timeout 69 } else { 70 if s.DataBearing() && oldMinutes > s.SessionTimeoutMinutes { 71 f.SessionTimeoutMinutes = s.SessionTimeoutMinutes 72 } else { 73 f.SessionTimeoutMinutes = oldMinutes 74 } 75 } 76 77 if _, ok := f.findServer(s.Addr); !ok { 78 return f.Topology, s 79 } 80 81 updatedDesc := s 82 switch f.Kind { 83 case description.Unknown: 84 updatedDesc = f.applyToUnknown(s) 85 case description.Sharded: 86 updatedDesc = f.applyToSharded(s) 87 case description.ReplicaSetNoPrimary: 88 updatedDesc = f.applyToReplicaSetNoPrimary(s) 89 case description.ReplicaSetWithPrimary: 90 updatedDesc = f.applyToReplicaSetWithPrimary(s) 91 case description.Single: 92 updatedDesc = f.applyToSingle(s) 93 } 94 95 for _, server := range f.Servers { 96 if server.WireVersion != nil { 97 if server.WireVersion.Max < SupportedWireVersions.Min { 98 f.compatible.Store(false) 99 f.compatibilityErr = fmt.Errorf( 100 "server at %s reports wire version %d, but this version of the Go driver requires "+ 101 "at least %d (MongoDB %s)", 102 server.Addr.String(), 103 server.WireVersion.Max, 104 SupportedWireVersions.Min, 105 MinSupportedMongoDBVersion, 106 ) 107 f.Topology.CompatibilityErr = f.compatibilityErr 108 return f.Topology, s 109 } 110 111 if server.WireVersion.Min > SupportedWireVersions.Max { 112 f.compatible.Store(false) 113 f.compatibilityErr = fmt.Errorf( 114 "server at %s requires wire version %d, but this version of the Go driver only supports up to %d", 115 server.Addr.String(), 116 server.WireVersion.Min, 117 SupportedWireVersions.Max, 118 ) 119 f.Topology.CompatibilityErr = f.compatibilityErr 120 return f.Topology, s 121 } 122 } 123 } 124 125 f.compatible.Store(true) 126 f.compatibilityErr = nil 127 return f.Topology, updatedDesc 128 } 129 130 func (f *fsm) applyToReplicaSetNoPrimary(s description.Server) description.Server { 131 switch s.Kind { 132 case description.Standalone, description.Mongos: 133 f.removeServerByAddr(s.Addr) 134 case description.RSPrimary: 135 f.updateRSFromPrimary(s) 136 case description.RSSecondary, description.RSArbiter, description.RSMember: 137 f.updateRSWithoutPrimary(s) 138 case description.Unknown, description.RSGhost: 139 f.replaceServer(s) 140 } 141 142 return s 143 } 144 145 func (f *fsm) applyToReplicaSetWithPrimary(s description.Server) description.Server { 146 switch s.Kind { 147 case description.Standalone, description.Mongos: 148 f.removeServerByAddr(s.Addr) 149 f.checkIfHasPrimary() 150 case description.RSPrimary: 151 f.updateRSFromPrimary(s) 152 case description.RSSecondary, description.RSArbiter, description.RSMember: 153 f.updateRSWithPrimaryFromMember(s) 154 case description.Unknown, description.RSGhost: 155 f.replaceServer(s) 156 f.checkIfHasPrimary() 157 } 158 159 return s 160 } 161 162 func (f *fsm) applyToSharded(s description.Server) description.Server { 163 switch s.Kind { 164 case description.Mongos, description.Unknown: 165 f.replaceServer(s) 166 case description.Standalone, description.RSPrimary, description.RSSecondary, description.RSArbiter, description.RSMember, description.RSGhost: 167 f.removeServerByAddr(s.Addr) 168 } 169 170 return s 171 } 172 173 func (f *fsm) applyToSingle(s description.Server) description.Server { 174 switch s.Kind { 175 case description.Unknown: 176 f.replaceServer(s) 177 case description.Standalone, description.Mongos: 178 if f.SetName != "" { 179 f.removeServerByAddr(s.Addr) 180 return s 181 } 182 183 f.replaceServer(s) 184 case description.RSPrimary, description.RSSecondary, description.RSArbiter, description.RSMember, description.RSGhost: 185 // A replica set name can be provided when creating a direct connection. In this case, if the set name returned 186 // by the hello response doesn't match up with the one provided during configuration, the server description 187 // is replaced with a default Unknown description. 188 // 189 // We create a new server description rather than doing s.Kind = description.Unknown because the other fields, 190 // such as RTT, need to be cleared for Unknown descriptions as well. 191 if f.SetName != "" && f.SetName != s.SetName { 192 s = description.Server{ 193 Addr: s.Addr, 194 Kind: description.Unknown, 195 } 196 } 197 198 f.replaceServer(s) 199 } 200 201 return s 202 } 203 204 func (f *fsm) applyToUnknown(s description.Server) description.Server { 205 switch s.Kind { 206 case description.Mongos: 207 f.setKind(description.Sharded) 208 f.replaceServer(s) 209 case description.RSPrimary: 210 f.updateRSFromPrimary(s) 211 case description.RSSecondary, description.RSArbiter, description.RSMember: 212 f.setKind(description.ReplicaSetNoPrimary) 213 f.updateRSWithoutPrimary(s) 214 case description.Standalone: 215 f.updateUnknownWithStandalone(s) 216 case description.Unknown, description.RSGhost: 217 f.replaceServer(s) 218 } 219 220 return s 221 } 222 223 func (f *fsm) checkIfHasPrimary() { 224 if _, ok := f.findPrimary(); ok { 225 f.setKind(description.ReplicaSetWithPrimary) 226 } else { 227 f.setKind(description.ReplicaSetNoPrimary) 228 } 229 } 230 231 // hasStalePrimary returns true if the topology has a primary that is "stale". 232 func hasStalePrimary(fsm fsm, srv description.Server) bool { 233 // Compare the election ID values of the server and the topology lexicographically. 234 compRes := bytes.Compare(srv.ElectionID[:], fsm.maxElectionID[:]) 235 236 if wireVersion := srv.WireVersion; wireVersion != nil && wireVersion.Max >= 17 { 237 // In the Post-6.0 case, a primary is considered "stale" if the server's election ID is greather than the 238 // topology's max election ID. In these versions, the primary is also considered "stale" if the server's 239 // election ID is LTE to the topologies election ID and the server's "setVersion" is less than the topology's 240 // max "setVersion". 241 return compRes == -1 || (compRes != 1 && srv.SetVersion < fsm.maxSetVersion) 242 } 243 244 // If the server's election ID is less than the topology's max election ID, the primary is considered 245 // "stale". Similarly, if the server's "setVersion" is less than the topology's max "setVersion", the 246 // primary is considered stale. 247 return compRes == -1 || fsm.maxSetVersion > srv.SetVersion 248 } 249 250 // transferEVTuple will transfer the ("ElectionID", "SetVersion") tuple from the description server to the topology. 251 // If the primary is stale, the tuple will not be transferred, the topology will update it's "Kind" value, and this 252 // routine will return "false". 253 func transferEVTuple(srv description.Server, fsm *fsm) bool { 254 stalePrimary := hasStalePrimary(*fsm, srv) 255 256 if wireVersion := srv.WireVersion; wireVersion != nil && wireVersion.Max >= 17 { 257 if stalePrimary { 258 fsm.checkIfHasPrimary() 259 return false 260 } 261 262 fsm.maxElectionID = srv.ElectionID 263 fsm.maxSetVersion = srv.SetVersion 264 265 return true 266 } 267 268 if srv.SetVersion != 0 && !srv.ElectionID.IsZero() { 269 if stalePrimary { 270 fsm.replaceServer(description.Server{ 271 Addr: srv.Addr, 272 LastError: fmt.Errorf( 273 "was a primary, but its set version or election id is stale"), 274 }) 275 276 fsm.checkIfHasPrimary() 277 278 return false 279 } 280 281 fsm.maxElectionID = srv.ElectionID 282 } 283 284 if srv.SetVersion > fsm.maxSetVersion { 285 fsm.maxSetVersion = srv.SetVersion 286 } 287 288 return true 289 } 290 291 func (f *fsm) updateRSFromPrimary(srv description.Server) { 292 if f.SetName == "" { 293 f.SetName = srv.SetName 294 } else if f.SetName != srv.SetName { 295 f.removeServerByAddr(srv.Addr) 296 f.checkIfHasPrimary() 297 298 return 299 } 300 301 if ok := transferEVTuple(srv, f); !ok { 302 return 303 } 304 305 if j, ok := f.findPrimary(); ok { 306 f.setServer(j, description.Server{ 307 Addr: f.Servers[j].Addr, 308 LastError: fmt.Errorf("was a primary, but a new primary was discovered"), 309 }) 310 } 311 312 f.replaceServer(srv) 313 314 for j := len(f.Servers) - 1; j >= 0; j-- { 315 found := false 316 for _, member := range srv.Members { 317 if member == f.Servers[j].Addr { 318 found = true 319 break 320 } 321 } 322 323 if !found { 324 f.removeServer(j) 325 } 326 } 327 328 for _, member := range srv.Members { 329 if _, ok := f.findServer(member); !ok { 330 f.addServer(member) 331 } 332 } 333 334 f.checkIfHasPrimary() 335 } 336 337 func (f *fsm) updateRSWithPrimaryFromMember(s description.Server) { 338 if f.SetName != s.SetName { 339 f.removeServerByAddr(s.Addr) 340 f.checkIfHasPrimary() 341 return 342 } 343 344 if s.Addr != s.CanonicalAddr { 345 f.removeServerByAddr(s.Addr) 346 f.checkIfHasPrimary() 347 return 348 } 349 350 f.replaceServer(s) 351 352 if _, ok := f.findPrimary(); !ok { 353 f.setKind(description.ReplicaSetNoPrimary) 354 } 355 } 356 357 func (f *fsm) updateRSWithoutPrimary(s description.Server) { 358 if f.SetName == "" { 359 f.SetName = s.SetName 360 } else if f.SetName != s.SetName { 361 f.removeServerByAddr(s.Addr) 362 return 363 } 364 365 for _, member := range s.Members { 366 if _, ok := f.findServer(member); !ok { 367 f.addServer(member) 368 } 369 } 370 371 if s.Addr != s.CanonicalAddr { 372 f.removeServerByAddr(s.Addr) 373 return 374 } 375 376 f.replaceServer(s) 377 } 378 379 func (f *fsm) updateUnknownWithStandalone(s description.Server) { 380 if len(f.Servers) > 1 { 381 f.removeServerByAddr(s.Addr) 382 return 383 } 384 385 f.setKind(description.Single) 386 f.replaceServer(s) 387 } 388 389 func (f *fsm) addServer(addr address.Address) { 390 f.Servers = append(f.Servers, description.Server{ 391 Addr: addr.Canonicalize(), 392 }) 393 } 394 395 func (f *fsm) findPrimary() (int, bool) { 396 for i, s := range f.Servers { 397 if s.Kind == description.RSPrimary { 398 return i, true 399 } 400 } 401 402 return 0, false 403 } 404 405 func (f *fsm) findServer(addr address.Address) (int, bool) { 406 canon := addr.Canonicalize() 407 for i, s := range f.Servers { 408 if canon == s.Addr { 409 return i, true 410 } 411 } 412 413 return 0, false 414 } 415 416 func (f *fsm) removeServer(i int) { 417 f.Servers = append(f.Servers[:i], f.Servers[i+1:]...) 418 } 419 420 func (f *fsm) removeServerByAddr(addr address.Address) { 421 if i, ok := f.findServer(addr); ok { 422 f.removeServer(i) 423 } 424 } 425 426 func (f *fsm) replaceServer(s description.Server) { 427 if i, ok := f.findServer(s.Addr); ok { 428 f.setServer(i, s) 429 } 430 } 431 432 func (f *fsm) setServer(i int, s description.Server) { 433 f.Servers[i] = s 434 } 435 436 func (f *fsm) setKind(k description.TopologyKind) { 437 f.Kind = k 438 }