Node-redis: يظل الخطأ الذي يتم إرجاعه من retry_strategy بدون علم

تم إنشاؤها على ٢٧ فبراير ٢٠١٧  ·  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: connect
21: 00: 14.695Z TRACE script: عميل Redis: جاهز
21: 10: 23.837Z TRACE script: Redis client: end
إعادة محاولة فحص الاستراتيجية
{محاولة: 1،
الخطأ: {[خطأ: اتصال Redis بـ redis.callision. info: فشل
total_retry_time: 0 ،
عدد مرات الاتصال: 1}

/node_modules/q/q.js:155
رمي البريد
^
خطأ محبط: تم إنهاء اتصال الدفق وإحباط الأمر. ربما تمت معالجتها.
في 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)
في emitOne (events.js: 90: 13)
في Socket.emit (events.js: 182: 7)
في emitErrorNT (net.js: 1255: 8)
في nextTickCallbackWith2Args (node.js: 474: 9)
في process._tickCallback (node.js: 388: 17)

وكسؤال: لماذا يستغرق اكتشاف هذا الاتصال حوالي 10 دقائق؟ هل هناك طريقة لإثارة خطأ في حالة عدم الرد خلال 10 ثوانٍ؟ قد يكون أي خيار مثل response_timeout وما إلى ذلك.

  • الإصدار : node_redis v.2.6.5 و Redis 3.0.7
  • النظام الأساسي : Node.js v5.5.0 على Ubuntu 14.04.4 LTS
  • الوصف : يظل الخطأ من 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 ، والذي ينبعث عند "خطأ" بشكل صحيح بعد انتهاء مهلة الاتصال.

أواجه نفس المشكلة ، وينتهي استخدام إستراتيجية إعادة المحاولة المخصصة بنقطة نهاية سيئة في "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);
});

عند تصحيح أخطاء التطبيق ، أقوم بالدخول إلى الشيك ENOTFOUND كما هو مذكور أعلاه في retry_strategy ولكنه لا يستدعي معالج حدث الخطأ.

لدي نفس المشكلة ، بعد البحث في الكود المصدري وجدت أنه إذا تغيرنا
هذا الخط (أو تمكين وضع التصحيح)
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" فارغ ولا يتم إضافة الخطأ إلى المصفوفة وبالتالي لا يتم إرساله. إذا فهمت بشكل صحيح ، فهو جزء قديم جدًا من التعليمات البرمجية ، لذلك نحتاج إلى إدخال من المشرفين أو من

لقد رأيت أيضًا أنه في أول اتصال فاشل يصدر حدث "إنهاء" ، قد يعني ذلك شيئًا (أو لا) ، اخترت Redis منذ يومين ، لذا لست متأكدًا من كيفية عمل الأجزاء الداخلية حتى الآن. سأحاول التفرع والحفر أكثر قليلاً عندما / عندما يكون لدي الوقت.

ويبدو أن المشكلة التالية حرفياً مرتبطة بهذه المشكلة رقم 1198

@ v1adko أنا أسافر حاليًا ، لكنني سأحاول إلقاء نظرة عليها لاحقًا اليوم أو غدًا (ما لم يضربني روبن بذلك).

لدي عنوان url الخاص بي redis بشكل خاطئ عن عمد لاختبار سيناريوهات الخطأ ولكني أرى أن إعادة المحاولة الخاصة بي لم يتم استدعاءها عند محاولة الاتصال بـ redis. يتم استدعاء retry_strategy فقط عند إغلاق الاتصال.

"const redis = تتطلب ('redis') ؛
const log = تتطلب ('./ 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 QualifyUrl = (url) => {
إرجاع '//' + url.replace (/ ^ \ / + /، "") ؛
} ؛

"

هل يمكن لأي شخص مساعدتي في حل هذه المشكلة.

كذلك هنا. يبدو أن هذا الاختراق السيئ يخلق السلوك المتوقع ، لكن لست متأكدًا مما إذا كان له أي آثار أوسع:

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

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

أواجه نفس المشكلات في الوقت الحالي. باستخدام retry_strategy ، يتم إرجاع الخطأ كما هو مشار إليه في المثال في الملف التمهيدي ، ومع ذلك لم يتم إرسال أي خطأ من قبل العميل. الإصلاحات المقترحة من @ 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); }

تواجه نفس المشكلة ، لا تؤدي عملية إعادة المحاولة إلى حدوث خطأ ، ولا يوجد حل بعد؟

هل نجح أحد؟

بدلًا من ذلك ، قمنا بتحويل تطبيقنا إلى https://github.com/luin/ioredis ، مما أدى إلى بعض التحسينات (الوعود الأصلية ، lazyConnect (تجنب فتح اتصال عند إنشاء عميل redis ، ساعدنا في معالجة الأخطاء تمامًا حيثما احتجنا)) ، ويسمح بتشغيل الكود التالي:

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

متغيرات Env:

REDIS_URL=localhost:6379
REDIS_PASSWORD=mypasswordissostrong
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات