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

    XMLWordPrintable

    Details

    • Type: Improvement
    • Status: Closed
    • Priority: Major - P3
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 1.4.0
    • Component/s: None
    • Security Level: Public
    • Labels:
      None

      Description

      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...
      

        Attachments

          Activity

            People

            Assignee:
            divjot.arora Divjot Arora
            Reporter:
            bartle David Bartley
            Participants:
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: