Node-redis: retry_strategy не сбрасывается после сброса ошибки

Созданный на 18 февр. 2017  ·  21Комментарии  ·  Источник: NodeRedis/node-redis

  • Версия : node_redis 2.6.5, redis 3.2.7
  • Платформа : Node.js 7.5.0 в Mac OS 10.12.3
  • Описание :

Я использую следующий код

const redis = require('redis');

const client = redis.createClient({
  url: 'redis://localhost:6379',
  retry_strategy: options => new Error('Fail')
});

setInterval(() => client.incr('some-key', console.log), 5000);

работает против локального Redis ( docker run --rm -p 6379:6379 redis )

Увидев первый вывод, я убиваю redis, чтобы имитировать отключение. Когда я вижу, что появляется ошибка, я перезапускаю redis. Связь больше не появляется. Я ожидал, что после сброса ошибки в автономные обработчики клиент попытается повторно подключиться по следующей команде. вместо этого он остается в закрытом состоянии. Также он возвращается с AbortError вместо new Error('Fail') .

Feature Request

Самый полезный комментарий

Быстрое обновление здесь.
Я добавил enable_offline_queue = false к своим параметрам createClient , и, похоже, он сделал то, что я хотел, а именно:
"Продолжайте попытки повторно подключиться к моему серверу Redis на основе функции retry_strategy , но сразу же выдают ошибки в ответ на попытки использовать клиент в то же время. Затем, если возможно повторное подключение, просто начните работать снова ".

К вашему сведению: при вызове get отображается следующая ошибка: AbortError: SET can't be processed. Stream not writeable.

Для моего использования в качестве прямого слоя между БД и моим приложением это должно быть нормально. Было бы неплохо узнать, возможно ли более «надежное» решение, где:

  • Клиент продолжает попытки переподключиться на основании retry_strategy
  • Звонки / потребители / пользователи Клиента не зависают при переподключении
  • Команды все еще могут быть поставлены в очередь (условно?) Для выполнения, если / когда соединение будет восстановлено ... в случае, если я увеличиваю вещи и т. Д.

Вероятно, здесь стоит рассмотреть множество сложных сценариев ...

Все 21 Комментарий

Из документации для retry_strategy видно, что node_redis будет пытаться повторно подключиться, только если функция возвращает число.

Если вы вернете число из этой функции, повторная попытка произойдет ровно через это время в миллисекундах.

В вашем случае вы возвращаете не номер, который не удовлетворяет требованиям для попытки повторного подключения.

Если вы вернете не число, повторная попытка не будет выполняться, и все автономные команды будут сброшены с ошибками.

поток, который я ищу: при отключении, повторите попытку подключения 3 раза с интервалом в 1 секунду. Если в третий раз он все еще отключится, он должен вернуть все невыполненные команды с ошибкой. Я не хочу, чтобы он перестал пытаться восстановить соединение.

С измененной версией примера, приведенного в README.md, вы сможете достичь того, что ищете. Однако, как только максимальное количество попыток, равное 3, исчерпано, клиент не будет автоматически пытаться повторно подключиться по следующей команде. Я считаю, что вам нужно будет снова вручную позвонить в createClient() .

const client = redis.createClient({
    url: 'redis://localhost:6379',
    retry_strategy: (options) => {
        if (options.times_connected >= 3) {
            // End reconnecting after a specific number of tries and flush all commands with a individual error
            return new Error('Retry attempts exhausted');
        }
        // reconnect after
        return 1000;
    }
});

Да, я не учел попытки, поскольку здесь это не имеет значения.

Итак, в основном вы имеете в виду:

let client = createClient();
function createClient () {
  return redis.createClient({
    url: 'redis://localhost:6379',
    retry_strategy: (options) => {
      client = createClient();
      return new Error('Retry attempts exhausted');
    }
  });
}

Что означает, что я больше не могу передавать свой клиент Redis в свой код?

Вы можете создать легкую оболочку для управления одноэлементным экземпляром клиента Redis.

// redisClient.js

let redis = require("redis");

let client = null;

const options = {
    url: 'redis://localhost:6379',
    retry_strategy: (options) => {
        client = null;
        return new Error("Redis client connection dropped.");
    }
};

