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  }