Uploaded image for project: 'Go Driver'
  1. Go Driver
  2. GODRIVER-1599

Add a way to determine if a call to Next or TryNext would block

    • Type: Icon: Improvement Improvement
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 1.4.0
    • Affects Version/s: None
    • Component/s: None
    • None

      Cursor has a TryNext method, but it's sort of confusing what it does. At first glance, it sounds like it'd return the next document of the underlying cursor batch if available, and return false if that's empty and a getMore would have to be issued. However, that's not what it does; instead, it will always issue a getMore if needed; the "try" variant simply prevents a subsequent getMore from being issued if no documents were returned.

      The simplest thing to do would be to add an IsBatchEmpty/WouldNextBlock/BatchDocsRemaining/etc... method. You could also add a 3rd variant of Next, though that'd probably make things even more confusing.

      As a workaround, I'm basically writing my tailer to look something like:

          cursor, err := client.Database("foo").Collection("bar").Find(ctx, bson.D{},
              options.Find().SetCursorType(options.TailableAwait))
          if err != nil {
              return err
          }
          defer cursor.Close(ctx)
          if err == nil {
              // Create a context that will cause any blocking call (i.e. `getMore`)
              // to fail; when this happens we'll sleep for a bit to prevent DoS'ing
              // the node.
              canceledCtx, cancel := context.WithCancel(ctx)
              cancel()
              nextCtx := canceled
      
              for {
                  for cursor.TryNext(nextCtx) {
                      nextCtx = canceledCtx
                      // Process current doc...
                  }
      
                  if err := cursor.Err(); err == context.Canceled {
                      // The driver must have tried to issue a `getMore`; sleep
                      // for a bit and then allow `TryNext` to block once.
                      time.Sleep(100 * time.Millisecond)
                      nextCtx = ctx
                      continue
                  } else if err != nil {
                      break
                  }
              }
          }
          // Handle cursor error...
      

      If I had something like IsBatchEmpty, I could write this as:

          cursor, err := client.Database("foo").Collection("bar").Find(ctx, bson.D{},
              options.Find().SetCursorType(options.TailableAwait))
          if err != nil {
              return err
          }
          defer cursor.Close(ctx)
          if err == nil {
              for cursor.TryNext(ctx) {
                  // Process current doc...
      
                  if cursor.IsBatchEmpty() {
                      // Avoid sending too many `getMore`s.
                      time.Sleep(100 * time.Millisecond)
                  }
              }
              err = cursor.Err()
          }
          // Handle cursor error...
      

            Assignee:
            divjot.arora@mongodb.com Divjot Arora (Inactive)
            Reporter:
            bartle David Bartley
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: