Node-redis: try_strategyから返されるエラーはキャッチされないままです

作成日 2017年02月27日  ·  19コメント  ·  ソース: NodeRedis/node-redis

私は以下のコードを持っており、 iptables -A OUTPUT -p tcp --dport 6379 -j REJECTを使用してドロップされた接続を模倣しようとしています。

self.client = redis.createClient(self.cfg.port, self.cfg.host, {
    retry_strategy: function (options) {
        console.log('retry strategy check');
        console.log(options);
        if (options.error) {
            if (options.error.code === 'ECONNREFUSED') {
                // End reconnecting on a specific error and flush all commands with a individual error
                return new Error('The server refused the connection');
            }
            if (options.error.code === 'ECONNRESET') {
                return new Error('The server reset the connection');
            }
            if (options.error.code === 'ETIMEDOUT') {
                return new Error('The server timeouted the connection');
            }
        }
        if (options.total_retry_time > 1000 * 60 * 60) {
            // End reconnecting after a specific timeout and flush all commands with a individual error
            return new Error('Retry time exhausted');
        }
        if (options.attempt > 5) {
            // End reconnecting with built in error
            return new Error('Retry attempts ended');
        }
        // reconnect after
        return 1000;
    }
});
self.client.on('ready', function () {
    log.trace('Redis client: ready');
});
self.client.on('connect', function () {
    log.trace('Redis client: connect');
});
self.client.on('reconnecting', function () {
    log.trace('Redis client: reconnecting');
});
self.client.on('error', function (err) {
    log.error({err: err}, 'Listener.redis.client error: %s', err);
    process.exit(1);
});
self.client.on('end', function () {
    log.trace('Redis client: end');
});
self.client.on('warning', function () {
    log.trace('Redis client: warning');
});

エラーイベントでは、すべてのredisエラーが発生すると想定されています。 しかし、これが私がコンソール出力に持っているものです:

21:00:14.666Z TRACEスクリプト:Redisクライアント:接続
21:00:14.695Z TRACEスクリプト:Redisクライアント:準備完了
21:10:23.837Z TRACEスクリプト:Redisクライアント:終了
戦略チェックを再試行する
{試行:1
エラー:{[エラー:redis.callisionへのRedis接続。 info:6379 failed-read ECONNRESET] code: 'ECONNRESET'、errno: 'ECONNRESET'、syscall: 'read'}、
total_retry_time:0、
times_connected:1}

/node_modules/q/q.js:155
eを投げます。
^
AbortError:ストリーム接続が終了し、コマンドが中止されました。 処理された可能性があります。
RedisClient.flush_and_error(/node_modules/redis/index.js:350:23)で
RedisClient.connection_gone(/node_modules/redis/index.js:612:18)で
RedisClient.on_error(/node_modules/redis/index.js:398:10)で
ソケットで。(/node_modules/redis/index.js:272:14)
EmmitOneで(events.js:90:13)
Socket.emitで(events.js:182:7)
EmmitErrorNT(net.js:1255:8)で
nextTickCallbackWith2Args(node.js:474:9)で
process._tickCallback(node.js:388:17)で

そして質問として:接続が失われたことを検出するのになぜ約10分かかるのですか? 10秒以内に応答がない場合にエラーを発生させる方法はありますか? response_timeoutなどの任意のオプションである可能性があります。

  • バージョン:node_redisv.2.6.5およびRedis3.0.7
  • プラットフォーム:Ubuntu 14.04.4LTS上のNode.jsv5.5.0
  • 説明:retry_strategyからのエラーはキャッチされないままです
pending-author-input

最も参考になるコメント

連絡あった ? 私も同じ問題を抱えてる。

全てのコメント19件

@pavelscこれを再現しようとしましたが、今のところできませんでした。

サードパーティのモジュールを使用せずに問題を再現してみてください。 現在、少なくともq使用しているようです。

同じエラーが発生しています。 意図的にRedisクライアントに不正なURLを提供した場合、on.errorメソッドは呼び出されません。 簡単な例を次に示します。

var redis = require("redis");

