github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/x/mongo/driver/topology/pool.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 "context" 11 "fmt" 12 "net" 13 "sync" 14 "sync/atomic" 15 "time" 16 17 "go.mongodb.org/mongo-driver/bson/primitive" 18 "go.mongodb.org/mongo-driver/event" 19 "go.mongodb.org/mongo-driver/internal/logger" 20 "go.mongodb.org/mongo-driver/mongo/address" 21 "go.mongodb.org/mongo-driver/x/mongo/driver" 22 ) 23 24 // Connection pool state constants. 25 const ( 26 poolPaused int = iota 27 poolReady 28 poolClosed 29 ) 30 31 // ErrPoolNotPaused is returned when attempting to mark a connection pool "ready" that is not 32 // currently "paused". 33 var ErrPoolNotPaused = PoolError("only a paused pool can be marked ready") 34 35 // ErrPoolClosed is returned when attempting to check out a connection from a closed pool. 36 var ErrPoolClosed = PoolError("attempted to check out a connection from closed connection pool") 37 38 // ErrConnectionClosed is returned from an attempt to use an already closed connection. 39 var ErrConnectionClosed = ConnectionError{ConnectionID: "<closed>", message: "connection is closed"} 40 41 // ErrWrongPool is return when a connection is returned to a pool it doesn't belong to. 42 var ErrWrongPool = PoolError("connection does not belong to this pool") 43 44 // PoolError is an error returned from a Pool method. 45 type PoolError string 46 47 func (pe PoolError) Error() string { return string(pe) } 48 49 // poolClearedError is an error returned when the connection pool is cleared or currently paused. It 50 // is a retryable error. 51 type poolClearedError struct { 52 err error 53 address address.Address 54 } 55 56 func (pce poolClearedError) Error() string { 57 return fmt.Sprintf( 58 "connection pool for %v was cleared because another operation failed with: %v", 59 pce.address, 60 pce.err) 61 } 62 63 // Retryable returns true. All poolClearedErrors are retryable. 64 func (poolClearedError) Retryable() bool { return true } 65 66 // Assert that poolClearedError is a driver.RetryablePoolError. 67 var _ driver.RetryablePoolError = poolClearedError{} 68 69 // poolConfig contains all aspects of the pool that can be configured 70 type poolConfig struct { 71 Address address.Address 72 MinPoolSize uint64 73 MaxPoolSize uint64 74 MaxConnecting uint64 75 MaxIdleTime time.Duration 76 MaintainInterval time.Duration 77 PoolMonitor *event.PoolMonitor 78 Logger *logger.Logger 79 handshakeErrFn func(error, uint64, *primitive.ObjectID) 80 } 81 82 type pool struct { 83 // The following integer fields must be accessed using the atomic package 84 // and should be at the beginning of the struct. 85 // - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG 86 // - suggested layout: https://go101.org/article/memory-layout.html 87 88 nextID uint64 // nextID is the next pool ID for a new connection. 89 pinnedCursorConnections uint64 90 pinnedTransactionConnections uint64 91 92 address address.Address 93 minSize uint64 94 maxSize uint64 95 maxConnecting uint64 96 monitor *event.PoolMonitor 97 logger *logger.Logger 98 99 // handshakeErrFn is used to handle any errors that happen during connection establishment and 100 // handshaking. 101 handshakeErrFn func(error, uint64, *primitive.ObjectID) 102 103 connOpts []ConnectionOption 104 generation *poolGenerationMap 105 106 maintainInterval time.Duration // maintainInterval is the maintain() loop interval. 107 maintainReady chan struct{} // maintainReady is a signal channel that starts the maintain() loop when ready() is called. 108 backgroundDone *sync.WaitGroup // backgroundDone waits for all background goroutines to return. 109 110 stateMu sync.RWMutex // stateMu guards state, lastClearErr 111 state int // state is the current state of the connection pool. 112 lastClearErr error // lastClearErr is the last error that caused the pool to be cleared. 113 114 // createConnectionsCond is the condition variable that controls when the createConnections() 115 // loop runs or waits. Its lock guards cancelBackgroundCtx, conns, and newConnWait. Any changes 116 // to the state of the guarded values must be made while holding the lock to prevent undefined 117 // behavior in the createConnections() waiting logic. 118 createConnectionsCond *sync.Cond 119 cancelBackgroundCtx context.CancelFunc // cancelBackgroundCtx is called to signal background goroutines to stop. 120 conns map[uint64]*connection // conns holds all currently open connections. 121 newConnWait wantConnQueue // newConnWait holds all wantConn requests for new connections. 122 123 idleMu sync.Mutex // idleMu guards idleConns, idleConnWait 124 idleConns []*connection // idleConns holds all idle connections. 125 idleConnWait wantConnQueue // idleConnWait holds all wantConn requests for idle connections. 126 } 127 128 // getState returns the current state of the pool. Callers must not hold the stateMu lock. 129 func (p *pool) getState() int { 130 p.stateMu.RLock() 131 defer p.stateMu.RUnlock() 132 133 return p.state 134 } 135 136 func mustLogPoolMessage(pool *pool) bool { 137 return pool.logger != nil && pool.logger.LevelComponentEnabled( 138 logger.LevelDebug, logger.ComponentConnection) 139 } 140 141 func logPoolMessage(pool *pool, msg string, keysAndValues ...interface{}) { 142 host, port, err := net.SplitHostPort(pool.address.String()) 143 if err != nil { 144 host = pool.address.String() 145 port = "" 146 } 147 148 pool.logger.Print(logger.LevelDebug, 149 logger.ComponentConnection, 150 msg, 151 logger.SerializeConnection(logger.Connection{ 152 Message: msg, 153 ServerHost: host, 154 ServerPort: port, 155 }, keysAndValues...)...) 156 157 } 158 159 type reason struct { 160 loggerConn string 161 event string 162 } 163 164 // connectionPerished checks if a given connection is perished and should be removed from the pool. 165 func connectionPerished(conn *connection) (reason, bool) { 166 switch { 167 case conn.closed(): 168 // A connection would only be closed if it encountered a network error during an operation and closed itself. 169 return reason{ 170 loggerConn: logger.ReasonConnClosedError, 171 event: event.ReasonError, 172 }, true 173 case conn.idleTimeoutExpired(): 174 return reason{ 175 loggerConn: logger.ReasonConnClosedIdle, 176 event: event.ReasonIdle, 177 }, true 178 case conn.pool.stale(conn): 179 return reason{ 180 loggerConn: logger.ReasonConnClosedStale, 181 event: event.ReasonStale, 182 }, true 183 } 184 185 return reason{}, false 186 } 187 188 // newPool creates a new pool. It will use the provided options when creating connections. 189 func newPool(config poolConfig, connOpts ...ConnectionOption) *pool { 190 if config.MaxIdleTime != time.Duration(0) { 191 connOpts = append(connOpts, WithIdleTimeout(func(_ time.Duration) time.Duration { return config.MaxIdleTime })) 192 } 193 194 var maxConnecting uint64 = 2 195 if config.MaxConnecting > 0 { 196 maxConnecting = config.MaxConnecting 197 } 198 199 maintainInterval := 10 * time.Second 200 if config.MaintainInterval != 0 { 201 maintainInterval = config.MaintainInterval 202 } 203 204 pool := &pool{ 205 address: config.Address, 206 minSize: config.MinPoolSize, 207 maxSize: config.MaxPoolSize, 208 maxConnecting: maxConnecting, 209 monitor: config.PoolMonitor, 210 logger: config.Logger, 211 handshakeErrFn: config.handshakeErrFn, 212 connOpts: connOpts, 213 generation: newPoolGenerationMap(), 214 state: poolPaused, 215 maintainInterval: maintainInterval, 216 maintainReady: make(chan struct{}, 1), 217 backgroundDone: &sync.WaitGroup{}, 218 createConnectionsCond: sync.NewCond(&sync.Mutex{}), 219 conns: make(map[uint64]*connection, config.MaxPoolSize), 220 idleConns: make([]*connection, 0, config.MaxPoolSize), 221 } 222 // minSize must not exceed maxSize if maxSize is not 0 223 if pool.maxSize != 0 && pool.minSize > pool.maxSize { 224 pool.minSize = pool.maxSize 225 } 226 pool.connOpts = append(pool.connOpts, withGenerationNumberFn(func(_ generationNumberFn) generationNumberFn { return pool.getGenerationForNewConnection })) 227 228 pool.generation.connect() 229 230 // Create a Context with cancellation that's used to signal the createConnections() and 231 // maintain() background goroutines to stop. Also create a "backgroundDone" WaitGroup that is 232 // used to wait for the background goroutines to return. 233 var ctx context.Context 234 ctx, pool.cancelBackgroundCtx = context.WithCancel(context.Background()) 235 236 for i := 0; i < int(pool.maxConnecting); i++ { 237 pool.backgroundDone.Add(1) 238 go pool.createConnections(ctx, pool.backgroundDone) 239 } 240 241 // If maintainInterval is not positive, don't start the maintain() goroutine. Expect that 242 // negative values are only used in testing; this config value is not user-configurable. 243 if maintainInterval > 0 { 244 pool.backgroundDone.Add(1) 245 go pool.maintain(ctx, pool.backgroundDone) 246 } 247 248 if mustLogPoolMessage(pool) { 249 keysAndValues := logger.KeyValues{ 250 logger.KeyMaxIdleTimeMS, config.MaxIdleTime.Milliseconds(), 251 logger.KeyMinPoolSize, config.MinPoolSize, 252 logger.KeyMaxPoolSize, config.MaxPoolSize, 253 logger.KeyMaxConnecting, config.MaxConnecting, 254 } 255 256 logPoolMessage(pool, logger.ConnectionPoolCreated, keysAndValues...) 257 } 258 259 if pool.monitor != nil { 260 pool.monitor.Event(&event.PoolEvent{ 261 Type: event.PoolCreated, 262 PoolOptions: &event.MonitorPoolOptions{ 263 MaxPoolSize: config.MaxPoolSize, 264 MinPoolSize: config.MinPoolSize, 265 }, 266 Address: pool.address.String(), 267 }) 268 } 269 270 return pool 271 } 272 273 // stale checks if a given connection's generation is below the generation of the pool 274 func (p *pool) stale(conn *connection) bool { 275 return conn == nil || p.generation.stale(conn.desc.ServiceID, conn.generation) 276 } 277 278 // ready puts the pool into the "ready" state and starts the background connection creation and 279 // monitoring goroutines. ready must be called before connections can be checked out. An unused, 280 // connected pool must be closed or it will leak goroutines and will not be garbage collected. 281 func (p *pool) ready() error { 282 // While holding the stateMu lock, set the pool to "ready" if it is currently "paused". 283 p.stateMu.Lock() 284 if p.state == poolReady { 285 p.stateMu.Unlock() 286 return nil 287 } 288 if p.state != poolPaused { 289 p.stateMu.Unlock() 290 return ErrPoolNotPaused 291 } 292 p.lastClearErr = nil 293 p.state = poolReady 294 p.stateMu.Unlock() 295 296 if mustLogPoolMessage(p) { 297 logPoolMessage(p, logger.ConnectionPoolReady) 298 } 299 300 // Send event.PoolReady before resuming the maintain() goroutine to guarantee that the 301 // "pool ready" event is always sent before maintain() starts creating connections. 302 if p.monitor != nil { 303 p.monitor.Event(&event.PoolEvent{ 304 Type: event.PoolReady, 305 Address: p.address.String(), 306 }) 307 } 308 309 // Signal maintain() to wake up immediately when marking the pool "ready". 310 select { 311 case p.maintainReady <- struct{}{}: 312 default: 313 } 314 315 return nil 316 } 317 318 // close closes the pool, closes all connections associated with the pool, and stops all background 319 // goroutines. All subsequent checkOut requests will return an error. An unused, ready pool must be 320 // closed or it will leak goroutines and will not be garbage collected. 321 func (p *pool) close(ctx context.Context) { 322 p.stateMu.Lock() 323 if p.state == poolClosed { 324 p.stateMu.Unlock() 325 return 326 } 327 p.state = poolClosed 328 p.stateMu.Unlock() 329 330 // Call cancelBackgroundCtx() to exit the maintain() and createConnections() background 331 // goroutines. Broadcast to the createConnectionsCond to wake up all createConnections() 332 // goroutines. We must hold the createConnectionsCond lock here because we're changing the 333 // condition by cancelling the "background goroutine" Context, even tho cancelling the Context 334 // is also synchronized by a lock. Otherwise, we run into an intermittent bug that prevents the 335 // createConnections() goroutines from exiting. 336 p.createConnectionsCond.L.Lock() 337 p.cancelBackgroundCtx() 338 p.createConnectionsCond.Broadcast() 339 p.createConnectionsCond.L.Unlock() 340 341 // Wait for all background goroutines to exit. 342 p.backgroundDone.Wait() 343 344 p.generation.disconnect() 345 346 if ctx == nil { 347 ctx = context.Background() 348 } 349 350 // If we have a deadline then we interpret it as a request to gracefully shutdown. We wait until 351 // either all the connections have been checked back into the pool (i.e. total open connections 352 // equals idle connections) or until the Context deadline is reached. 353 if _, ok := ctx.Deadline(); ok { 354 ticker := time.NewTicker(100 * time.Millisecond) 355 defer ticker.Stop() 356 357 graceful: 358 for { 359 if p.totalConnectionCount() == p.availableConnectionCount() { 360 break graceful 361 } 362 363 select { 364 case <-ticker.C: 365 case <-ctx.Done(): 366 break graceful 367 default: 368 } 369 } 370 } 371 372 // Empty the idle connections stack and try to deliver ErrPoolClosed to any waiting wantConns 373 // from idleConnWait while holding the idleMu lock. 374 p.idleMu.Lock() 375 p.idleConns = p.idleConns[:0] 376 for { 377 w := p.idleConnWait.popFront() 378 if w == nil { 379 break 380 } 381 w.tryDeliver(nil, ErrPoolClosed) 382 } 383 p.idleMu.Unlock() 384 385 // Collect all conns from the pool and try to deliver ErrPoolClosed to any waiting wantConns 386 // from newConnWait while holding the createConnectionsCond lock. We can't call removeConnection 387 // on the connections while holding any locks, so do that after we release the lock. 388 p.createConnectionsCond.L.Lock() 389 conns := make([]*connection, 0, len(p.conns)) 390 for _, conn := range p.conns { 391 conns = append(conns, conn) 392 } 393 for { 394 w := p.newConnWait.popFront() 395 if w == nil { 396 break 397 } 398 w.tryDeliver(nil, ErrPoolClosed) 399 } 400 p.createConnectionsCond.L.Unlock() 401 402 // Now that we're not holding any locks, remove all of the connections we collected from the 403 // pool. 404 for _, conn := range conns { 405 _ = p.removeConnection(conn, reason{ 406 loggerConn: logger.ReasonConnClosedPoolClosed, 407 event: event.ReasonPoolClosed, 408 }, nil) 409 _ = p.closeConnection(conn) // We don't care about errors while closing the connection. 410 } 411 412 if mustLogPoolMessage(p) { 413 logPoolMessage(p, logger.ConnectionPoolClosed) 414 } 415 416 if p.monitor != nil { 417 p.monitor.Event(&event.PoolEvent{ 418 Type: event.PoolClosedEvent, 419 Address: p.address.String(), 420 }) 421 } 422 } 423 424 func (p *pool) pinConnectionToCursor() { 425 atomic.AddUint64(&p.pinnedCursorConnections, 1) 426 } 427 428 func (p *pool) unpinConnectionFromCursor() { 429 // See https://golang.org/pkg/sync/atomic/#AddUint64 for an explanation of the ^uint64(0) syntax. 430 atomic.AddUint64(&p.pinnedCursorConnections, ^uint64(0)) 431 } 432 433 func (p *pool) pinConnectionToTransaction() { 434 atomic.AddUint64(&p.pinnedTransactionConnections, 1) 435 } 436 437 func (p *pool) unpinConnectionFromTransaction() { 438 // See https://golang.org/pkg/sync/atomic/#AddUint64 for an explanation of the ^uint64(0) syntax. 439 atomic.AddUint64(&p.pinnedTransactionConnections, ^uint64(0)) 440 } 441 442 // checkOut checks out a connection from the pool. If an idle connection is not available, the 443 // checkOut enters a queue waiting for either the next idle or new connection. If the pool is not 444 // ready, checkOut returns an error. 445 // Based partially on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1324 446 func (p *pool) checkOut(ctx context.Context) (conn *connection, err error) { 447 if mustLogPoolMessage(p) { 448 logPoolMessage(p, logger.ConnectionCheckoutStarted) 449 } 450 451 // TODO(CSOT): If a Timeout was specified at any level, respect the Timeout is server selection, connection 452 // TODO checkout. 453 if p.monitor != nil { 454 p.monitor.Event(&event.PoolEvent{ 455 Type: event.GetStarted, 456 Address: p.address.String(), 457 }) 458 } 459 460 // Check the pool state while holding a stateMu read lock. If the pool state is not "ready", 461 // return an error. Do all of this while holding the stateMu read lock to prevent a state change between 462 // checking the state and entering the wait queue. Not holding the stateMu read lock here may 463 // allow a checkOut() to enter the wait queue after clear() pauses the pool and clears the wait 464 // queue, resulting in createConnections() doing work while the pool is "paused". 465 p.stateMu.RLock() 466 switch p.state { 467 case poolClosed: 468 p.stateMu.RUnlock() 469 470 if mustLogPoolMessage(p) { 471 keysAndValues := logger.KeyValues{ 472 logger.KeyReason, logger.ReasonConnCheckoutFailedPoolClosed, 473 } 474 475 logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...) 476 } 477 478 if p.monitor != nil { 479 p.monitor.Event(&event.PoolEvent{ 480 Type: event.GetFailed, 481 Address: p.address.String(), 482 Reason: event.ReasonPoolClosed, 483 }) 484 } 485 return nil, ErrPoolClosed 486 case poolPaused: 487 err := poolClearedError{err: p.lastClearErr, address: p.address} 488 p.stateMu.RUnlock() 489 490 if mustLogPoolMessage(p) { 491 keysAndValues := logger.KeyValues{ 492 logger.KeyReason, logger.ReasonConnCheckoutFailedError, 493 } 494 495 logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...) 496 } 497 498 if p.monitor != nil { 499 p.monitor.Event(&event.PoolEvent{ 500 Type: event.GetFailed, 501 Address: p.address.String(), 502 Reason: event.ReasonConnectionErrored, 503 }) 504 } 505 return nil, err 506 } 507 508 if ctx == nil { 509 ctx = context.Background() 510 } 511 512 // Create a wantConn, which we will use to request an existing idle or new connection. Always 513 // cancel the wantConn if checkOut() returned an error to make sure any delivered connections 514 // are returned to the pool (e.g. if a connection was delivered immediately after the Context 515 // timed out). 516 w := newWantConn() 517 defer func() { 518 if err != nil { 519 w.cancel(p, err) 520 } 521 }() 522 523 // Get in the queue for an idle connection. If getOrQueueForIdleConn returns true, it was able to 524 // immediately deliver an idle connection to the wantConn, so we can return the connection or 525 // error from the wantConn without waiting for "ready". 526 if delivered := p.getOrQueueForIdleConn(w); delivered { 527 // If delivered = true, we didn't enter the wait queue and will return either a connection 528 // or an error, so unlock the stateMu lock here. 529 p.stateMu.RUnlock() 530 531 if w.err != nil { 532 if mustLogPoolMessage(p) { 533 keysAndValues := logger.KeyValues{ 534 logger.KeyReason, logger.ReasonConnCheckoutFailedError, 535 } 536 537 logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...) 538 } 539 540 if p.monitor != nil { 541 p.monitor.Event(&event.PoolEvent{ 542 Type: event.GetFailed, 543 Address: p.address.String(), 544 Reason: event.ReasonConnectionErrored, 545 }) 546 } 547 return nil, w.err 548 } 549 550 if mustLogPoolMessage(p) { 551 keysAndValues := logger.KeyValues{ 552 logger.KeyDriverConnectionID, w.conn.driverConnectionID, 553 } 554 555 logPoolMessage(p, logger.ConnectionCheckedOut, keysAndValues...) 556 } 557 558 if p.monitor != nil { 559 p.monitor.Event(&event.PoolEvent{ 560 Type: event.GetSucceeded, 561 Address: p.address.String(), 562 ConnectionID: w.conn.driverConnectionID, 563 }) 564 } 565 566 return w.conn, nil 567 } 568 569 // If we didn't get an immediately available idle connection, also get in the queue for a new 570 // connection while we're waiting for an idle connection. 571 p.queueForNewConn(w) 572 p.stateMu.RUnlock() 573 574 // Wait for either the wantConn to be ready or for the Context to time out. 575 select { 576 case <-w.ready: 577 if w.err != nil { 578 if mustLogPoolMessage(p) { 579 keysAndValues := logger.KeyValues{ 580 logger.KeyReason, logger.ReasonConnCheckoutFailedError, 581 logger.KeyError, w.err.Error(), 582 } 583 584 logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...) 585 } 586 587 if p.monitor != nil { 588 p.monitor.Event(&event.PoolEvent{ 589 Type: event.GetFailed, 590 Address: p.address.String(), 591 Reason: event.ReasonConnectionErrored, 592 }) 593 } 594 595 return nil, w.err 596 } 597 598 if mustLogPoolMessage(p) { 599 keysAndValues := logger.KeyValues{ 600 logger.KeyDriverConnectionID, w.conn.driverConnectionID, 601 } 602 603 logPoolMessage(p, logger.ConnectionCheckedOut, keysAndValues...) 604 } 605 606 if p.monitor != nil { 607 p.monitor.Event(&event.PoolEvent{ 608 Type: event.GetSucceeded, 609 Address: p.address.String(), 610 ConnectionID: w.conn.driverConnectionID, 611 }) 612 } 613 return w.conn, nil 614 case <-ctx.Done(): 615 if mustLogPoolMessage(p) { 616 keysAndValues := logger.KeyValues{ 617 logger.KeyReason, logger.ReasonConnCheckoutFailedTimout, 618 } 619 620 logPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...) 621 } 622 623 if p.monitor != nil { 624 p.monitor.Event(&event.PoolEvent{ 625 Type: event.GetFailed, 626 Address: p.address.String(), 627 Reason: event.ReasonTimedOut, 628 }) 629 } 630 631 return nil, WaitQueueTimeoutError{ 632 Wrapped: ctx.Err(), 633 PinnedCursorConnections: atomic.LoadUint64(&p.pinnedCursorConnections), 634 PinnedTransactionConnections: atomic.LoadUint64(&p.pinnedTransactionConnections), 635 maxPoolSize: p.maxSize, 636 totalConnectionCount: p.totalConnectionCount(), 637 } 638 } 639 } 640 641 // closeConnection closes a connection. 642 func (p *pool) closeConnection(conn *connection) error { 643 if conn.pool != p { 644 return ErrWrongPool 645 } 646 647 if atomic.LoadInt64(&conn.state) == connConnected { 648 conn.closeConnectContext() 649 conn.wait() // Make sure that the connection has finished connecting. 650 } 651 652 err := conn.close() 653 if err != nil { 654 return ConnectionError{ConnectionID: conn.id, Wrapped: err, message: "failed to close net.Conn"} 655 } 656 657 return nil 658 } 659 660 func (p *pool) getGenerationForNewConnection(serviceID *primitive.ObjectID) uint64 { 661 return p.generation.addConnection(serviceID) 662 } 663 664 // removeConnection removes a connection from the pool and emits a "ConnectionClosed" event. 665 func (p *pool) removeConnection(conn *connection, reason reason, err error) error { 666 if conn == nil { 667 return nil 668 } 669 670 if conn.pool != p { 671 return ErrWrongPool 672 } 673 674 p.createConnectionsCond.L.Lock() 675 _, ok := p.conns[conn.driverConnectionID] 676 if !ok { 677 // If the connection has been removed from the pool already, exit without doing any 678 // additional state changes. 679 p.createConnectionsCond.L.Unlock() 680 return nil 681 } 682 delete(p.conns, conn.driverConnectionID) 683 // Signal the createConnectionsCond so any goroutines waiting for a new connection slot in the 684 // pool will proceed. 685 p.createConnectionsCond.Signal() 686 p.createConnectionsCond.L.Unlock() 687 688 // Only update the generation numbers map if the connection has retrieved its generation number. 689 // Otherwise, we'd decrement the count for the generation even though it had never been 690 // incremented. 691 if conn.hasGenerationNumber() { 692 p.generation.removeConnection(conn.desc.ServiceID) 693 } 694 695 if mustLogPoolMessage(p) { 696 keysAndValues := logger.KeyValues{ 697 logger.KeyDriverConnectionID, conn.driverConnectionID, 698 logger.KeyReason, reason.loggerConn, 699 } 700 701 if err != nil { 702 keysAndValues.Add(logger.KeyError, err.Error()) 703 } 704 705 logPoolMessage(p, logger.ConnectionClosed, keysAndValues...) 706 } 707 708 if p.monitor != nil { 709 p.monitor.Event(&event.PoolEvent{ 710 Type: event.ConnectionClosed, 711 Address: p.address.String(), 712 ConnectionID: conn.driverConnectionID, 713 Reason: reason.event, 714 Error: err, 715 }) 716 } 717 718 return nil 719 } 720 721 // checkIn returns an idle connection to the pool. If the connection is perished or the pool is 722 // closed, it is removed from the connection pool and closed. 723 func (p *pool) checkIn(conn *connection) error { 724 if conn == nil { 725 return nil 726 } 727 if conn.pool != p { 728 return ErrWrongPool 729 } 730 731 if mustLogPoolMessage(p) { 732 keysAndValues := logger.KeyValues{ 733 logger.KeyDriverConnectionID, conn.driverConnectionID, 734 } 735 736 logPoolMessage(p, logger.ConnectionCheckedIn, keysAndValues...) 737 } 738 739 if p.monitor != nil { 740 p.monitor.Event(&event.PoolEvent{ 741 Type: event.ConnectionReturned, 742 ConnectionID: conn.driverConnectionID, 743 Address: conn.addr.String(), 744 }) 745 } 746 747 return p.checkInNoEvent(conn) 748 } 749 750 // checkInNoEvent returns a connection to the pool. It behaves identically to checkIn except it does 751 // not publish events. It is only intended for use by pool-internal functions. 752 func (p *pool) checkInNoEvent(conn *connection) error { 753 if conn == nil { 754 return nil 755 } 756 if conn.pool != p { 757 return ErrWrongPool 758 } 759 760 // Bump the connection idle deadline here because we're about to make the connection "available". 761 // The idle deadline is used to determine when a connection has reached its max idle time and 762 // should be closed. A connection reaches its max idle time when it has been "available" in the 763 // idle connections stack for more than the configured duration (maxIdleTimeMS). Set it before 764 // we call connectionPerished(), which checks the idle deadline, because a newly "available" 765 // connection should never be perished due to max idle time. 766 conn.bumpIdleDeadline() 767 768 if reason, perished := connectionPerished(conn); perished { 769 _ = p.removeConnection(conn, reason, nil) 770 go func() { 771 _ = p.closeConnection(conn) 772 }() 773 return nil 774 } 775 776 if conn.pool.getState() == poolClosed { 777 _ = p.removeConnection(conn, reason{ 778 loggerConn: logger.ReasonConnClosedPoolClosed, 779 event: event.ReasonPoolClosed, 780 }, nil) 781 782 go func() { 783 _ = p.closeConnection(conn) 784 }() 785 return nil 786 } 787 788 p.idleMu.Lock() 789 defer p.idleMu.Unlock() 790 791 for { 792 w := p.idleConnWait.popFront() 793 if w == nil { 794 break 795 } 796 if w.tryDeliver(conn, nil) { 797 return nil 798 } 799 } 800 801 for _, idle := range p.idleConns { 802 if idle == conn { 803 return fmt.Errorf("duplicate idle conn %p in idle connections stack", conn) 804 } 805 } 806 807 p.idleConns = append(p.idleConns, conn) 808 return nil 809 } 810 811 // clear marks all connections as stale by incrementing the generation number, stops all background 812 // goroutines, removes all requests from idleConnWait and newConnWait, and sets the pool state to 813 // "paused". If serviceID is nil, clear marks all connections as stale. If serviceID is not nil, 814 // clear marks only connections associated with the given serviceID stale (for use in load balancer 815 // mode). 816 func (p *pool) clear(err error, serviceID *primitive.ObjectID) { 817 if p.getState() == poolClosed { 818 return 819 } 820 821 p.generation.clear(serviceID) 822 823 // If serviceID is nil (i.e. not in load balancer mode), transition the pool to a paused state 824 // by stopping all background goroutines, clearing the wait queues, and setting the pool state 825 // to "paused". 826 sendEvent := true 827 if serviceID == nil { 828 // While holding the stateMu lock, set the pool state to "paused" if it's currently "ready", 829 // and set lastClearErr to the error that caused the pool to be cleared. If the pool is 830 // already paused, don't send another "ConnectionPoolCleared" event. 831 p.stateMu.Lock() 832 if p.state == poolPaused { 833 sendEvent = false 834 } 835 if p.state == poolReady { 836 p.state = poolPaused 837 } 838 p.lastClearErr = err 839 p.stateMu.Unlock() 840 841 pcErr := poolClearedError{err: err, address: p.address} 842 843 // Clear the idle connections wait queue. 844 p.idleMu.Lock() 845 for { 846 w := p.idleConnWait.popFront() 847 if w == nil { 848 break 849 } 850 w.tryDeliver(nil, pcErr) 851 } 852 p.idleMu.Unlock() 853 854 // Clear the new connections wait queue. This effectively pauses the createConnections() 855 // background goroutine because newConnWait is empty and checkOut() won't insert any more 856 // wantConns into newConnWait until the pool is marked "ready" again. 857 p.createConnectionsCond.L.Lock() 858 for { 859 w := p.newConnWait.popFront() 860 if w == nil { 861 break 862 } 863 w.tryDeliver(nil, pcErr) 864 } 865 p.createConnectionsCond.L.Unlock() 866 } 867 868 if mustLogPoolMessage(p) { 869 keysAndValues := logger.KeyValues{ 870 logger.KeyServiceID, serviceID, 871 } 872 873 logPoolMessage(p, logger.ConnectionPoolCleared, keysAndValues...) 874 } 875 876 if sendEvent && p.monitor != nil { 877 p.monitor.Event(&event.PoolEvent{ 878 Type: event.PoolCleared, 879 Address: p.address.String(), 880 ServiceID: serviceID, 881 }) 882 } 883 } 884 885 // getOrQueueForIdleConn attempts to deliver an idle connection to the given wantConn. If there is 886 // an idle connection in the idle connections stack, it pops an idle connection, delivers it to the 887 // wantConn, and returns true. If there are no idle connections in the idle connections stack, it 888 // adds the wantConn to the idleConnWait queue and returns false. 889 func (p *pool) getOrQueueForIdleConn(w *wantConn) bool { 890 p.idleMu.Lock() 891 defer p.idleMu.Unlock() 892 893 // Try to deliver an idle connection from the idleConns stack first. 894 for len(p.idleConns) > 0 { 895 conn := p.idleConns[len(p.idleConns)-1] 896 p.idleConns = p.idleConns[:len(p.idleConns)-1] 897 898 if conn == nil { 899 continue 900 } 901 902 if reason, perished := connectionPerished(conn); perished { 903 _ = conn.pool.removeConnection(conn, reason, nil) 904 go func() { 905 _ = conn.pool.closeConnection(conn) 906 }() 907 continue 908 } 909 910 if !w.tryDeliver(conn, nil) { 911 // If we couldn't deliver the conn to w, put it back in the idleConns stack. 912 p.idleConns = append(p.idleConns, conn) 913 } 914 915 // If we got here, we tried to deliver an idle conn to w. No matter if tryDeliver() returned 916 // true or false, w is no longer waiting and doesn't need to be added to any wait queues, so 917 // return delivered = true. 918 return true 919 } 920 921 p.idleConnWait.cleanFront() 922 p.idleConnWait.pushBack(w) 923 return false 924 } 925 926 func (p *pool) queueForNewConn(w *wantConn) { 927 p.createConnectionsCond.L.Lock() 928 defer p.createConnectionsCond.L.Unlock() 929 930 p.newConnWait.cleanFront() 931 p.newConnWait.pushBack(w) 932 p.createConnectionsCond.Signal() 933 } 934 935 func (p *pool) totalConnectionCount() int { 936 p.createConnectionsCond.L.Lock() 937 defer p.createConnectionsCond.L.Unlock() 938 939 return len(p.conns) 940 } 941 942 func (p *pool) availableConnectionCount() int { 943 p.idleMu.Lock() 944 defer p.idleMu.Unlock() 945 946 return len(p.idleConns) 947 } 948 949 // createConnections creates connections for wantConn requests on the newConnWait queue. 950 func (p *pool) createConnections(ctx context.Context, wg *sync.WaitGroup) { 951 defer wg.Done() 952 953 // condition returns true if the createConnections() loop should continue and false if it should 954 // wait. Note that the condition also listens for Context cancellation, which also causes the 955 // loop to continue, allowing for a subsequent check to return from createConnections(). 956 condition := func() bool { 957 checkOutWaiting := p.newConnWait.len() > 0 958 poolHasSpace := p.maxSize == 0 || uint64(len(p.conns)) < p.maxSize 959 cancelled := ctx.Err() != nil 960 return (checkOutWaiting && poolHasSpace) || cancelled 961 } 962 963 // wait waits for there to be an available wantConn and for the pool to have space for a new 964 // connection. When the condition becomes true, it creates a new connection and returns the 965 // waiting wantConn and new connection. If the Context is cancelled or there are any 966 // errors, wait returns with "ok = false". 967 wait := func() (*wantConn, *connection, bool) { 968 p.createConnectionsCond.L.Lock() 969 defer p.createConnectionsCond.L.Unlock() 970 971 for !condition() { 972 p.createConnectionsCond.Wait() 973 } 974 975 if ctx.Err() != nil { 976 return nil, nil, false 977 } 978 979 p.newConnWait.cleanFront() 980 w := p.newConnWait.popFront() 981 if w == nil { 982 return nil, nil, false 983 } 984 985 conn := newConnection(p.address, p.connOpts...) 986 conn.pool = p 987 conn.driverConnectionID = atomic.AddUint64(&p.nextID, 1) 988 p.conns[conn.driverConnectionID] = conn 989 990 return w, conn, true 991 } 992 993 for ctx.Err() == nil { 994 w, conn, ok := wait() 995 if !ok { 996 continue 997 } 998 999 if mustLogPoolMessage(p) { 1000 keysAndValues := logger.KeyValues{ 1001 logger.KeyDriverConnectionID, conn.driverConnectionID, 1002 } 1003 1004 logPoolMessage(p, logger.ConnectionCreated, keysAndValues...) 1005 } 1006 1007 if p.monitor != nil { 1008 p.monitor.Event(&event.PoolEvent{ 1009 Type: event.ConnectionCreated, 1010 Address: p.address.String(), 1011 ConnectionID: conn.driverConnectionID, 1012 }) 1013 } 1014 1015 // Pass the createConnections context to connect to allow pool close to cancel connection 1016 // establishment so shutdown doesn't block indefinitely if connectTimeout=0. 1017 err := conn.connect(ctx) 1018 if err != nil { 1019 w.tryDeliver(nil, err) 1020 1021 // If there's an error connecting the new connection, call the handshake error handler 1022 // that implements the SDAM handshake error handling logic. This must be called after 1023 // delivering the connection error to the waiting wantConn. If it's called before, the 1024 // handshake error handler may clear the connection pool, leading to a different error 1025 // message being delivered to the same waiting wantConn in idleConnWait when the wait 1026 // queues are cleared. 1027 if p.handshakeErrFn != nil { 1028 p.handshakeErrFn(err, conn.generation, conn.desc.ServiceID) 1029 } 1030 1031 _ = p.removeConnection(conn, reason{ 1032 loggerConn: logger.ReasonConnClosedError, 1033 event: event.ReasonError, 1034 }, err) 1035 1036 _ = p.closeConnection(conn) 1037 1038 continue 1039 } 1040 1041 if mustLogPoolMessage(p) { 1042 keysAndValues := logger.KeyValues{ 1043 logger.KeyDriverConnectionID, conn.driverConnectionID, 1044 } 1045 1046 logPoolMessage(p, logger.ConnectionReady, keysAndValues...) 1047 } 1048 1049 if p.monitor != nil { 1050 p.monitor.Event(&event.PoolEvent{ 1051 Type: event.ConnectionReady, 1052 Address: p.address.String(), 1053 ConnectionID: conn.driverConnectionID, 1054 }) 1055 } 1056 1057 if w.tryDeliver(conn, nil) { 1058 continue 1059 } 1060 1061 _ = p.checkInNoEvent(conn) 1062 } 1063 } 1064 1065 func (p *pool) maintain(ctx context.Context, wg *sync.WaitGroup) { 1066 defer wg.Done() 1067 1068 ticker := time.NewTicker(p.maintainInterval) 1069 defer ticker.Stop() 1070 1071 // remove removes the *wantConn at index i from the slice and returns the new slice. The order 1072 // of the slice is not maintained. 1073 remove := func(arr []*wantConn, i int) []*wantConn { 1074 end := len(arr) - 1 1075 arr[i], arr[end] = arr[end], arr[i] 1076 return arr[:end] 1077 } 1078 1079 // removeNotWaiting removes any wantConns that are no longer waiting from given slice of 1080 // wantConns. That allows maintain() to use the size of its wantConns slice as an indication of 1081 // how many new connection requests are outstanding and subtract that from the number of 1082 // connections to ask for when maintaining minPoolSize. 1083 removeNotWaiting := func(arr []*wantConn) []*wantConn { 1084 for i := len(arr) - 1; i >= 0; i-- { 1085 w := arr[i] 1086 if !w.waiting() { 1087 arr = remove(arr, i) 1088 } 1089 } 1090 1091 return arr 1092 } 1093 1094 wantConns := make([]*wantConn, 0, p.minSize) 1095 defer func() { 1096 for _, w := range wantConns { 1097 w.tryDeliver(nil, ErrPoolClosed) 1098 } 1099 }() 1100 1101 for { 1102 select { 1103 case <-ticker.C: 1104 case <-p.maintainReady: 1105 case <-ctx.Done(): 1106 return 1107 } 1108 1109 // Only maintain the pool while it's in the "ready" state. If the pool state is not "ready", 1110 // wait for the next tick or "ready" signal. Do all of this while holding the stateMu read 1111 // lock to prevent a state change between checking the state and entering the wait queue. 1112 // Not holding the stateMu read lock here may allow maintain() to request wantConns after 1113 // clear() pauses the pool and clears the wait queue, resulting in createConnections() 1114 // doing work while the pool is "paused". 1115 p.stateMu.RLock() 1116 if p.state != poolReady { 1117 p.stateMu.RUnlock() 1118 continue 1119 } 1120 1121 p.removePerishedConns() 1122 1123 // Remove any wantConns that are no longer waiting. 1124 wantConns = removeNotWaiting(wantConns) 1125 1126 // Figure out how many more wantConns we need to satisfy minPoolSize. Assume that the 1127 // outstanding wantConns (i.e. the ones that weren't removed from the slice) will all return 1128 // connections when they're ready, so only add wantConns to make up the difference. Limit 1129 // the number of connections requested to max 10 at a time to prevent overshooting 1130 // minPoolSize in case other checkOut() calls are requesting new connections, too. 1131 total := p.totalConnectionCount() 1132 n := int(p.minSize) - total - len(wantConns) 1133 if n > 10 { 1134 n = 10 1135 } 1136 1137 for i := 0; i < n; i++ { 1138 w := newWantConn() 1139 p.queueForNewConn(w) 1140 wantConns = append(wantConns, w) 1141 1142 // Start a goroutine for each new wantConn, waiting for it to be ready. 1143 go func() { 1144 <-w.ready 1145 if w.conn != nil { 1146 _ = p.checkInNoEvent(w.conn) 1147 } 1148 }() 1149 } 1150 p.stateMu.RUnlock() 1151 } 1152 } 1153 1154 func (p *pool) removePerishedConns() { 1155 p.idleMu.Lock() 1156 defer p.idleMu.Unlock() 1157 1158 for i := range p.idleConns { 1159 conn := p.idleConns[i] 1160 if conn == nil { 1161 continue 1162 } 1163 1164 if reason, perished := connectionPerished(conn); perished { 1165 p.idleConns[i] = nil 1166 1167 _ = p.removeConnection(conn, reason, nil) 1168 go func() { 1169 _ = p.closeConnection(conn) 1170 }() 1171 } 1172 } 1173 1174 p.idleConns = compact(p.idleConns) 1175 } 1176 1177 // compact removes any nil pointers from the slice and keeps the non-nil pointers, retaining the 1178 // order of the non-nil pointers. 1179 func compact(arr []*connection) []*connection { 1180 offset := 0 1181 for i := range arr { 1182 if arr[i] == nil { 1183 continue 1184 } 1185 arr[offset] = arr[i] 1186 offset++ 1187 } 1188 return arr[:offset] 1189 } 1190 1191 // A wantConn records state about a wanted connection (that is, an active call to checkOut). 1192 // The conn may be gotten by creating a new connection or by finding an idle connection, or a 1193 // cancellation may make the conn no longer wanted. These three options are racing against each 1194 // other and use wantConn to coordinate and agree about the winning outcome. 1195 // Based on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1174-1240 1196 type wantConn struct { 1197 ready chan struct{} 1198 1199 mu sync.Mutex // Guards conn, err 1200 conn *connection 1201 err error 1202 } 1203 1204 func newWantConn() *wantConn { 1205 return &wantConn{ 1206 ready: make(chan struct{}, 1), 1207 } 1208 } 1209 1210 // waiting reports whether w is still waiting for an answer (connection or error). 1211 func (w *wantConn) waiting() bool { 1212 select { 1213 case <-w.ready: 1214 return false 1215 default: 1216 return true 1217 } 1218 } 1219 1220 // tryDeliver attempts to deliver conn, err to w and reports whether it succeeded. 1221 func (w *wantConn) tryDeliver(conn *connection, err error) bool { 1222 w.mu.Lock() 1223 defer w.mu.Unlock() 1224 1225 if w.conn != nil || w.err != nil { 1226 return false 1227 } 1228 1229 w.conn = conn 1230 w.err = err 1231 if w.conn == nil && w.err == nil { 1232 panic("x/mongo/driver/topology: internal error: misuse of tryDeliver") 1233 } 1234 1235 close(w.ready) 1236 1237 return true 1238 } 1239 1240 // cancel marks w as no longer wanting a result (for example, due to cancellation). If a connection 1241 // has been delivered already, cancel returns it with p.checkInNoEvent(). Note that the caller must 1242 // not hold any locks on the pool while calling cancel. 1243 func (w *wantConn) cancel(p *pool, err error) { 1244 if err == nil { 1245 panic("x/mongo/driver/topology: internal error: misuse of cancel") 1246 } 1247 1248 w.mu.Lock() 1249 if w.conn == nil && w.err == nil { 1250 close(w.ready) // catch misbehavior in future delivery 1251 } 1252 conn := w.conn 1253 w.conn = nil 1254 w.err = err 1255 w.mu.Unlock() 1256 1257 if conn != nil { 1258 _ = p.checkInNoEvent(conn) 1259 } 1260 } 1261 1262 // A wantConnQueue is a queue of wantConns. 1263 // Based on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1242-1306 1264 type wantConnQueue struct { 1265 // This is a queue, not a deque. 1266 // It is split into two stages - head[headPos:] and tail. 1267 // popFront is trivial (headPos++) on the first stage, and 1268 // pushBack is trivial (append) on the second stage. 1269 // If the first stage is empty, popFront can swap the 1270 // first and second stages to remedy the situation. 1271 // 1272 // This two-stage split is analogous to the use of two lists 1273 // in Okasaki's purely functional queue but without the 1274 // overhead of reversing the list when swapping stages. 1275 head []*wantConn 1276 headPos int 1277 tail []*wantConn 1278 } 1279 1280 // len returns the number of items in the queue. 1281 func (q *wantConnQueue) len() int { 1282 return len(q.head) - q.headPos + len(q.tail) 1283 } 1284 1285 // pushBack adds w to the back of the queue. 1286 func (q *wantConnQueue) pushBack(w *wantConn) { 1287 q.tail = append(q.tail, w) 1288 } 1289 1290 // popFront removes and returns the wantConn at the front of the queue. 1291 func (q *wantConnQueue) popFront() *wantConn { 1292 if q.headPos >= len(q.head) { 1293 if len(q.tail) == 0 { 1294 return nil 1295 } 1296 // Pick up tail as new head, clear tail. 1297 q.head, q.headPos, q.tail = q.tail, 0, q.head[:0] 1298 } 1299 w := q.head[q.headPos] 1300 q.head[q.headPos] = nil 1301 q.headPos++ 1302 return w 1303 } 1304 1305 // peekFront returns the wantConn at the front of the queue without removing it. 1306 func (q *wantConnQueue) peekFront() *wantConn { 1307 if q.headPos < len(q.head) { 1308 return q.head[q.headPos] 1309 } 1310 if len(q.tail) > 0 { 1311 return q.tail[0] 1312 } 1313 return nil 1314 } 1315 1316 // cleanFront pops any wantConns that are no longer waiting from the head of the queue. 1317 func (q *wantConnQueue) cleanFront() { 1318 for { 1319 w := q.peekFront() 1320 if w == nil || w.waiting() { 1321 return 1322 } 1323 q.popFront() 1324 } 1325 }