This commit is contained in:
		
							
								
								
									
										46
									
								
								vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/go.mongodb.org/mongo-driver/x/mongo/driver/operation.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -59,8 +59,9 @@ type RetryablePoolError interface {
 | 
			
		||||
	Retryable() bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LabeledError is an error that can have error labels added to it.
 | 
			
		||||
type LabeledError interface {
 | 
			
		||||
// labeledError is an error that can have error labels added to it.
 | 
			
		||||
type labeledError interface {
 | 
			
		||||
	error
 | 
			
		||||
	HasErrorLabel(string) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -398,9 +399,19 @@ func (op Operation) Execute(ctx context.Context) error {
 | 
			
		||||
		// Set the previous indefinite error to be returned in any case where a retryable write error does not have a
 | 
			
		||||
		// NoWritesPerfomed label (the definite case).
 | 
			
		||||
		switch err := err.(type) {
 | 
			
		||||
		case LabeledError:
 | 
			
		||||
		case labeledError:
 | 
			
		||||
			// If the "prevIndefiniteErr" is nil, then the current error is the first error encountered
 | 
			
		||||
			// during the retry attempt cycle. We must persist the first error in the case where all
 | 
			
		||||
			// following errors are labeled "NoWritesPerformed", which would otherwise raise nil as the
 | 
			
		||||
			// error.
 | 
			
		||||
			if prevIndefiniteErr == nil {
 | 
			
		||||
				prevIndefiniteErr = err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// If the error is not labeled NoWritesPerformed and is retryable, then set the previous
 | 
			
		||||
			// indefinite error to be the current error.
 | 
			
		||||
			if !err.HasErrorLabel(NoWritesPerformed) && err.HasErrorLabel(RetryableWriteError) {
 | 
			
		||||
				prevIndefiniteErr = err.(error)
 | 
			
		||||
				prevIndefiniteErr = err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -595,6 +606,13 @@ func (op Operation) Execute(ctx context.Context) error {
 | 
			
		||||
		finishedInfo.cmdErr = err
 | 
			
		||||
		op.publishFinishedEvent(ctx, finishedInfo)
 | 
			
		||||
 | 
			
		||||
		// prevIndefiniteErrorIsSet is "true" if the "err" variable has been set to the "prevIndefiniteErr" in
 | 
			
		||||
		// a case in the switch statement below.
 | 
			
		||||
		var prevIndefiniteErrIsSet bool
 | 
			
		||||
 | 
			
		||||
		// TODO(GODRIVER-2579): When refactoring the "Execute" method, consider creating a separate method for the
 | 
			
		||||
		// error handling logic below. This will remove the necessity of the "checkError" goto label.
 | 
			
		||||
	checkError:
 | 
			
		||||
		var perr error
 | 
			
		||||
		switch tt := err.(type) {
 | 
			
		||||
		case WriteCommandError:
 | 
			
		||||
@@ -627,9 +645,13 @@ func (op Operation) Execute(ctx context.Context) error {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// If the error is no longer retryable and has the NoWritesPerformed label, then we should
 | 
			
		||||
			// return the previous indefinite error.
 | 
			
		||||
			if tt.HasErrorLabel(NoWritesPerformed) {
 | 
			
		||||
				return prevIndefiniteErr
 | 
			
		||||
			// set the error to the "previous indefinite error" unless the current error is already the
 | 
			
		||||
			// "previous indefinite error". After reseting, repeat the error check.
 | 
			
		||||
			if tt.HasErrorLabel(NoWritesPerformed) && !prevIndefiniteErrIsSet {
 | 
			
		||||
				err = prevIndefiniteErr
 | 
			
		||||
				prevIndefiniteErrIsSet = true
 | 
			
		||||
 | 
			
		||||
				goto checkError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// If the operation isn't being retried, process the response
 | 
			
		||||
@@ -720,9 +742,13 @@ func (op Operation) Execute(ctx context.Context) error {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// If the error is no longer retryable and has the NoWritesPerformed label, then we should
 | 
			
		||||
			// return the previous indefinite error.
 | 
			
		||||
			if tt.HasErrorLabel(NoWritesPerformed) {
 | 
			
		||||
				return prevIndefiniteErr
 | 
			
		||||
			// set the error to the "previous indefinite error" unless the current error is already the
 | 
			
		||||
			// "previous indefinite error". After reseting, repeat the error check.
 | 
			
		||||
			if tt.HasErrorLabel(NoWritesPerformed) && !prevIndefiniteErrIsSet {
 | 
			
		||||
				err = prevIndefiniteErr
 | 
			
		||||
				prevIndefiniteErrIsSet = true
 | 
			
		||||
 | 
			
		||||
				goto checkError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// If the operation isn't being retried, process the response
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								vendor/go.mongodb.org/mongo-driver/x/mongo/driver/topology/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -520,6 +520,7 @@ func (s *Server) update() {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeoutCnt := 0
 | 
			
		||||
	for {
 | 
			
		||||
		// Check if the server is disconnecting. Even if waitForNextCheck has already read from the done channel, we
 | 
			
		||||
		// can safely read from it again because Disconnect closes the channel.
 | 
			
		||||
@@ -545,18 +546,42 @@ func (s *Server) update() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Must hold the processErrorLock while updating the server description and clearing the
 | 
			
		||||
		// pool. Not holding the lock leads to possible out-of-order processing of pool.clear() and
 | 
			
		||||
		// pool.ready() calls from concurrent server description updates.
 | 
			
		||||
		s.processErrorLock.Lock()
 | 
			
		||||
		s.updateDescription(desc)
 | 
			
		||||
		if err := desc.LastError; err != nil {
 | 
			
		||||
			// Clear the pool once the description has been updated to Unknown. Pass in a nil service ID to clear
 | 
			
		||||
			// because the monitoring routine only runs for non-load balanced deployments in which servers don't return
 | 
			
		||||
			// IDs.
 | 
			
		||||
			s.pool.clear(err, nil)
 | 
			
		||||
		if isShortcut := func() bool {
 | 
			
		||||
			// Must hold the processErrorLock while updating the server description and clearing the
 | 
			
		||||
			// pool. Not holding the lock leads to possible out-of-order processing of pool.clear() and
 | 
			
		||||
			// pool.ready() calls from concurrent server description updates.
 | 
			
		||||
			s.processErrorLock.Lock()
 | 
			
		||||
			defer s.processErrorLock.Unlock()
 | 
			
		||||
 | 
			
		||||
			s.updateDescription(desc)
 | 
			
		||||
			// Retry after the first timeout before clearing the pool in case of a FAAS pause as
 | 
			
		||||
			// described in GODRIVER-2577.
 | 
			
		||||
			if err := unwrapConnectionError(desc.LastError); err != nil && timeoutCnt < 1 {
 | 
			
		||||
				if err == context.Canceled || err == context.DeadlineExceeded {
 | 
			
		||||
					timeoutCnt++
 | 
			
		||||
					// We want to immediately retry on timeout error. Continue to next loop.
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
				if err, ok := err.(net.Error); ok && err.Timeout() {
 | 
			
		||||
					timeoutCnt++
 | 
			
		||||
					// We want to immediately retry on timeout error. Continue to next loop.
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if err := desc.LastError; err != nil {
 | 
			
		||||
				// Clear the pool once the description has been updated to Unknown. Pass in a nil service ID to clear
 | 
			
		||||
				// because the monitoring routine only runs for non-load balanced deployments in which servers don't return
 | 
			
		||||
				// IDs.
 | 
			
		||||
				s.pool.clear(err, nil)
 | 
			
		||||
			}
 | 
			
		||||
			// We're either not handling a timeout error, or we just handled the 2nd consecutive
 | 
			
		||||
			// timeout error. In either case, reset the timeout count to 0 and return false to
 | 
			
		||||
			// continue the normal check process.
 | 
			
		||||
			timeoutCnt = 0
 | 
			
		||||
			return false
 | 
			
		||||
		}(); isShortcut {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		s.processErrorLock.Unlock()
 | 
			
		||||
 | 
			
		||||
		// If the server supports streaming or we're already streaming, we want to move to streaming the next response
 | 
			
		||||
		// without waiting. If the server has transitioned to Unknown from a network error, we want to do another
 | 
			
		||||
@@ -707,19 +732,31 @@ func (s *Server) check() (description.Server, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	var durationNanos int64
 | 
			
		||||
 | 
			
		||||
	// Create a new connection if this is the first check, the connection was closed after an error during the previous
 | 
			
		||||
	// check, or the previous check was cancelled.
 | 
			
		||||
	start := time.Now()
 | 
			
		||||
	if s.conn == nil || s.conn.closed() || s.checkWasCancelled() {
 | 
			
		||||
		// Create a new connection if this is the first check, the connection was closed after an error during the previous
 | 
			
		||||
		// check, or the previous check was cancelled.
 | 
			
		||||
		isNilConn := s.conn == nil
 | 
			
		||||
		if !isNilConn {
 | 
			
		||||
			s.publishServerHeartbeatStartedEvent(s.conn.ID(), false)
 | 
			
		||||
		}
 | 
			
		||||
		// Create a new connection and add it's handshake RTT as a sample.
 | 
			
		||||
		err = s.setupHeartbeatConnection()
 | 
			
		||||
		durationNanos = time.Since(start).Nanoseconds()
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			// Use the description from the connection handshake as the value for this check.
 | 
			
		||||
			s.rttMonitor.addSample(s.conn.helloRTT)
 | 
			
		||||
			descPtr = &s.conn.desc
 | 
			
		||||
			if !isNilConn {
 | 
			
		||||
				s.publishServerHeartbeatSucceededEvent(s.conn.ID(), durationNanos, s.conn.desc, false)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			err = unwrapConnectionError(err)
 | 
			
		||||
			if !isNilConn {
 | 
			
		||||
				s.publishServerHeartbeatFailedEvent(s.conn.ID(), durationNanos, err, false)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if descPtr == nil && err == nil {
 | 
			
		||||
	} else {
 | 
			
		||||
		// An existing connection is being used. Use the server description properties to execute the right heartbeat.
 | 
			
		||||
 | 
			
		||||
		// Wrap conn in a type that implements driver.StreamerConnection.
 | 
			
		||||
@@ -729,7 +766,6 @@ func (s *Server) check() (description.Server, error) {
 | 
			
		||||
		streamable := previousDescription.TopologyVersion != nil
 | 
			
		||||
 | 
			
		||||
		s.publishServerHeartbeatStartedEvent(s.conn.ID(), s.conn.getCurrentlyStreaming() || streamable)
 | 
			
		||||
		start := time.Now()
 | 
			
		||||
		switch {
 | 
			
		||||
		case s.conn.getCurrentlyStreaming():
 | 
			
		||||
			// The connection is already in a streaming state, so we stream the next response.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user