exports.handler = function (event, context, callback) {

    console.log("Executing test lambda for diagnosing redis issues");

    var redisInfo = {
        HOST: process.env.REDIS_HOST,
        PORT: process.env.REDIS_PORT
    };

    console.log(process.env.REDIS_HOST);
    console.log(process.env.REDIS_PORT);

    console.log("Connecting to Redis...");

    var client = redis.createClient({
        host: redisInfo.HOST,
        port: redisInfo.PORT,
        retry_strategy: function (options) {

            if (options.total_retry_time > 2000) {
                console.log("throwing an error...");
                return new Error('Retry time exhausted');
            }

            return 200;
        }
    });

    // if you'd like to select database 3, instead of 0 (default), call
    // client.select(3, function() { /* ... */ });

    client.on("error", function (err) {
        console.log("Error " + err);
        callback(null, "Error with Redis");
    });

    client.on('connect', function() {
        console.log("Connected to Redis");
    });

    client.on('end', function() {
        console.log("Redis end");
    });

    client.set("string key", "string val", redis.print);
    client.hset("hash key", "hashtest 1", "some value", redis.print);
    client.hset(["hash key", "hashtest 2", "some other value"], redis.print);
    client.hkeys("hash key", function (err, replies) {
        console.log(replies.length + " replies:");
        replies.forEach(function (reply, i) {
            console.log("    " + i + ": " + reply);
        });
        client.quit();
    });

    client.quit();

    callback(null, "Success");
};

今のところ、connect_timeoutの使用に戻っています。これは、接続タイムアウトの期限が切れた後、「エラー」を正しく出力します。

私は同じ問題を抱えています。悪いエンドポイントでカスタムretry_strategyを使用すると、「AbortError:」になります。

これは今日も私を捕らえました。 コードを簡単に見ると、これは意図的な動作のようです。 https://github.com/NodeRedis/node_redis/blob/79558c524ff783000a6027fb159739770f98b10e/index.js#L405は、 retry_strategyが設定されている場合、エラーを発行せず、代わりにエラーをスローし続けることを明示的に示しています。 なぜそうなのか知りたいのですが、一目見ただけでは出せない理由はないようです。 この条件を取り除くことができなかった理由はありますか?そのため、エラーは常に発生しますか?

私も一貫してこの問題を抱えています。

私もENOTFOUND受け取ったときにエラーをキャッチできません。

