docs: function () {
      var self = this;
      if (self._stopped)
        throw new Error("Called waitUntilCaughtUp on stopped handle!");
  
      // Calling waitUntilCaughtUp requries us to wait for the oplog connection to
      // be ready.
      self._readyFuture.wait();
  
      while (!self._stopped) {
        // We need to make the selector at least as restrictive as the actual
        // tailing selector (ie, we need to specify the DB name) or else we might
        // find a TS that won't show up in the actual tail stream.
        try {
          var lastEntry = self._oplogLastEntryConnection.findOne(
            OPLOG_COLLECTION, self._baseOplogSelector,
            {fields: {ts: 1}, sort: {$natural: -1}});
          break;
        } catch (e) {
          // During failover (eg) if we get an exception we should log and retry
          // instead of crashing.
          Meteor._debug("Got exception while reading last entry: " + e);
          Meteor._sleepForMs(100);
        }
      }
  
      if (self._stopped)
        return;
  
      if (!lastEntry) {
        // Really, nothing in the oplog? Well, we've processed everything.
        return;
      }
  
      var ts = lastEntry.ts;
      if (!ts)
        throw Error("oplog entry without ts: " + EJSON.stringify(lastEntry));
  
      if (self._lastProcessedTS && ts.lessThanOrEqual(self._lastProcessedTS)) {
        // We've already caught up to here.
        return;
      }
  
  
      // Insert the future into our list. Almost always, this will be at the end,
      // but it's conceivable that if we fail over from one primary to another,
      // the oplog entries we see will go backwards.
      var insertAfter = self._catchingUpFutures.length;
      while (insertAfter - 1 > 0
             && self._catchingUpFutures[insertAfter - 1].ts.greaterThan(ts)) {
        insertAfter--;
      }
      var f = new Future;
      self._catchingUpFutures.splice(insertAfter, 0, {ts: ts, future: f});
      f.wait();
    }