module.exports = {
    getClient: () => {
        if (client == null) {
            client = redis.createClient(options);
        }
        return client;
    }
};

Использовать:

// usage

let client = require("redisClient.js");

client.getClient().get("some key");
client.getClient().set("some key", "some value");

Я полагаю, это очень похоже на вызов createClient() в стратегии повтора, но я не большой поклонник этого метода.

Если вы хотите уменьшить потребность в постоянном вызове getClient() , вы можете создать большую оболочку, которая обертывает все вызовы redis, такие как get() , set() и т. Д., И вызовите getClient() в каждом из этих методов. Однако этот метод getClient() является довольно распространенным методом ленивого, а не жадного управления подключениями.

конечно, но тогда почему эта библиотека вообще предоставляет какую-либо стратегию повтора, если вам все равно нужно обернуть ее дополнительной оболочкой повтора?

Я не автор, поэтому я не уверен в мотивах конкретной реализации retry_strategy. В моем собственном использовании node_redis я обнаружил, что текущая функциональность retry_strategy является полезной, но я согласен, что ей не хватает возможности автоматически повторять попытку несмотря ни на что. В моем использовании я постоянно возвращал число из retry_strategy и никогда не возвращал ошибку, так как я всегда хочу, чтобы была сделана попытка подключения. Однако я регистрирую ошибки в retry_strategy.

Как насчет этого? https://github.com/viglucci/node_redis/tree/feature-reconnect-after-flush#retry_strategy -example

var client = redis.createClient({
    retry_strategy: function (options) {
        if (options.error && 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.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');
        }
        // attempt reconnect after retry_delay, and flush any pending commands
        return {
            retry_delay: Math.min(options.attempt * 100, 3000),
            error: new Error('Your custom error.');
        }
    }
});

Концептуально имеет большой смысл. Другой способ реализовать это - разделить это на две стратегии:

var client = redis.createClient({
  // this strategy handles the pending redis commands when the connection goes down
  flush_strategy (options) {
    if (options.attempt >= 3) {
      // flush all pending commands with this error
      return new Error('Redis unavailable')
    }
    // let the connection come up again on its own
    return null;
  },
  // this strategy handles the reconnect of a failing redis
  retry_strategy (options) {
    if (options.total_retry_time > 1000 * 60 * 60) {
      // The connection is never going to get up again
      // kill the client with the error event
      return new Error('Retry time exhausted');
    }
    // attempt reconnect after this delay
    return Math.min(options.attempt * 100, 3000)
  }
});

Мне не очень нравится название flush_strategy но можно было бы придумать что-нибудь получше.

Ой интересно. То, что вы предлагаете, больше похоже на переименование текущего retry_strategy в connection_strategy и использование существующего retry_strategy качестве того, что вы назвали flush_strategy .

var client = redis.createClient({
    // this strategy handles reconnection
    connection_strategy (options) {
        if (options.total_retry_time > 1000 * 60 * 60) {
            // The connection is never going to get up again
            // kill the client with the error event
            return new Error('Retry time exhausted');
        }
        // attempt reconnect after this delay
        return Math.min(options.attempt * 100, 3000)
    },
    // this strategy handles the pending redis commands when the connection goes down
    retry_strategy (options) {
        if (options.attempt >= 3) {
            // flush all pending commands with this error
            return new Error('Redis unavailable');
        }
        // let the client attempt the commands once a connection is available
        return null;
    },
});

Реализовано: https://github.com/viglucci/node_redis/tree/feature-connection-strategy#retry_strategy -example

@Janpot, можете ли вы уточнить вариант использования, в котором это необходимо? Я попытался уменьшить количество опций, чтобы упростить использование, и retry_strategy уже является мощным вариантом.

@viglucci, спасибо, что оставили хорошие отзывы! Мне лично больше всего нравится ваше предложение вернуть объект, если это будет реализовано. На самом деле это не было бы критическим изменением, хотя я планирую вскоре открыть ветку v3. В настоящее время очень сложно реализовать множество новых функций, и удаление всех устаревших вещей очень поможет.