{
    host: "foo",
    retry_strategy: function (options) {
        if (options.error && options.error.code === "ENOTFOUND") {
            return new Error("The server was not found");
        }

        // reconnect after
        return 1000;
}

と:

redis.on("error", err => {
    console.error("Cache Error: " + err);
});

アプリケーションをデバッグしているときに、上記のretry_strategyたようにENOTFOUNDチェックに入りますが、エラーイベントハンドラーを呼び出していません。

私は同じ問題を抱えています。ソースコードを調べた後、変更すると
この行(またはデバッグモードを有効にする)
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L381 -L382

そして、このコードをここに挿入します(すぐに配列にエラーを追加します)
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L352 -L353

    if (options.error) {
      aggregated_errors.push(options.error);
    }

それは動作し、「エラー」を正しく出力します。

'command_queue'は空であり、エラーが配列に追加されることはなく、したがって発行されないため、その関数のネストされたループは実行されません。 私が正しく理解していれば、それはかなり古いコードなので、メンテナまたは@BridgeARからの入力が必要

また、最初に失敗した接続で「end」イベントが発生することもわかりました。これは何かを意味する(または意味しない)可能性があります。2日前にRedisを取得したので、内部がどのように機能するかはまだわかりません。 時間があれば、もう少しフォークして掘り下げてみます。

そして文字通り次の問題はこの問題に関連しているようです#1198

@ v1adko私は現在旅行中ですが、今日または明日(ルーベンが私を打ち

エラーシナリオをテストするためにredisURLが意図的に間違っていますが、redisに接続しようとしたときにretry_strategyが呼び出されないことがわかります。 try_strategyは、接続が閉じられたときにのみ呼び出されます。

`const redis = require( 'redis');
const log = require( './ logUtil')。logger;

module.exports.connect =()=> {

var redisRetryStrategy = function(options) {
    if (options.error && options.error.code === 'ECONNREFUSED') {
        // End reconnecting on a specific error and flush all commands with 
        // a individual error
        log.error('The redis server refused the connection');
        return new Error('The redis server refused the connection');
    }

    log.info(`Already spent ${options.total_retry_time} milliseconds to re-establish connection with redis`);
    if (options.total_retry_time > 2000) {
        // End reconnecting after a specific timeout and flush all commands 
        // with a individual error 
        log.error('Retry time exhausted');
        return new Error('Retry time exhausted');
    }
    log.info(`Attempting ${options.attempt} time to establish connection with redis`);
    if (options.attempt > 5) {
        // End reconnecting with built in error 
        log.error('Exhausted the retry attempts to establish connection to redis');
        return undefined;
    }
    // reconnect after 
    return 100;
}


log.info(`Redis connection url is :${process.env.REDIS_URL}`);
var redisClient = redis.createClient(qualifyUrl(process.env.REDIS_URL), {
    retry_strategy: redisRetryStrategy
});

redisClient.offline_queue_length = 3;

redisClient.on('connect', function() {
    console.log('Connected to Redis');
});

redisClient.on('reconnecting', function() {
    console.log('Re-Connecting to Redis');
});

redisClient.on('error', (err)=> {
    console.log(`Error trying to create redis connection: ${JSON.stringify(err)}`);
});
return redisClient;

}

constqualifyUrl =(url)=> {
'//' + url.replace(/ ^ \ / + /、 "");を返します。
};

`

誰かが私がこの問題を解決するのを手伝ってくれませんか。

こっちも一緒。 この厄介なハッキングは、期待される動作を生み出すように見えますが、それがより広い意味を持つかどうかはわかりません。

const client = redis.createClient({
  retry_strategy: ({error}) => client.emit('error', error)
});

client.on('error', console.error);

現在、同じ問題が発生しています。 try_strategyを使用して、Readmeの例に示されているようにエラーを返しますが、クライアントからエラーは発生しません。 @ v1adkoによって提案された修正は、少なくとも額面

ここで言及されている後方互換性は何ですか?
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L380

@maaelが指摘しているように、retry_strategyが設定されている@ c24wによって提案されているように、クライアントに対して手動でエラーを

編集:私がパッケージを掘り下げているとき、私は手動で放出することはおそらく前進する方法ではないことに気づいています。 言及された重大な変更を理解する必要があるようです。

連絡あった ? 私も同じ問題を抱えてる。

連絡あった?

行うのは間違った考えです:
js if (options.error && options.error.code === 'ECONNREFUSED') { // End reconnecting on a specific error and flush all commands with // a individual error return Math.min(options.attempt * 100, 3000); }

同じ問題があり、retry_Strategyがエラーイベントをトリガーしていません。まだ修正されていませんか?

誰かが成功しましたか?

代わりに、実装をhttps://github.com/luin/ioredisに切り替えました

let cachedItem;

  try {
    logger.debug(`Fetching GraphCMS query in redis cache...`);
    // XXX If fetching data from redis fails, we will fall back to running the query against GraphCMS API in order to ensure the client gets the data anyway
    cachedItem = await redisClient.get(body);
  } catch (e) {
    logger.debug(`An exception occurred while fetching redis cache.`);
    logger.error(e);
    epsagon.setError(e);
  }

次のutils/redis.jsを使用します。

import { createLogger } from '@unly/utils-simple-logger';
import Redis from 'ioredis';
import epsagon from './epsagon';

const logger = createLogger({
  label: 'Redis client',
});

/**
 * Creates a redis client
 *
 * <strong i="11">@param</strong> url Url of the redis client, must contain the port number and be of the form "localhost:6379"
 * <strong i="12">@param</strong> password Password of the redis client
 * <strong i="13">@param</strong> maxRetriesPerRequest By default, all pending commands will be flushed with an error every 20 retry attempts.
 *          That makes sure commands won't wait forever when the connection is down.
 *          Set to null to disable this behavior, and every command will wait forever until the connection is alive again.
 * <strong i="14">@return</strong> {Redis}
 */
export const getClient = (url = process.env.REDIS_URL, password = process.env.REDIS_PASSWORD, maxRetriesPerRequest = 20) => {
  const client = new Redis(`redis://${url}`, {
    password,
    showFriendlyErrorStack: true, // See https://github.com/luin/ioredis#error-handling
    lazyConnect: true, // XXX Don't attempt to connect when initializing the client, in order to properly handle connection failure on a use-case basis
    maxRetriesPerRequest,
  });

  client.on('connect', function () {
    logger.info('Connected to redis instance');
  });

  client.on('ready', function () {
    logger.info('Redis instance is ready (data loaded from disk)');
  });

  // Handles redis connection temporarily going down without app crashing
  // If an error is handled here, then redis will attempt to retry the request based on maxRetriesPerRequest
  client.on('error', function (e) {
    logger.error(`Error connecting to redis: "${e}"`);
    epsagon.setError(e);
  });

  return client;
};

そしてutils/redis.test.jsファイル:

import { getClient } from './redis';

let redisClient;
let redisClientFailure;

describe('utils/redis.js', () => {
  beforeAll(() => {
    redisClient = getClient();
    redisClientFailure = getClient('localhost:5555', null, 0); // XXX This shouldn't throw an error because we're using lazyConnect:true which doesn't automatically connect to redis
  });

  afterAll(async () => {
    await redisClient.quit();
    await redisClientFailure.quit();
  });

  describe('should successfully init the redis client', () => {
    test('when provided connection info are correct', async () => {
      // Environment variables are from the .env.test file - This tests a localhost connection only
      expect(redisClient.options.host).toEqual(process.env.REDIS_URL.split(':')[0]);
      expect(redisClient.options.port).toEqual(parseInt(process.env.REDIS_URL.split(':')[1], 10));
      expect(redisClient.options.password).toEqual(process.env.REDIS_PASSWORD);
    });

    test('when connection info are incorrect', async () => {
      expect(redisClientFailure.options.host).toEqual('localhost');
      expect(redisClientFailure.options.port).toEqual(5555);
    });
  });

  describe('should successfully perform native operations (read/write/delete/update)', () => {
    test('when using async/await (using native node.js promises)', async () => {
      const setResult = await redisClient.set('key-1', 'value-1');
      expect(setResult).toEqual('OK');

      const result = await redisClient.get('key-1');
      expect(result).toEqual('value-1');

      const delResult = await redisClient.del('key-1');
      expect(delResult).toEqual(1);

      const setResultB = await redisClient.set('key-1', 'value-1b');
      expect(setResultB).toEqual('OK');

      const resultB = await redisClient.get('key-1');
      expect(resultB).toEqual('value-1b');

      const setResultC = await redisClient.set('key-1', 'value-1c');
      expect(setResultC).toEqual('OK');

      const resultC = await redisClient.get('key-1');
      expect(resultC).toEqual('value-1c');
    });
  });

  describe('should allow to catch an error when failing to open a connection to redis, in order to gracefully handle the error instead of crashing the app', () => {
    test('when connection info are incorrect', async () => {
      expect(redisClientFailure.options.host).toEqual('localhost');
      expect(redisClientFailure.options.port).toEqual(5555);

      try {
        await redisClientFailure.set('key-1', 'value-1'); // This should throw an error, because the connection to redis will be made when executing the
        expect(true).toBe(false); // This shouldn't be called, or the test will fail
      } catch (e) {
        expect(e).toBeDefined();
        expect(e.message).toContain('Reached the max retries per request limit');
      }
      await redisClientFailure.quit();
    });
  });
});

環境変数:

REDIS_URL=localhost:6379
REDIS_PASSWORD=mypasswordissostrong
このページは役に立ちましたか?
0 / 5 - 0 評価

関連する問題

strumwolf picture strumwolf  ·  4コメント

Atala picture Atala  ·  3コメント

juriansluiman picture juriansluiman  ·  3コメント

dotSlashLu picture dotSlashLu  ·  5コメント

Mickael-van-der-Beek picture Mickael-van-der-Beek  ·  6コメント