github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/x/mongo/driver/topology/topology.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 contains types that handles the discovery, monitoring, and selection
     8  // of servers. This package is designed to expose enough inner workings of service discovery
     9  // and monitoring to allow low level applications to have fine grained control, while hiding
    10  // most of the detailed implementation of the algorithms.
    11  package topology // import "go.mongodb.org/mongo-driver/x/mongo/driver/topology"
    12  
    13  import (
    14  	"context"
    15  	"errors"
    16  	"fmt"
    17  	"net"
    18  	"net/url"
    19  	"strings"
    20  	"sync"
    21  	"sync/atomic"
    22  	"time"
    23  
    24  	"go.mongodb.org/mongo-driver/bson/primitive"
    25  	"go.mongodb.org/mongo-driver/event"
    26  	"go.mongodb.org/mongo-driver/internal/randutil"
    27  	"go.mongodb.org/mongo-driver/mongo/address"
    28  	"go.mongodb.org/mongo-driver/mongo/description"
    29  	"go.mongodb.org/mongo-driver/mongo/options"
    30  	"go.mongodb.org/mongo-driver/x/mongo/driver"
    31  	"go.mongodb.org/mongo-driver/x/mongo/driver/dns"
    32  )
    33  
    34  // Topology state constants.
    35  const (
    36  	topologyDisconnected int64 = iota
    37  	topologyDisconnecting
    38  	topologyConnected
    39  	topologyConnecting
    40  )
    41  
    42  // ErrSubscribeAfterClosed is returned when a user attempts to subscribe to a
    43  // closed Server or Topology.
    44  var ErrSubscribeAfterClosed = errors.New("cannot subscribe after closeConnection")
    45  
    46  // ErrTopologyClosed is returned when a user attempts to call a method on a
    47  // closed Topology.
    48  var ErrTopologyClosed = errors.New("topology is closed")
    49  
    50  // ErrTopologyConnected is returned whena  user attempts to Connect to an
    51  // already connected Topology.
    52  var ErrTopologyConnected = errors.New("topology is connected or connecting")
    53  
    54  // ErrServerSelectionTimeout is returned from server selection when the server
    55  // selection process took longer than allowed by the timeout.
    56  var ErrServerSelectionTimeout = errors.New("server selection timeout")
    57  
    58  // MonitorMode represents the way in which a server is monitored.
    59  type MonitorMode uint8
    60  
    61  // random is a package-global pseudo-random number generator.
    62  var random = randutil.NewLockedRand()
    63  
    64  // These constants are the available monitoring modes.
    65  const (
    66  	AutomaticMode MonitorMode = iota
    67  	SingleMode
    68  )
    69  
    70  // Topology represents a MongoDB deployment.
    71  type Topology struct {
    72  	state int64
    73  
    74  	cfg *Config
    75  
    76  	desc atomic.Value // holds a description.Topology
    77  
    78  	dnsResolver *dns.Resolver
    79  
    80  	done chan struct{}
    81  
    82  	pollingRequired   bool
    83  	pollingDone       chan struct{}
    84  	pollingwg         sync.WaitGroup
    85  	rescanSRVInterval time.Duration
    86  	pollHeartbeatTime atomic.Value // holds a bool
    87  
    88  	updateCallback updateTopologyCallback
    89  	fsm            *fsm
    90  
    91  	// This should really be encapsulated into it's own type. This will likely
    92  	// require a redesign so we can share a minimum of data between the
    93  	// subscribers and the topology.
    94  	subscribers         map[uint64]chan description.Topology
    95  	currentSubscriberID uint64
    96  	subscriptionsClosed bool
    97  	subLock             sync.Mutex
    98  
    99  	// We should redesign how we Connect and handle individal servers. This is
   100  	// too difficult to maintain and it's rather easy to accidentally access
   101  	// the servers without acquiring the lock or checking if the servers are
   102  	// closed. This lock should also be an RWMutex.
   103  	serversLock   sync.Mutex
   104  	serversClosed bool
   105  	servers       map[address.Address]*Server
   106  
   107  	id primitive.ObjectID
   108  }
   109  
   110  var _ driver.Deployment = &Topology{}
   111  var _ driver.Subscriber = &Topology{}
   112  
   113  type serverSelectionState struct {
   114  	selector    description.ServerSelector
   115  	timeoutChan <-chan time.Time
   116  }
   117  
   118  func newServerSelectionState(selector description.ServerSelector, timeoutChan <-chan time.Time) serverSelectionState {
   119  	return serverSelectionState{
   120  		selector:    selector,
   121  		timeoutChan: timeoutChan,
   122  	}
   123  }
   124  
   125  // New creates a new topology. A "nil" config is interpreted as the default configuration.
   126  func New(cfg *Config) (*Topology, error) {
   127  	if cfg == nil {
   128  		var err error
   129  		cfg, err = NewConfig(options.Client(), nil)
   130  		if err != nil {
   131  			return nil, err
   132  		}
   133  	}
   134  
   135  	t := &Topology{
   136  		cfg:               cfg,
   137  		done:              make(chan struct{}),
   138  		pollingDone:       make(chan struct{}),
   139  		rescanSRVInterval: 60 * time.Second,
   140  		fsm:               newFSM(),
   141  		subscribers:       make(map[uint64]chan description.Topology),
   142  		servers:           make(map[address.Address]*Server),
   143  		dnsResolver:       dns.DefaultResolver,
   144  		id:                primitive.NewObjectID(),
   145  	}
   146  	t.desc.Store(description.Topology{})
   147  	t.updateCallback = func(desc description.Server) description.Server {
   148  		return t.apply(context.TODO(), desc)
   149  	}
   150  
   151  	if t.cfg.URI != "" {
   152  		t.pollingRequired = strings.HasPrefix(t.cfg.URI, "mongodb+srv://") && !t.cfg.LoadBalanced
   153  	}
   154  
   155  	t.publishTopologyOpeningEvent()
   156  
   157  	return t, nil
   158  }
   159  
   160  // Connect initializes a Topology and starts the monitoring process. This function
   161  // must be called to properly monitor the topology.
   162  func (t *Topology) Connect() error {
   163  	if !atomic.CompareAndSwapInt64(&t.state, topologyDisconnected, topologyConnecting) {
   164  		return ErrTopologyConnected
   165  	}
   166  
   167  	t.desc.Store(description.Topology{})
   168  	var err error
   169  	t.serversLock.Lock()
   170  
   171  	// A replica set name sets the initial topology type to ReplicaSetNoPrimary unless a direct connection is also
   172  	// specified, in which case the initial type is Single.
   173  	if t.cfg.ReplicaSetName != "" {
   174  		t.fsm.SetName = t.cfg.ReplicaSetName
   175  		t.fsm.Kind = description.ReplicaSetNoPrimary
   176  	}
   177  
   178  	// A direct connection unconditionally sets the topology type to Single.
   179  	if t.cfg.Mode == SingleMode {
   180  		t.fsm.Kind = description.Single
   181  	}
   182  
   183  	for _, a := range t.cfg.SeedList {
   184  		addr := address.Address(a).Canonicalize()
   185  		t.fsm.Servers = append(t.fsm.Servers, description.NewDefaultServer(addr))
   186  	}
   187  
   188  	switch {
   189  	case t.cfg.LoadBalanced:
   190  		// In LoadBalanced mode, we mock a series of events: TopologyDescriptionChanged from Unknown to LoadBalanced,
   191  		// ServerDescriptionChanged from Unknown to LoadBalancer, and then TopologyDescriptionChanged to reflect the
   192  		// previous ServerDescriptionChanged event. We publish all of these events here because we don't start server
   193  		// monitoring routines in this mode, so we have to mock state changes.
   194  
   195  		// Transition from Unknown with no servers to LoadBalanced with a single Unknown server.
   196  		t.fsm.Kind = description.LoadBalanced
   197  		t.publishTopologyDescriptionChangedEvent(description.Topology{}, t.fsm.Topology)
   198  
   199  		addr := address.Address(t.cfg.SeedList[0]).Canonicalize()
   200  		if err := t.addServer(addr); err != nil {
   201  			t.serversLock.Unlock()
   202  			return err
   203  		}
   204  
   205  		// Transition the server from Unknown to LoadBalancer.
   206  		newServerDesc := t.servers[addr].Description()
   207  		t.publishServerDescriptionChangedEvent(t.fsm.Servers[0], newServerDesc)
   208  
   209  		// Transition from LoadBalanced with an Unknown server to LoadBalanced with a LoadBalancer.
   210  		oldDesc := t.fsm.Topology
   211  		t.fsm.Servers = []description.Server{newServerDesc}
   212  		t.desc.Store(t.fsm.Topology)
   213  		t.publishTopologyDescriptionChangedEvent(oldDesc, t.fsm.Topology)
   214  	default:
   215  		// In non-LB mode, we only publish an initial TopologyDescriptionChanged event from Unknown with no servers to
   216  		// the current state (e.g. Unknown with one or more servers if we're discovering or Single with one server if
   217  		// we're connecting directly). Other events are published when state changes occur due to responses in the
   218  		// server monitoring goroutines.
   219  
   220  		newDesc := description.Topology{
   221  			Kind:                  t.fsm.Kind,
   222  			Servers:               t.fsm.Servers,
   223  			SessionTimeoutMinutes: t.fsm.SessionTimeoutMinutes,
   224  		}
   225  		t.desc.Store(newDesc)
   226  		t.publishTopologyDescriptionChangedEvent(description.Topology{}, t.fsm.Topology)
   227  		for _, a := range t.cfg.SeedList {
   228  			addr := address.Address(a).Canonicalize()
   229  			err = t.addServer(addr)
   230  			if err != nil {
   231  				t.serversLock.Unlock()
   232  				return err
   233  			}
   234  		}
   235  	}
   236  
   237  	t.serversLock.Unlock()
   238  	if t.pollingRequired {
   239  		uri, err := url.Parse(t.cfg.URI)
   240  		if err != nil {
   241  			return err
   242  		}
   243  		// sanity check before passing the hostname to resolver
   244  		if parsedHosts := strings.Split(uri.Host, ","); len(parsedHosts) != 1 {
   245  			return fmt.Errorf("URI with SRV must include one and only one hostname")
   246  		}
   247  		_, _, err = net.SplitHostPort(uri.Host)
   248  		if err == nil {
   249  			// we were able to successfully extract a port from the host,
   250  			// but should not be able to when using SRV
   251  			return fmt.Errorf("URI with srv must not include a port number")
   252  		}
   253  		go t.pollSRVRecords(uri.Host)
   254  		t.pollingwg.Add(1)
   255  	}
   256  
   257  	t.subscriptionsClosed = false // explicitly set in case topology was disconnected and then reconnected
   258  
   259  	atomic.StoreInt64(&t.state, topologyConnected)
   260  	return nil
   261  }
   262  
   263  // Disconnect closes the topology. It stops the monitoring thread and
   264  // closes all open subscriptions.
   265  func (t *Topology) Disconnect(ctx context.Context) error {
   266  	if !atomic.CompareAndSwapInt64(&t.state, topologyConnected, topologyDisconnecting) {
   267  		return ErrTopologyClosed
   268  	}
   269  
   270  	servers := make(map[address.Address]*Server)
   271  	t.serversLock.Lock()
   272  	t.serversClosed = true
   273  	for addr, server := range t.servers {
   274  		servers[addr] = server
   275  	}
   276  	t.serversLock.Unlock()
   277  
   278  	for _, server := range servers {
   279  		_ = server.Disconnect(ctx)
   280  		t.publishServerClosedEvent(server.address)
   281  	}
   282  
   283  	t.subLock.Lock()
   284  	for id, ch := range t.subscribers {
   285  		close(ch)
   286  		delete(t.subscribers, id)
   287  	}
   288  	t.subscriptionsClosed = true
   289  	t.subLock.Unlock()
   290  
   291  	if t.pollingRequired {
   292  		t.pollingDone <- struct{}{}
   293  		t.pollingwg.Wait()
   294  	}
   295  
   296  	t.desc.Store(description.Topology{})
   297  
   298  	atomic.StoreInt64(&t.state, topologyDisconnected)
   299  	t.publishTopologyClosedEvent()
   300  	return nil
   301  }
   302  
   303  // Description returns a description of the topology.
   304  func (t *Topology) Description() description.Topology {
   305  	td, ok := t.desc.Load().(description.Topology)
   306  	if !ok {
   307  		td = description.Topology{}
   308  	}
   309  	return td
   310  }
   311  
   312  // Kind returns the topology kind of this Topology.
   313  func (t *Topology) Kind() description.TopologyKind { return t.Description().Kind }
   314  
   315  // Subscribe returns a Subscription on which all updated description.Topologys
   316  // will be sent. The channel of the subscription will have a buffer size of one,
   317  // and will be pre-populated with the current description.Topology.
   318  // Subscribe implements the driver.Subscriber interface.
   319  func (t *Topology) Subscribe() (*driver.Subscription, error) {
   320  	if atomic.LoadInt64(&t.state) != topologyConnected {
   321  		return nil, errors.New("cannot subscribe to Topology that is not connected")
   322  	}
   323  	ch := make(chan description.Topology, 1)
   324  	td, ok := t.desc.Load().(description.Topology)
   325  	if !ok {
   326  		td = description.Topology{}
   327  	}
   328  	ch <- td
   329  
   330  	t.subLock.Lock()
   331  	defer t.subLock.Unlock()
   332  	if t.subscriptionsClosed {
   333  		return nil, ErrSubscribeAfterClosed
   334  	}
   335  	id := t.currentSubscriberID
   336  	t.subscribers[id] = ch
   337  	t.currentSubscriberID++
   338  
   339  	return &driver.Subscription{
   340  		Updates: ch,
   341  		ID:      id,
   342  	}, nil
   343  }
   344  
   345  // Unsubscribe unsubscribes the given subscription from the topology and closes the subscription channel.
   346  // Unsubscribe implements the driver.Subscriber interface.
   347  func (t *Topology) Unsubscribe(sub *driver.Subscription) error {
   348  	t.subLock.Lock()
   349  	defer t.subLock.Unlock()
   350  
   351  	if t.subscriptionsClosed {
   352  		return nil
   353  	}
   354  
   355  	ch, ok := t.subscribers[sub.ID]
   356  	if !ok {
   357  		return nil
   358  	}
   359  
   360  	close(ch)
   361  	delete(t.subscribers, sub.ID)
   362  	return nil
   363  }
   364  
   365  // RequestImmediateCheck will send heartbeats to all the servers in the
   366  // topology right away, instead of waiting for the heartbeat timeout.
   367  func (t *Topology) RequestImmediateCheck() {
   368  	if atomic.LoadInt64(&t.state) != topologyConnected {
   369  		return
   370  	}
   371  	t.serversLock.Lock()
   372  	for _, server := range t.servers {
   373  		server.RequestImmediateCheck()
   374  	}
   375  	t.serversLock.Unlock()
   376  }
   377  
   378  // SelectServer selects a server with given a selector. SelectServer complies with the
   379  // server selection spec, and will time out after serverSelectionTimeout or when the
   380  // parent context is done.
   381  func (t *Topology) SelectServer(ctx context.Context, ss description.ServerSelector) (driver.Server, error) {
   382  	if atomic.LoadInt64(&t.state) != topologyConnected {
   383  		return nil, ErrTopologyClosed
   384  	}
   385  	var ssTimeoutCh <-chan time.Time
   386  
   387  	if t.cfg.ServerSelectionTimeout > 0 {
   388  		ssTimeout := time.NewTimer(t.cfg.ServerSelectionTimeout)
   389  		ssTimeoutCh = ssTimeout.C
   390  		defer ssTimeout.Stop()
   391  	}
   392  
   393  	var doneOnce bool
   394  	var sub *driver.Subscription
   395  	selectionState := newServerSelectionState(ss, ssTimeoutCh)
   396  	for {
   397  		var suitable []description.Server
   398  		var selectErr error
   399  
   400  		if !doneOnce {
   401  			// for the first pass, select a server from the current description.
   402  			// this improves selection speed for up-to-date topology descriptions.
   403  			suitable, selectErr = t.selectServerFromDescription(t.Description(), selectionState)
   404  			doneOnce = true
   405  		} else {
   406  			// if the first pass didn't select a server, the previous description did not contain a suitable server, so
   407  			// we subscribe to the topology and attempt to obtain a server from that subscription
   408  			if sub == nil {
   409  				var err error
   410  				sub, err = t.Subscribe()
   411  				if err != nil {
   412  					return nil, err
   413  				}
   414  				defer t.Unsubscribe(sub)
   415  			}
   416  
   417  			suitable, selectErr = t.selectServerFromSubscription(ctx, sub.Updates, selectionState)
   418  		}
   419  		if selectErr != nil {
   420  			return nil, selectErr
   421  		}
   422  
   423  		if len(suitable) == 0 {
   424  			// try again if there are no servers available
   425  			continue
   426  		}
   427  
   428  		// If there's only one suitable server description, try to find the associated server and
   429  		// return it. This is an optimization primarily for standalone and load-balanced deployments.
   430  		if len(suitable) == 1 {
   431  			server, err := t.FindServer(suitable[0])
   432  			if err != nil {
   433  				return nil, err
   434  			}
   435  			if server == nil {
   436  				continue
   437  			}
   438  			return server, nil
   439  		}
   440  
   441  		// Randomly select 2 suitable server descriptions and find servers for them. We select two
   442  		// so we can pick the one with the one with fewer in-progress operations below.
   443  		desc1, desc2 := pick2(suitable)
   444  		server1, err := t.FindServer(desc1)
   445  		if err != nil {
   446  			return nil, err
   447  		}
   448  		server2, err := t.FindServer(desc2)
   449  		if err != nil {
   450  			return nil, err
   451  		}
   452  
   453  		// If we don't have an actual server for one or both of the provided descriptions, either
   454  		// return the one server we have, or try again if they're both nil. This could happen for a
   455  		// number of reasons, including that the server has since stopped being a part of this
   456  		// topology.
   457  		if server1 == nil || server2 == nil {
   458  			if server1 == nil && server2 == nil {
   459  				continue
   460  			}
   461  			if server1 != nil {
   462  				return server1, nil
   463  			}
   464  			return server2, nil
   465  		}
   466  
   467  		// Of the two randomly selected suitable servers, pick the one with fewer in-use connections.
   468  		// We use in-use connections as an analog for in-progress operations because they are almost
   469  		// always the same value for a given server.
   470  		if server1.OperationCount() < server2.OperationCount() {
   471  			return server1, nil
   472  		}
   473  		return server2, nil
   474  	}
   475  }
   476  
   477  // pick2 returns 2 random server descriptions from the input slice of server descriptions,
   478  // guaranteeing that the same element from the slice is not picked twice. The order of server
   479  // descriptions in the input slice may be modified. If fewer than 2 server descriptions are
   480  // provided, pick2 will panic.
   481  func pick2(ds []description.Server) (description.Server, description.Server) {
   482  	// Select a random index from the input slice and keep the server description from that index.
   483  	idx := random.Intn(len(ds))
   484  	s1 := ds[idx]
   485  
   486  	// Swap the selected index to the end and reslice to remove it so we don't pick the same server
   487  	// description twice.
   488  	ds[idx], ds[len(ds)-1] = ds[len(ds)-1], ds[idx]
   489  	ds = ds[:len(ds)-1]
   490  
   491  	// Select another random index from the input slice and return both selected server descriptions.
   492  	return s1, ds[random.Intn(len(ds))]
   493  }
   494  
   495  // FindServer will attempt to find a server that fits the given server description.
   496  // This method will return nil, nil if a matching server could not be found.
   497  func (t *Topology) FindServer(selected description.Server) (*SelectedServer, error) {
   498  	if atomic.LoadInt64(&t.state) != topologyConnected {
   499  		return nil, ErrTopologyClosed
   500  	}
   501  	t.serversLock.Lock()
   502  	defer t.serversLock.Unlock()
   503  	server, ok := t.servers[selected.Addr]
   504  	if !ok {
   505  		return nil, nil
   506  	}
   507  
   508  	desc := t.Description()
   509  	return &SelectedServer{
   510  		Server: server,
   511  		Kind:   desc.Kind,
   512  	}, nil
   513  }
   514  
   515  // selectServerFromSubscription loops until a topology description is available for server selection. It returns
   516  // when the given context expires, server selection timeout is reached, or a description containing a selectable
   517  // server is available.
   518  func (t *Topology) selectServerFromSubscription(ctx context.Context, subscriptionCh <-chan description.Topology,
   519  	selectionState serverSelectionState) ([]description.Server, error) {
   520  
   521  	current := t.Description()
   522  	for {
   523  		select {
   524  		case <-ctx.Done():
   525  			return nil, ServerSelectionError{Wrapped: ctx.Err(), Desc: current}
   526  		case <-selectionState.timeoutChan:
   527  			return nil, ServerSelectionError{Wrapped: ErrServerSelectionTimeout, Desc: current}
   528  		case current = <-subscriptionCh:
   529  		}
   530  
   531  		suitable, err := t.selectServerFromDescription(current, selectionState)
   532  		if err != nil {
   533  			return nil, err
   534  		}
   535  
   536  		if len(suitable) > 0 {
   537  			return suitable, nil
   538  		}
   539  		t.RequestImmediateCheck()
   540  	}
   541  }
   542  
   543  // selectServerFromDescription process the given topology description and returns a slice of suitable servers.
   544  func (t *Topology) selectServerFromDescription(desc description.Topology,
   545  	selectionState serverSelectionState) ([]description.Server, error) {
   546  
   547  	// Unlike selectServerFromSubscription, this code path does not check ctx.Done or selectionState.timeoutChan because
   548  	// selecting a server from a description is not a blocking operation.
   549  
   550  	if desc.CompatibilityErr != nil {
   551  		return nil, desc.CompatibilityErr
   552  	}
   553  
   554  	// If the topology kind is LoadBalanced, the LB is the only server and it is always considered selectable. The
   555  	// selectors exported by the driver should already return the LB as a candidate, so this but this check ensures that
   556  	// the LB is always selectable even if a user of the low-level driver provides a custom selector.
   557  	if desc.Kind == description.LoadBalanced {
   558  		return desc.Servers, nil
   559  	}
   560  
   561  	allowedIndexes := make([]int, 0, len(desc.Servers))
   562  	for i, s := range desc.Servers {
   563  		if s.Kind != description.Unknown {
   564  			allowedIndexes = append(allowedIndexes, i)
   565  		}
   566  	}
   567  
   568  	allowed := make([]description.Server, len(allowedIndexes))
   569  	for i, idx := range allowedIndexes {
   570  		allowed[i] = desc.Servers[idx]
   571  	}
   572  
   573  	suitable, err := selectionState.selector.SelectServer(desc, allowed)
   574  	if err != nil {
   575  		return nil, ServerSelectionError{Wrapped: err, Desc: desc}
   576  	}
   577  	return suitable, nil
   578  }
   579  
   580  func (t *Topology) pollSRVRecords(hosts string) {
   581  	defer t.pollingwg.Done()
   582  
   583  	serverConfig := newServerConfig(t.cfg.ServerOpts...)
   584  	heartbeatInterval := serverConfig.heartbeatInterval
   585  
   586  	pollTicker := time.NewTicker(t.rescanSRVInterval)
   587  	defer pollTicker.Stop()
   588  	t.pollHeartbeatTime.Store(false)
   589  	var doneOnce bool
   590  	defer func() {
   591  		//  ¯\_(ツ)_/¯
   592  		if r := recover(); r != nil && !doneOnce {
   593  			<-t.pollingDone
   594  		}
   595  	}()
   596  
   597  	for {
   598  		select {
   599  		case <-pollTicker.C:
   600  		case <-t.pollingDone:
   601  			doneOnce = true
   602  			return
   603  		}
   604  		topoKind := t.Description().Kind
   605  		if !(topoKind == description.Unknown || topoKind == description.Sharded) {
   606  			break
   607  		}
   608  
   609  		parsedHosts, err := t.dnsResolver.ParseHosts(hosts, t.cfg.SRVServiceName, false)
   610  		// DNS problem or no verified hosts returned
   611  		if err != nil || len(parsedHosts) == 0 {
   612  			if !t.pollHeartbeatTime.Load().(bool) {
   613  				pollTicker.Stop()
   614  				pollTicker = time.NewTicker(heartbeatInterval)
   615  				t.pollHeartbeatTime.Store(true)
   616  			}
   617  			continue
   618  		}
   619  		if t.pollHeartbeatTime.Load().(bool) {
   620  			pollTicker.Stop()
   621  			pollTicker = time.NewTicker(t.rescanSRVInterval)
   622  			t.pollHeartbeatTime.Store(false)
   623  		}
   624  
   625  		cont := t.processSRVResults(parsedHosts)
   626  		if !cont {
   627  			break
   628  		}
   629  	}
   630  	<-t.pollingDone
   631  	doneOnce = true
   632  }
   633  
   634  func (t *Topology) processSRVResults(parsedHosts []string) bool {
   635  	t.serversLock.Lock()
   636  	defer t.serversLock.Unlock()
   637  
   638  	if t.serversClosed {
   639  		return false
   640  	}
   641  	prev := t.fsm.Topology
   642  	diff := diffHostList(t.fsm.Topology, parsedHosts)
   643  
   644  	if len(diff.Added) == 0 && len(diff.Removed) == 0 {
   645  		return true
   646  	}
   647  
   648  	for _, r := range diff.Removed {
   649  		addr := address.Address(r).Canonicalize()
   650  		s, ok := t.servers[addr]
   651  		if !ok {
   652  			continue
   653  		}
   654  		go func() {
   655  			cancelCtx, cancel := context.WithCancel(context.Background())
   656  			cancel()
   657  			_ = s.Disconnect(cancelCtx)
   658  		}()
   659  		delete(t.servers, addr)
   660  		t.fsm.removeServerByAddr(addr)
   661  		t.publishServerClosedEvent(s.address)
   662  	}
   663  
   664  	// Now that we've removed all the hosts that disappeared from the SRV record, we need to add any
   665  	// new hosts added to the SRV record. If adding all of the new hosts would increase the number
   666  	// of servers past srvMaxHosts, shuffle the list of added hosts.
   667  	if t.cfg.SRVMaxHosts > 0 && len(t.servers)+len(diff.Added) > t.cfg.SRVMaxHosts {
   668  		random.Shuffle(len(diff.Added), func(i, j int) {
   669  			diff.Added[i], diff.Added[j] = diff.Added[j], diff.Added[i]
   670  		})
   671  	}
   672  	// Add all added hosts until the number of servers reaches srvMaxHosts.
   673  	for _, a := range diff.Added {
   674  		if t.cfg.SRVMaxHosts > 0 && len(t.servers) >= t.cfg.SRVMaxHosts {
   675  			break
   676  		}
   677  		addr := address.Address(a).Canonicalize()
   678  		_ = t.addServer(addr)
   679  		t.fsm.addServer(addr)
   680  	}
   681  
   682  	//store new description
   683  	newDesc := description.Topology{
   684  		Kind:                  t.fsm.Kind,
   685  		Servers:               t.fsm.Servers,
   686  		SessionTimeoutMinutes: t.fsm.SessionTimeoutMinutes,
   687  	}
   688  	t.desc.Store(newDesc)
   689  
   690  	if !prev.Equal(newDesc) {
   691  		t.publishTopologyDescriptionChangedEvent(prev, newDesc)
   692  	}
   693  
   694  	t.subLock.Lock()
   695  	for _, ch := range t.subscribers {
   696  		// We drain the description if there's one in the channel
   697  		select {
   698  		case <-ch:
   699  		default:
   700  		}
   701  		ch <- newDesc
   702  	}
   703  	t.subLock.Unlock()
   704  
   705  	return true
   706  }
   707  
   708  // apply updates the Topology and its underlying FSM based on the provided server description and returns the server
   709  // description that should be stored.
   710  func (t *Topology) apply(ctx context.Context, desc description.Server) description.Server {
   711  	t.serversLock.Lock()
   712  	defer t.serversLock.Unlock()
   713  
   714  	ind, ok := t.fsm.findServer(desc.Addr)
   715  	if t.serversClosed || !ok {
   716  		return desc
   717  	}
   718  
   719  	prev := t.fsm.Topology
   720  	oldDesc := t.fsm.Servers[ind]
   721  	if oldDesc.TopologyVersion.CompareToIncoming(desc.TopologyVersion) > 0 {
   722  		return oldDesc
   723  	}
   724  
   725  	var current description.Topology
   726  	current, desc = t.fsm.apply(desc)
   727  
   728  	if !oldDesc.Equal(desc) {
   729  		t.publishServerDescriptionChangedEvent(oldDesc, desc)
   730  	}
   731  
   732  	diff := diffTopology(prev, current)
   733  
   734  	for _, removed := range diff.Removed {
   735  		if s, ok := t.servers[removed.Addr]; ok {
   736  			go func() {
   737  				cancelCtx, cancel := context.WithCancel(ctx)
   738  				cancel()
   739  				_ = s.Disconnect(cancelCtx)
   740  			}()
   741  			delete(t.servers, removed.Addr)
   742  			t.publishServerClosedEvent(s.address)
   743  		}
   744  	}
   745  
   746  	for _, added := range diff.Added {
   747  		_ = t.addServer(added.Addr)
   748  	}
   749  
   750  	t.desc.Store(current)
   751  	if !prev.Equal(current) {
   752  		t.publishTopologyDescriptionChangedEvent(prev, current)
   753  	}
   754  
   755  	t.subLock.Lock()
   756  	for _, ch := range t.subscribers {
   757  		// We drain the description if there's one in the channel
   758  		select {
   759  		case <-ch:
   760  		default:
   761  		}
   762  		ch <- current
   763  	}
   764  	t.subLock.Unlock()
   765  
   766  	return desc
   767  }
   768  
   769  func (t *Topology) addServer(addr address.Address) error {
   770  	if _, ok := t.servers[addr]; ok {
   771  		return nil
   772  	}
   773  
   774  	svr, err := ConnectServer(addr, t.updateCallback, t.id, t.cfg.ServerOpts...)
   775  	if err != nil {
   776  		return err
   777  	}
   778  
   779  	t.servers[addr] = svr
   780  
   781  	return nil
   782  }
   783  
   784  // String implements the Stringer interface
   785  func (t *Topology) String() string {
   786  	desc := t.Description()
   787  
   788  	serversStr := ""
   789  	t.serversLock.Lock()
   790  	defer t.serversLock.Unlock()
   791  	for _, s := range t.servers {
   792  		serversStr += "{ " + s.String() + " }, "
   793  	}
   794  	return fmt.Sprintf("Type: %s, Servers: [%s]", desc.Kind, serversStr)
   795  }
   796  
   797  // publishes a ServerDescriptionChangedEvent to indicate the server description has changed
   798  func (t *Topology) publishServerDescriptionChangedEvent(prev description.Server, current description.Server) {
   799  	serverDescriptionChanged := &event.ServerDescriptionChangedEvent{
   800  		Address:             current.Addr,
   801  		TopologyID:          t.id,
   802  		PreviousDescription: prev,
   803  		NewDescription:      current,
   804  	}
   805  
   806  	if t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.ServerDescriptionChanged != nil {
   807  		t.cfg.ServerMonitor.ServerDescriptionChanged(serverDescriptionChanged)
   808  	}
   809  }
   810  
   811  // publishes a ServerClosedEvent to indicate the server has closed
   812  func (t *Topology) publishServerClosedEvent(addr address.Address) {
   813  	serverClosed := &event.ServerClosedEvent{
   814  		Address:    addr,
   815  		TopologyID: t.id,
   816  	}
   817  
   818  	if t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.ServerClosed != nil {
   819  		t.cfg.ServerMonitor.ServerClosed(serverClosed)
   820  	}
   821  }
   822  
   823  // publishes a TopologyDescriptionChangedEvent to indicate the topology description has changed
   824  func (t *Topology) publishTopologyDescriptionChangedEvent(prev description.Topology, current description.Topology) {
   825  	topologyDescriptionChanged := &event.TopologyDescriptionChangedEvent{
   826  		TopologyID:          t.id,
   827  		PreviousDescription: prev,
   828  		NewDescription:      current,
   829  	}
   830  
   831  	if t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.TopologyDescriptionChanged != nil {
   832  		t.cfg.ServerMonitor.TopologyDescriptionChanged(topologyDescriptionChanged)
   833  	}
   834  }
   835  
   836  // publishes a TopologyOpeningEvent to indicate the topology is being initialized
   837  func (t *Topology) publishTopologyOpeningEvent() {
   838  	topologyOpening := &event.TopologyOpeningEvent{
   839  		TopologyID: t.id,
   840  	}
   841  
   842  	if t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.TopologyOpening != nil {
   843  		t.cfg.ServerMonitor.TopologyOpening(topologyOpening)
   844  	}
   845  }
   846  
   847  // publishes a TopologyClosedEvent to indicate the topology has been closed
   848  func (t *Topology) publishTopologyClosedEvent() {
   849  	topologyClosed := &event.TopologyClosedEvent{
   850  		TopologyID: t.id,
   851  	}
   852  
   853  	if t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.TopologyClosed != nil {
   854  		t.cfg.ServerMonitor.TopologyClosed(topologyClosed)
   855  	}
   856  }