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 }