@BridgeAR Это почти то, что прямо сейчас, если соединение redis по какой-то причине прерывается, все мои команды просто зависают. Предположим, у меня есть резервный вариант для операции, которую выполняет redis, тогда этот откат также зависает. Я бы предпочел, чтобы redis возвращал ошибку после нескольких попыток, которые я могу поймать и использовать запасной вариант. Это не значит, что я не хочу, чтобы клиент перестал пытаться подключиться. Redis может вернуться через минуту.

Я просто не вижу смысла в нынешнем поведении. Вы возвращаете ошибку в обработчике повторов, и тогда ваш клиент просто умирает навсегда. Кому нужно такое поведение, если вы не создаете клиента для каждой команды?

Или, конечно, я могу не увидеть варианта использования этого 😄

@BridgeAR Спасибо. Что ж, если у вас есть «дорога к V3» с простыми задачами, такими как удаление поддержки параметров конфигурации / удаление предупреждений об устаревании и т. Д., Ide будет заинтересован в отправке запросов на вытягивание. Вероятно, я не смог бы справиться с большой работой, такой как добавление поддержки дополнительных команд redis, но простые вещи по уборке дома я, вероятно, мог бы сделать, если бы они были открыты для PR, как только будет доступна ветка функций V3.

@viglucci я пригласил в качестве соавтора. Я скоро составлю план

+1 за решение этой проблемы.

Я использую Express, и хотя мой retry_strategy по-прежнему возвращает целые числа, чтобы попытаться подключиться снова в будущем, команды (и веб-запросы) продолжают накапливать / создавать резервные копии вместо того, чтобы бросать что-то, чтобы они могли получить продолжают свою жизнь ... и, надеюсь, связь _в конце концов_ восстановится до того, как стратегия скажет сдаться или что-то в этом роде.

Можно ли этого добиться? @Janpot нашли ли вы что-нибудь доступное в настоящее время для решения этой ситуации?

Может быть, что-то с опциями enable_offline_queue и / или retry_unfulfilled_commands может сделать это?

Спасибо всем за ваш труд и помощь!

Быстрое обновление здесь.
Я добавил enable_offline_queue = false к своим параметрам createClient , и, похоже, он сделал то, что я хотел, а именно:
"Продолжайте попытки повторно подключиться к моему серверу Redis на основе функции retry_strategy , но сразу же выдают ошибки в ответ на попытки использовать клиент в то же время. Затем, если возможно повторное подключение, просто начните работать снова ".

К вашему сведению: при вызове get отображается следующая ошибка: AbortError: SET can't be processed. Stream not writeable.

Для моего использования в качестве прямого слоя между БД и моим приложением это должно быть нормально. Было бы неплохо узнать, возможно ли более «надежное» решение, где:

  • Клиент продолжает попытки переподключиться на основании retry_strategy
  • Звонки / потребители / пользователи Клиента не зависают при переподключении
  • Команды все еще могут быть поставлены в очередь (условно?) Для выполнения, если / когда соединение будет восстановлено ... в случае, если я увеличиваю вещи и т. Д.

Вероятно, здесь стоит рассмотреть множество сложных сценариев ...

@newhouse ,

Спасибо, что упомянули об этом. Нам нужно было такое же поведение для нашего приложения, и ваше решение работало для немедленного возврата вызывающей стороне ошибки, но позволяло нам повторять попытки подключения бесконечно.

Спасибо @newhouse , я не уверен, что когда-нибудь нашел бы это.

ИМО, это должно быть поведение по умолчанию или, по крайней мере, явно вызываться в документации около retry_strategy . Сообщите мне, если вы считаете, что последнее разумно, и я открою PR, чтобы добавить документы.

@bwhitty будет приветствоваться пиар от кого угодно 👍

Одна из проблем с использованием enable_offline_queue = false заключается в том, что команды, отправленные сразу после вызова createClient , но до фактического открытия соединения, также завершатся ошибкой.

Одна из проблем с использованием enable_offline_queue = false заключается в том, что команды, отправленные сразу после вызова createClient , но до фактического открытия соединения, также завершатся ошибкой.

@jkirkwood Похоже, вы захотите отложить попытку отправки ваших первых команд до тех пор, пока не будет ready (или аналогичное соответствующее событие)?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги