Node-redis: Kesalahan, yang dikembalikan dari retry_strategy tetap tidak tertangkap

Dibuat pada 27 Feb 2017  ·  19Komentar  ·  Sumber: NodeRedis/node-redis

Saya memiliki kode di bawah ini dan mencoba meniru koneksi yang terputus menggunakan 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');
});

Seharusnya semua kesalahan redis dipancarkan dalam acara kesalahan. Tapi inilah yang saya dapatkan di keluaran konsol:

21:00:14.666Z Skrip TRACE: Klien redis: sambungkan
21:00:14.695Z Skrip TRACE: Klien redis: siap
21:10:23.837Z Skrip TRACE: Klien redis: akhir
coba lagi cek strategi
{ upaya: 1,
error: { [Kesalahan: Redis koneksi ke redis.callision. info:6379 gagal - baca ECONNRESET] kode: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read' },
total_coba_waktu: 0,
times_connected: 1 }

/node_modules/q/q.js:155
lempar e;
^
AbortError: Koneksi streaming berakhir dan perintah dibatalkan. Mungkin sudah diproses.
di RedisClient.flush_and_error (/node_modules/redis/index.js:350:23)
di RedisClient.connection_gone (/node_modules/redis/index.js:612:18)
di RedisClient.on_error (/node_modules/redis/index.js:398:10)
di Soket.(/node_modules/redis/index.js:272:14)
di emitOne (events.js:90:13)
di Socket.emit (events.js:182:7)
di emitErrorNT (net.js:1255:8)
di nextTickCallbackWith2Args (node.js:474:9)
di process._tickCallback (node.js:388:17)

Dan sebagai pertanyaan: Mengapa dibutuhkan sekitar 10 menit untuk mendeteksi bahwa koneksi terputus? Apakah ada cara untuk meningkatkan kesalahan jika tidak ada respons dalam 10 detik? Mungkin ada opsi seperti response_timeout dll.

  • Versi : node_redis v.2.6.5 dan Redis 3.0.7
  • Platform : Node.js v5.5.0 di Ubuntu 14.04.4 LTS
  • Deskripsi : kesalahan dari retry_strategy tetap tidak tertangkap
pending-author-input

Komentar yang paling membantu

Ada berita ? Saya memiliki masalah yang sama.

Semua 19 komentar

@pavelsc Saya mencoba mereproduksi ini tetapi sejauh ini saya tidak bisa.

Silakan coba mereproduksi masalah tanpa modul pihak ketiga. Saat ini Anda tampaknya setidaknya menggunakan q .

Saya mengalami kesalahan yang sama. Jika saya sengaja memberikan klien Redis URL yang buruk, metode on.error tidak dipanggil. Berikut adalah contoh sederhana:

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");
};

Untuk saat ini saya kembali menggunakan connect_timeout, yang memancarkan 'kesalahan' dengan benar setelah batas waktu koneksi berakhir.

Saya mengalami masalah yang sama, menggunakan retry_strategy khusus dengan titik akhir yang buruk berakhir di "AbortError:"

Ini membuat saya keluar hari ini juga. Dari melihat kode secara singkat, ini tampaknya merupakan perilaku yang disengaja. https://github.com/NodeRedis/node_redis/blob/79558c524ff783000a6027fb159739770f98b10e/index.js#L405 secara eksplisit menyatakan bahwa jika retry_strategy disetel, jangan memancarkan kesalahan dan malah terus membuangnya. Saya ingin tahu mengapa ini terjadi, sepertinya tidak ada alasan mengapa itu tidak bisa memancarkannya alih-alih melempar dari pandangan sekilas. Apakah ada alasan kondisional ini tidak dapat dihilangkan, sehingga kesalahan akan selalu muncul?

Saya mengalami masalah ini juga, secara konsisten.

Saya juga tidak dapat menangkap kesalahan saat menerima 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;
}

dengan:

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

Men-debug aplikasi, saya masuk ke cek ENOTFOUND seperti yang disebutkan di atas dalam retry_strategy tetapi itu tidak menjalankan pengendali acara kesalahan.

Saya memiliki masalah yang sama, setelah menggali melalui kode sumber saya menemukan bahwa jika kita berubah
baris ini (atau aktifkan mode debug)
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L381 -L382

Dan masukkan potongan kode ini di sini (tambahkan kesalahan ke array segera)
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L352 -L353

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

Ia bekerja dan memancarkan 'kesalahan' dengan benar.

Loop bersarang dalam fungsi itu tidak dieksekusi, karena 'command_queue' kosong dan kesalahan tidak pernah ditambahkan ke array dan karenanya tidak dipancarkan. Jika saya mengerti dengan benar, ini adalah bagian kode yang cukup lama, jadi kami membutuhkan masukan dari pengelola atau dari @BridgeAR

Saya juga melihat bahwa pada koneksi pertama yang gagal memancarkan 'end' event , itu mungkin berarti sesuatu (atau tidak), saya mengambil Redis dua hari yang lalu, jadi belum yakin bagaimana internal bekerja. Saya akan mencoba untuk bercabang dan menggali sedikit lebih banyak ketika jika/ketika saya punya waktu.

Dan secara harfiah masalah berikutnya tampaknya terkait dengan masalah #1198 ini

@v1adko Saya sedang bepergian, tetapi saya akan mencoba melihatnya nanti hari ini atau besok (kecuali Ruben mengalahkan saya).

Saya memiliki url redis yang sengaja salah untuk menguji skenario kesalahan tetapi saya melihat retry_strategy saya tidak dipanggil ketika mencoba terhubung ke redis. retry_strategy dipanggil hanya ketika koneksi ditutup.

`const redis = membutuhkan('redis');
const log = membutuhkan('./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;

}

const kualifikasiUrl = (url) => {
return '//' + url.replace(/^\/+/,"");
};

`

Bisakah seseorang tolong bantu saya menyelesaikan masalah ini.

Sama disini. Peretasan jahat ini tampaknya menciptakan perilaku yang diharapkan, tetapi tidak yakin apakah itu memiliki implikasi yang lebih luas:

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

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

Saya mengalami masalah yang sama saat ini. Menggunakan retry_strategy, mengembalikan kesalahan seperti yang ditunjukkan oleh contoh di Readme, namun tidak ada kesalahan yang dipancarkan oleh klien. Perbaikan yang diusulkan oleh @v1adko memecahkan masalah setidaknya pada nilai nominal.

Saya bertanya-tanya apa ketidakcocokan ke belakang yang disebutkan di sini?
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L380

Seperti yang ditunjukkan oleh @maael , perilaku tersebut tampaknya disengaja ketika retry_strategy disetel. Jadi, apakah perilaku itu diharapkan, tetapi dokumentasinya salah? Haruskah saya memancarkan kesalahan untuk klien secara manual seperti yang disarankan oleh @c24w ?

edit: Saat saya menggali ke dalam paket, saya menyadari bahwa memancarkan secara manual mungkin bukan jalan ke depan. Sepertinya saya perlu memahami perubahan melanggar yang disebutkan.

Ada berita ? Saya memiliki masalah yang sama.

Ada berita?

adalah ide yang salah untuk dilakukan:
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); }

mengalami masalah yang sama, retry_Strategy tidak memicu peristiwa kesalahan, belum ada perbaikan?

Apakah ada yang berhasil?

Kami mengalihkan implementasi kami ke https://github.com/luin/ioredis sebagai gantinya, yang membawa beberapa peningkatan (Janji asli, lazyConnect(hindari membuka koneksi saat membuat instance klien redis, membantu kami menangani kesalahan tepat di tempat yang kami butuhkan)), dan memungkinkan kode berikut untuk dijalankan:

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);
  }

Menggunakan 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;
};

Dan file 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();
    });
  });
});

Variabel lingkungan:

REDIS_URL=localhost:6379
REDIS_PASSWORD=mypasswordissostrong
Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

strumwolf picture strumwolf  ·  4Komentar

twappworld picture twappworld  ·  7Komentar

Stono picture Stono  ·  6Komentar

shmendo picture shmendo  ·  6Komentar

Atala picture Atala  ·  3Komentar