Skip to content

Commit b605d09

Browse files
authored
GODRIVER-3172 Read response in the background after an op timeout. (#1589)
1 parent 722a2f2 commit b605d09

16 files changed

+868
-39
lines changed

internal/csot/csot.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ type timeoutKey struct{}
2121
// TODO default behavior.
2222
func MakeTimeoutContext(ctx context.Context, to time.Duration) (context.Context, context.CancelFunc) {
2323
// Only use the passed in Duration as a timeout on the Context if it
24-
// is non-zero.
24+
// is non-zero and if the Context doesn't already have a timeout.
2525
cancelFunc := func() {}
26-
if to != 0 {
26+
if _, deadlineSet := ctx.Deadline(); to != 0 && !deadlineSet {
2727
ctx, cancelFunc = context.WithTimeout(ctx, to)
2828
}
29+
30+
// Add timeoutKey either way to indicate CSOT is enabled.
2931
return context.WithValue(ctx, timeoutKey{}, true), cancelFunc
3032
}
3133

mongo/change_stream.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,10 @@ func (cs *ChangeStream) executeOperation(ctx context.Context, resuming bool) err
277277
cs.aggregate.Pipeline(plArr)
278278
}
279279

280-
// If no deadline is set on the passed-in context, cs.client.timeout is set, and context is not already
281-
// a Timeout context, honor cs.client.timeout in new Timeout context for change stream operation execution
282-
// and potential retry.
283-
if _, deadlineSet := ctx.Deadline(); !deadlineSet && cs.client.timeout != nil && !csot.IsTimeoutContext(ctx) {
280+
// If cs.client.timeout is set and context is not already a Timeout context,
281+
// honor cs.client.timeout in new Timeout context for change stream
282+
// operation execution and potential retry.
283+
if cs.client.timeout != nil && !csot.IsTimeoutContext(ctx) {
284284
newCtx, cancelFunc := csot.MakeTimeoutContext(ctx, *cs.client.timeout)
285285
// Redefine ctx to be the new timeout-derived context.
286286
ctx = newCtx

mongo/collection.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,15 @@ func aggregate(a aggregateParams) (cur *Cursor, err error) {
863863
Timeout(a.client.timeout).
864864
MaxTime(ao.MaxTime)
865865

866+
// Omit "maxTimeMS" from operations that return a user-managed cursor to
867+
// prevent confusing "cursor not found" errors. To maintain existing
868+
// behavior for users who set "timeoutMS" with no context deadline, only
869+
// omit "maxTimeMS" when a context deadline is set.
870+
//
871+
// See DRIVERS-2722 for more detail.
872+
_, deadlineSet := a.ctx.Deadline()
873+
op.OmitCSOTMaxTimeMS(deadlineSet)
874+
866875
if ao.AllowDiskUse != nil {
867876
op.AllowDiskUse(*ao.AllowDiskUse)
868877
}
@@ -1191,6 +1200,22 @@ func (coll *Collection) Distinct(ctx context.Context, fieldName string, filter i
11911200
// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/find/.
11921201
func (coll *Collection) Find(ctx context.Context, filter interface{},
11931202
opts ...*options.FindOptions) (cur *Cursor, err error) {
1203+
// Omit "maxTimeMS" from operations that return a user-managed cursor to
1204+
// prevent confusing "cursor not found" errors. To maintain existing
1205+
// behavior for users who set "timeoutMS" with no context deadline, only
1206+
// omit "maxTimeMS" when a context deadline is set.
1207+
//
1208+
// See DRIVERS-2722 for more detail.
1209+
_, deadlineSet := ctx.Deadline()
1210+
return coll.find(ctx, filter, deadlineSet, opts...)
1211+
}
1212+
1213+
func (coll *Collection) find(
1214+
ctx context.Context,
1215+
filter interface{},
1216+
omitCSOTMaxTimeMS bool,
1217+
opts ...*options.FindOptions,
1218+
) (cur *Cursor, err error) {
11941219

11951220
if ctx == nil {
11961221
ctx = context.Background()
@@ -1230,7 +1255,8 @@ func (coll *Collection) Find(ctx context.Context, filter interface{},
12301255
CommandMonitor(coll.client.monitor).ServerSelector(selector).
12311256
ClusterClock(coll.client.clock).Database(coll.db.name).Collection(coll.name).
12321257
Deployment(coll.client.deployment).Crypt(coll.client.cryptFLE).ServerAPI(coll.client.serverAPI).
1233-
Timeout(coll.client.timeout).MaxTime(fo.MaxTime).Logger(coll.client.logger)
1258+
Timeout(coll.client.timeout).MaxTime(fo.MaxTime).Logger(coll.client.logger).
1259+
OmitCSOTMaxTimeMS(omitCSOTMaxTimeMS)
12341260

12351261
cursorOpts := coll.client.createBaseCursorOptions()
12361262

@@ -1408,7 +1434,7 @@ func (coll *Collection) FindOne(ctx context.Context, filter interface{},
14081434
// by the server.
14091435
findOpts = append(findOpts, options.Find().SetLimit(-1))
14101436

1411-
cursor, err := coll.Find(ctx, filter, findOpts...)
1437+
cursor, err := coll.find(ctx, filter, false, findOpts...)
14121438
return &SingleResult{
14131439
ctx: ctx,
14141440
cur: cursor,

mongo/gridfs/bucket.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,10 @@ func (b *Bucket) Delete(fileID interface{}) error {
257257
//
258258
// Use the context parameter to time-out or cancel the delete operation. The deadline set by SetWriteDeadline is ignored.
259259
func (b *Bucket) DeleteContext(ctx context.Context, fileID interface{}) error {
260-
// If no deadline is set on the passed-in context, Timeout is set on the Client, and context is
261-
// not already a Timeout context, honor Timeout in new Timeout context for operation execution to
260+
// If Timeout is set on the Client and context is not already a Timeout
261+
// context, honor Timeout in new Timeout context for operation execution to
262262
// be shared by both delete operations.
263-
if _, deadlineSet := ctx.Deadline(); !deadlineSet && b.db.Client().Timeout() != nil && !csot.IsTimeoutContext(ctx) {
263+
if b.db.Client().Timeout() != nil && !csot.IsTimeoutContext(ctx) {
264264
newCtx, cancelFunc := csot.MakeTimeoutContext(ctx, *b.db.Client().Timeout())
265265
// Redefine ctx to be the new timeout-derived context.
266266
ctx = newCtx
@@ -384,10 +384,10 @@ func (b *Bucket) Drop() error {
384384
//
385385
// Use the context parameter to time-out or cancel the drop operation. The deadline set by SetWriteDeadline is ignored.
386386
func (b *Bucket) DropContext(ctx context.Context) error {
387-
// If no deadline is set on the passed-in context, Timeout is set on the Client, and context is
388-
// not already a Timeout context, honor Timeout in new Timeout context for operation execution to
387+
// If Timeout is set on the Client and context is not already a Timeout
388+
// context, honor Timeout in new Timeout context for operation execution to
389389
// be shared by both drop operations.
390-
if _, deadlineSet := ctx.Deadline(); !deadlineSet && b.db.Client().Timeout() != nil && !csot.IsTimeoutContext(ctx) {
390+
if b.db.Client().Timeout() != nil && !csot.IsTimeoutContext(ctx) {
391391
newCtx, cancelFunc := csot.MakeTimeoutContext(ctx, *b.db.Client().Timeout())
392392
// Redefine ctx to be the new timeout-derived context.
393393
ctx = newCtx

0 commit comments

Comments
 (0)