Esempio n. 1
0
Command.prototype._iterateKeys = function (transform) {
  if (typeof this._keys === 'undefined') {
    if (typeof transform !== 'function') {
      transform = function (key) {
        return key;
      };
    }
    this._keys = [];
    if (commands.exists(this.name)) {
      var keyIndexes = commands.getKeyIndexes(this.name, this.args);
      for (var i = 0; i < keyIndexes.length; i++) {
        var index = keyIndexes[i];
        this.args[index] = transform(this.args[index]);
        this._keys.push(this.args[index]);
      }
    }
  }
  return this._keys;
};
Esempio n. 2
0
Pipeline.prototype.fillResult = function (value, position) {
  var i;
  if (this._queue[position].name === 'exec' && Array.isArray(value[1])) {
    var execLength = value[1].length;
    for (i = 0; i < execLength; i++) {
      if (value[1][i] instanceof Error) {
        continue;
      }
      var cmd = this._queue[position - (execLength - i)];
      try {
        value[1][i] = cmd.transformReply(value[1][i]);
      } catch (err) {
        value[1][i] = err;
      }
    }
  }
  this._result[position] = value;

  if (--this.replyPending) {
    return;
  }

  if (this.isCluster) {
    var retriable = true;
    var commonError;
    var inTransaction;
    for (i = 0; i < this._result.length; ++i) {
      var error = this._result[i][0];
      var command = this._queue[i];
      if (command.name === 'multi') {
        inTransaction = true;
      } else if (command.name === 'exec') {
        inTransaction = false;
      }
      if (error) {
        if (command.name === 'exec' && error.message === 'EXECABORT Transaction discarded because of previous errors.') {
          continue;
        }
        if (!commonError) {
          commonError = {
            name: error.name,
            message: error.message
          };
        } else if (commonError.name !== error.name || commonError.message !== error.message) {
          retriable = false;
          break;
        }
      } else if (!inTransaction) {
        var isReadOnly = commands.exists(command.name) && commands.hasFlag(command.name, 'readonly');
        if (!isReadOnly) {
          retriable = false;
          break;
        }
      }
    }
    if (commonError && retriable) {
      var _this = this;
      var errv = commonError.message.split(' ');
      var queue = this._queue;
      inTransaction = false;
      this._queue = [];
      for (i = 0; i < queue.length; ++i) {
        if (errv[0] === 'ASK' && !inTransaction &&
            queue[i].name !== 'asking' &&
            (!queue[i - 1] || queue[i - 1].name !== 'asking')) {
          var asking = new Command('asking');
          asking.ignore = true;
          this.sendCommand(asking);
        }
        queue[i].initPromise();
        this.sendCommand(queue[i]);
        if (queue[i].name === 'multi') {
          inTransaction = true;
        } else if (queue[i].name === 'exec') {
          inTransaction = false;
        }
      }

      var matched = true;
      if (typeof this.leftRedirections === 'undefined') {
        this.leftRedirections = {};
      }
      var exec = function () {
        _this.exec();
      };
      this.redis.handleError(commonError, this.leftRedirections, {
        moved: function (slot, key) {
          _this.preferKey = key;
          _this.redis.slots[errv[1]] = [key];
          _this.redis.refreshSlotsCache();
          _this.exec();
        },
        ask: function (slot, key) {
          _this.preferKey = key;
          _this.exec();
        },
        tryagain: exec,
        clusterDown: exec,
        connectionClosed: exec,
        maxRedirections: function () {
          matched = false;
        },
        defaults: function () {
          matched = false;
        }
      });
      if (matched) {
        return;
      }
    }
  }

  var ignoredCount = 0;
  for (i = 0; i < this._queue.length - ignoredCount; ++i) {
    if (this._queue[i + ignoredCount].ignore) {
      ignoredCount += 1;
    }
    this._result[i] = this._result[i + ignoredCount];
  }
  this.resolve(this._result.slice(0, this._result.length - ignoredCount));
};
Esempio n. 3
0
Cluster.prototype.sendCommand = function (command, stream, node) {
  if (this.status === 'end') {
    command.reject(new Error('Connection is closed.'));
    return command.promise;
  }
  var to = this.options.scaleReads;
  if (to !== 'master') {
    var isCommandReadOnly = commands.exists(command.name) && commands.hasFlag(command.name, 'readonly');
    if (!isCommandReadOnly) {
      to = 'master';
    }
  }

  var targetSlot = node ? node.slot : command.getSlot();
  var ttl = {};
  var _this = this;
  if (!node && !command.__is_reject_overwritten) {
    command.__is_reject_overwritten = true;
    var reject = command.reject;
    command.reject = function (err) {
      var partialTry = _.partial(tryConnection, true);
      _this.handleError(err, ttl, {
        moved: function (slot, key) {
          debug('command %s is moved to %s', command.name, key);
          if (_this.slots[slot]) {
            _this.slots[slot][0] = key;
          } else {
            _this.slots[slot] = [key];
          }
          var splitKey = key.split(':');
          _this.connectionPool.findOrCreate({ host: splitKey[0], port: Number(splitKey[1]) });
          tryConnection();
          _this.refreshSlotsCache();
        },
        ask: function (slot, key) {
          debug('command %s is required to ask %s:%s', command.name, key);
          tryConnection(false, key);
        },
        tryagain: partialTry,
        clusterDown: partialTry,
        connectionClosed: partialTry,
        maxRedirections: function (redirectionError) {
          reject.call(command, redirectionError);
        },
        defaults: function () {
          reject.call(command, err);
        }
      });
    };
  }
  tryConnection();

  function tryConnection(random, asking) {
    if (_this.status === 'end') {
      command.reject(new Error('Cluster is ended.'));
      return;
    }
    var redis;
    if (_this.status === 'ready' || (command.name === 'cluster')) {
      if (node && node.redis) {
        redis = node.redis;
      } else if (Command.checkFlag('ENTER_SUBSCRIBER_MODE', command.name) ||
                 Command.checkFlag('EXIT_SUBSCRIBER_MODE', command.name)) {
        redis = _this.subscriber;
      } else {
        if (!random) {
          if (typeof targetSlot === 'number' && _this.slots[targetSlot]) {
            var nodeKeys = _this.slots[targetSlot];
            if (typeof to === 'function') {
              var nodes =
                  nodeKeys
                  .map(function (key) {
                    return _this.connectionPool.nodes.all[key];
                  });
              redis = to(nodes, command);
              if (Array.isArray(redis)) {
                redis = utils.sample(redis);
              }
              if (!redis) {
                redis = nodes[0];
              }
            } else {
              var key;
              if (to === 'all') {
                key = utils.sample(nodeKeys);
              } else if (to === 'slave' && nodeKeys.length > 1) {
                key = utils.sample(nodeKeys, 1);
              } else {
                key = nodeKeys[0];
              }
              redis = _this.connectionPool.nodes.all[key];
            }
          }
          if (asking) {
            redis = _this.connectionPool.nodes.all[asking];
            redis.asking();
          }
        }
        if (!redis) {
          redis = _.sample(_this.connectionPool.nodes[to]) ||
            _.sample(_this.connectionPool.nodes.all);
        }
      }
      if (node && !node.redis) {
        node.redis = redis;
      }
    }
    if (redis) {
      redis.sendCommand(command, stream);
    } else if (_this.options.enableOfflineQueue) {
      _this.offlineQueue.push({
        command: command,
        stream: stream,
        node: node
      });
    } else {
      command.reject(new Error('Cluster isn\'t ready and enableOfflineQueue options is false'));
    }
  }
  return command.promise;
};