Node-redis: エラーをフラッシュした後、retry_strategyがリセットされない

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

  • バージョン:node_redis 2.6.5、redis 3.2.7
  • プラットフォーム:Mac OS10.12.3上のNode.js7.5.0
  • 説明

私は次のコードを使用しています

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 = falsecreateClientオプションに追加しましたが、希望どおりの結果が得られたようです。
retry_strategy関数に基づいてRedisサーバーへの再接続を試み続けますが、その間にクライアントを使用しようとするとすぐにエラーがスローされます。その後、再接続できる場合は、作業を再開してください。 "。

参考: get呼び出しで次のエラーが発生しました: AbortError: SET can't be processed. Stream not writeable.

DBとアプリの間のストレートレイヤーとして使用する場合は、これで問題ありません。 次の場合に、より「堅牢な」ソリューションが可能かどうかを知っておくとよいでしょう。

  • クライアントは引き続きretry_strategy基づいて再接続を試みます
  • クライアントの通話/消費者/ユーザーは、再接続中にハングしません
  • 接続が再確立された場合/接続が再確立された場合に実行するために、コマンドを(条件付きで?)キューに入れることができます...私が物事をインクリメントしている場合など。

おそらくここで考慮すべき多くの難しいシナリオ...

全てのコメント21件

再試行戦略のドキュメントによると、node_redisは、関数が数値を返した場合にのみ再接続を試みるようです。

この関数から数値を返すと、その時間の直後にミリ秒単位で再試行が行われます。

あなたの場合、あなたは非番号を返していますが、それは再接続を試みるための要件を満たしていません。

数値以外を返すと、それ以上の再試行は行われず、すべてのオフラインコマンドがエラーでフラッシュされます。

私が探しているフローは次のとおりです。切断時に、1秒間隔で3回接続を再試行します。 3回目にまだ切断されている場合は、未処理のすべてのコマンドをエラーとともに返す必要があります。 再接続の試行を停止したくありません。

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()を呼び出す必要性を減らしたい場合は、 get()set()などのすべてのredis呼び出しをラップする大きなラッパーを作成できます。これらの各メソッドで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.');
        }
    }
});

概念的には非常に理にかなっています。 これを実装する別の方法は、これを2つの戦略に分割することです。

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_strategyflush_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

@Janpotこれが必要なユースケースを詳しくretry_strategyはすでに強力なオプションです。

@viglucciここに答えて、良いフィードバックをしてくれてありがとう! これが実装される場合に備えて、オブジェクトを返すというあなたの提案が個人的に好きです。 間もなくv3ブランチを開く予定ですが、これは実際には重大な変更ではありません。 現在、多くの新機能を実装するのは非常に面倒であり、廃止されたものをすべて削除すると大いに役立ちます。

@BridgeAR今のところ、redis接続が何らかの理由で切断された場合、すべてのコマンドがハングします。 redisが実行している操作のフォールバックがあるとすると、そのフォールバックもハングします。 フォールバックをキャッチして使用できるように、数回の再試行後にredisがエラーを返すようにしたいです。 これは、クライアントが接続の試行を停止したくないという意味ではありません。 Redisはすぐに戻ってくるかもしれません。

現在の振る舞いの要点がわかりません。 再試行ハンドラーでエラーを返すと、クライアントは永久に停止します。 すべてのコマンドに対してクライアントを作成しない限り、誰がそのような動作を必要としますか?

または、もちろんそのユースケースが表示されない場合があります😄

@BridgeARありがとう。 構成パラメータのサポートの削除や非推奨の警告の削除などの簡単なタスクを含む「V3への道」ロードマップがある場合は、プルリクエストの送信に関心があります。 おそらく、追加のredisコマンドのサポートを追加するような大規模な作業に取り組むことはできませんが、V3機能ブランチが利用可能になったらPRに対応していれば、簡単なハウスクリーニングを行うことができます。

@viglucciコラボレーターとして招待しました。 近いうちに計画を立てるつもりです

この問題の解決策については+1。

Expressを使用していますが、 retry_strategyが整数を返し、今後再接続を試みますが、コマンド(およびWebリクエスト)は、何かをスローするのではなく、パイル/バックアップを続けます。彼らの生活を続けてください...そしてうまくいけば、戦略が諦めるか何かを言う前に、接続が_最終的に_再確立されます。

これは達成できますか? @Janpotこの状況を解決するために現在利用できるものを見つけましたか?

たぶん、 enable_offline_queueおよび/またはretry_unfulfilled_commandsオプションを備えた何かがこれを達成できますか?

あなたのハードワークと助けてくれてありがとう!

ここでクイックアップデート。
enable_offline_queue = falsecreateClientオプションに追加しましたが、希望どおりの結果が得られたようです。
retry_strategy関数に基づいてRedisサーバーへの再接続を試み続けますが、その間にクライアントを使用しようとするとすぐにエラーがスローされます。その後、再接続できる場合は、作業を再開してください。 "。

参考: get呼び出しで次のエラーが発生しました: AbortError: SET can't be processed. Stream not writeable.

DBとアプリの間のストレートレイヤーとして使用する場合は、これで問題ありません。 次の場合に、より「堅牢な」ソリューションが可能かどうかを知っておくとよいでしょう。

  • クライアントは引き続きretry_strategy基づいて再接続を試みます
  • クライアントの通話/消費者/ユーザーは、再接続中にハングしません
  • 接続が再確立された場合/接続が再確立された場合に実行するために、コマンドを(条件付きで?)キューに入れることができます...私が物事をインクリメントしている場合など。

おそらくここで考慮すべき多くの難しいシナリオ...

@newhouse

これに言及してくれてありがとう。 アプリケーションにも同じ動作が必要であり、ソリューションは呼び出し元にすぐにエラーを返すように機能しましたが、無期限に接続を再試行できるようにしました。

@newhouseに感謝します。これを見つけたかどうかはわかりません。

IMOこれはデフォルトの動作であるか、少なくともretry_strategy周辺のドキュメントで明示的に呼び出されている必要があります。 後者が妥当だと思われる場合はお知らせください。PRを開いてドキュメントを追加します。

@bwhitty誰からのドキュメントPRも大歓迎です👍

enable_offline_queue = falseを使用する際の1つの落とし穴は、 createClient呼び出した直後に、接続が実際に開かれる前に送信されるコマンドも失敗することです。

enable_offline_queue = falseを使用する際の1つの落とし穴は、 createClient呼び出した直後に、接続が実際に開かれる前に送信されるコマンドも失敗することです。

@jkirkwood readyイベント(または同様の適切なイベント)が発行されるまで、最初のコマンドの送信を遅らせたいと思われますか?

このページは役に立ちましたか?
0 / 5 - 0 評価