Skip to content

Commit 8f23bf4

Browse files
GODRIVER-2588 Run Close in cursor.All with context.Background(). (#1098)
Co-authored-by: Kevin Albertson <[email protected]>
1 parent 805904f commit 8f23bf4

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

mongo/cursor.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,10 @@ func (c *Cursor) All(ctx context.Context, results interface{}) error {
246246
var index int
247247
var err error
248248

249-
defer c.Close(ctx)
249+
// Defer a call to Close to try to clean up the cursor server-side when all
250+
// documents have not been exhausted. Use context.Background() to ensure Close
251+
// completes even if the context passed to All has errored.
252+
defer c.Close(context.Background())
250253

251254
batch := c.batch // exhaust the current batch before iterating the batch cursor
252255
for {

mongo/integration/cursor_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package integration
88

99
import (
1010
"context"
11+
"errors"
1112
"os"
1213
"testing"
1314
"time"
@@ -203,6 +204,51 @@ func TestCursor(t *testing.T) {
203204
assert.True(mt, ok, "expected mongo.CommandError, got: %T", err)
204205
assert.Equal(mt, failpointData.ErrorCode, mongoErr.Code, "expected code %v, got: %v", failpointData.ErrorCode, mongoErr.Code)
205206
})
207+
208+
mt.Run("deferred Close uses context.Background", func(mt *mtest.T) {
209+
initCollection(mt, mt.Coll)
210+
211+
// Find with batchSize 2 so All will run getMore for next 3 docs and error.
212+
cur, err := mt.Coll.Find(context.Background(), bson.D{},
213+
options.Find().SetBatchSize(2))
214+
assert.Nil(mt, err, "Find error: %v", err)
215+
216+
// Create a context and immediately cancel it.
217+
canceledCtx, cancel := context.WithCancel(context.Background())
218+
cancel()
219+
220+
// Clear "insert" and "find" events.
221+
mt.ClearEvents()
222+
223+
// Call All with the canceled context and expect context.Canceled.
224+
var docs []bson.D
225+
err = cur.All(canceledCtx, &docs)
226+
assert.NotNil(mt, err, "expected error for All, got nil")
227+
assert.True(mt, errors.Is(err, context.Canceled),
228+
"expected context.Canceled error, got %v", err)
229+
230+
// Assert that a "getMore" command was sent and failed (Next used the
231+
// canceled context).
232+
stEvt := mt.GetStartedEvent()
233+
assert.NotNil(mt, stEvt, `expected a "getMore" started event, got no event`)
234+
assert.Equal(mt, stEvt.CommandName, "getMore",
235+
`expected a "getMore" started event, got %q`, stEvt.CommandName)
236+
fEvt := mt.GetFailedEvent()
237+
assert.NotNil(mt, fEvt, `expected a failed "getMore" event, got no event`)
238+
assert.Equal(mt, fEvt.CommandName, "getMore",
239+
`expected a failed "getMore" event, got %q`, fEvt.CommandName)
240+
241+
// Assert that a "killCursors" command was sent and was successful (Close
242+
// used the 2 second Client Timeout).
243+
stEvt = mt.GetStartedEvent()
244+
assert.NotNil(mt, stEvt, `expected a "killCursors" started event, got no event`)
245+
assert.Equal(mt, stEvt.CommandName, "killCursors",
246+
`expected a "killCursors" started event, got %q`, stEvt.CommandName)
247+
suEvt := mt.GetSucceededEvent()
248+
assert.NotNil(mt, suEvt, `expected a successful "killCursors" event, got no event`)
249+
assert.Equal(mt, suEvt.CommandName, "killCursors",
250+
`expected a successful "killCursors" event, got %q`, suEvt.CommandName)
251+
})
206252
})
207253
mt.RunOpts("close", noClientOpts, func(mt *mtest.T) {
208254
failpointOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion("4.0")

0 commit comments

Comments
 (0)