@@ -12,7 +12,6 @@ import (
1212 "fmt"
1313 "reflect"
1414 "strconv"
15- "time"
1615
1716 "go.mongodb.org/mongo-driver/v2/bson"
1817 "go.mongodb.org/mongo-driver/v2/internal/csot"
@@ -103,33 +102,6 @@ type changeStreamConfig struct {
103102 crypt driver.Crypt
104103}
105104
106- // validChangeStreamTimeouts will return "false" if maxAwaitTimeMS is set,
107- // timeoutMS is set to a non-zero value, and maxAwaitTimeMS is greater than or
108- // equal to timeoutMS. Otherwise, the timeouts are valid.
109- func validChangeStreamTimeouts (ctx context.Context , cs * ChangeStream ) bool {
110- if cs .options == nil || cs .client == nil {
111- return true
112- }
113-
114- maxAwaitTime := cs .options .MaxAwaitTime
115- timeout := cs .client .timeout
116-
117- if maxAwaitTime == nil {
118- return true
119- }
120-
121- if deadline , ok := ctx .Deadline (); ok {
122- ctxTimeout := time .Until (deadline )
123- timeout = & ctxTimeout
124- }
125-
126- if timeout == nil {
127- return true
128- }
129-
130- return * timeout <= 0 || * maxAwaitTime < * timeout
131- }
132-
133105func newChangeStream (ctx context.Context , config changeStreamConfig , pipeline interface {},
134106 opts ... options.Lister [options.ChangeStreamOptions ]) (* ChangeStream , error ) {
135107 if ctx == nil {
@@ -696,10 +668,33 @@ func (cs *ChangeStream) next(ctx context.Context, nonBlocking bool) bool {
696668}
697669
698670func (cs * ChangeStream ) loopNext (ctx context.Context , nonBlocking bool ) {
699- if ! validChangeStreamTimeouts (ctx , cs ) {
700- cs .err = fmt .Errorf ("MaxAwaitTime must be less than the operation timeout" )
671+ // To avoid unnecessary socket timeouts, we attempt to short-circuit tailable
672+ // awaitData "getMore" operations by ensuring that the maxAwaitTimeMS is less
673+ // than the operation timeout.
674+ //
675+ // The specifications assume that drivers iteratively apply the timeout
676+ // provided at the constructor level (e.g., (*collection).Find) for tailable
677+ // awaitData cursors:
678+ //
679+ // If set, drivers MUST apply the timeoutMS option to the initial aggregate
680+ // operation. Drivers MUST also apply the original timeoutMS value to each
681+ // next call on the change stream but MUST NOT use it to derive a maxTimeMS
682+ // field for getMore commands.
683+ //
684+ // The Go Driver might decide to support the above behavior with DRIVERS-2722.
685+ // The principal concern is that it would be unexpected for users to apply an
686+ // operation-level timeout via contexts to a constructor and then that timeout
687+ // later be applied while working with a resulting cursor. Instead, it is more
688+ // idiomatic to apply the timeout to the context passed to Next or TryNext.
689+ if cs .options != nil && ! nonBlocking {
690+ maxAwaitTime := cs .cursorOptions .MaxAwaitTime
691+
692+ // If maxAwaitTime is not set, this check is unnecessary.
693+ if maxAwaitTime != nil && ! mongoutil .TimeoutWithinContext (ctx , * maxAwaitTime ) {
694+ cs .err = fmt .Errorf ("MaxAwaitTime must be less than the operation timeout" )
701695
702- return
696+ return
697+ }
703698 }
704699
705700 // Apply the client-level timeout if the operation-level timeout is not set.
0 commit comments