Node-redis: retry_strategy wird nach dem Löschen des Fehlers nicht zurückgesetzt

Erstellt am 18. Feb. 2017  ·  21Kommentare  ·  Quelle: NodeRedis/node-redis

  • Version : node_redis 2.6.5, redis 3.2.7
  • Plattform : Node.js 7.5.0 unter Mac OS 10.12.3
  • Beschreibung :

Ich verwende folgenden Code

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

gegen eine lokale Redis ( docker run --rm -p 6379:6379 redis )

Nachdem ich die erste Ausgabe sehe, beende ich die Redis, um die Trennung zu simulieren. Wenn ich gesehen habe, dass der Fehler auftritt, starte ich die Redis neu. Die Verbindung kommt nicht wieder. Ich würde erwarten, dass der Client nach dem Leeren des Fehlers an die Offline-Handler versucht, die Verbindung beim nächsten Befehl erneut herzustellen. stattdessen bleibt es im geschlossenen Zustand. Außerdem wird mit AbortError statt new Error('Fail') .

Feature Request

Hilfreichster Kommentar

Schnelles Update hier.
Ich habe die enable_offline_queue = false zu meinen createClient Optionen hinzugefügt, und es scheint das getan zu haben, was ich wollte, nämlich:
"Versuchen Sie weiterhin, eine Verbindung zu meinem Redis-Server basierend auf der Funktion retry_strategy herzustellen, geben Sie jedoch sofort Fehler aus, wenn Sie versuchen, den Client in der Zwischenzeit zu verwenden. Wenn Sie dann die Verbindung wiederherstellen können, beginnen Sie einfach wieder mit der Arbeit ".

Zu Ihrer Information: Zeigt den folgenden Fehler bei einem get Aufruf: AbortError: SET can't be processed. Stream not writeable.

Für meine Verwendung als gerade Schicht zwischen der DB und meiner App sollte dies in Ordnung sein. Wäre schön zu wissen, ob es eine "robustere" Lösung gibt, bei der:

  • Der Client versucht weiterhin, die Verbindung basierend auf retry_strategy
  • Anrufe/Verbraucher/Benutzer des Clients hängen nicht, während er wieder verbunden wird
  • Befehle können immer noch (bedingt?) in die Warteschlange gestellt werden, um ausgeführt zu werden, wenn / wenn eine Verbindung wiederhergestellt wird ... falls ich Dinge inkrementiere usw.

Wahrscheinlich sind hier viele schwierige Szenarien zu berücksichtigen...

Alle 21 Kommentare

Aus der Dokumentation zu retry_strategy geht hervor, dass node_redis nur dann versucht, eine Verbindung wiederherzustellen, wenn die Funktion eine Zahl zurückgibt.

Wenn Sie von dieser Funktion eine Zahl zurückgeben, erfolgt der Wiederholungsversuch genau nach dieser Zeit in Millisekunden.

In Ihrem Fall geben Sie eine Nicht-Nummer zurück, die die Voraussetzungen für einen erneuten Verbindungsversuch nicht erfüllt.

Wenn Sie eine Nicht-Nummer zurückgeben, wird kein weiterer Versuch unternommen und alle Offlinebefehle werden mit Fehlern geleert.

Der Ablauf, den ich suche, ist: Beim Trennen versuchen Sie, die Verbindung dreimal mit 1-Sekunden-Intervall erneut herzustellen. Wenn es beim dritten Mal immer noch getrennt wird, sollte es alle ausstehenden Befehle mit einem Fehler zurückgeben. Ich möchte nicht, dass es aufhört, sich erneut zu verbinden.

Mit einer modifizierten Version des Beispiels in der README.md sollten Sie in der Lage sein, das Gesuchte zu erreichen. Sobald jedoch die maximale Anzahl von 3 Versuchen erschöpft ist, versucht der Client beim nächsten Befehl nicht automatisch, die Verbindung wiederherzustellen. Ich glaube, Sie müssten createClient() erneut manuell aufrufen.

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

Ja, ich habe die Versuche weggelassen, da das hier keinen Unterschied macht.

Du meinst also im Grunde:

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

Was bedeutet, dass ich meinen Redis-Client nicht mehr in meinem Code weitergeben kann?

Sie können einen Light-Wrapper erstellen, um eine Singleton-Instanz des Redis-Clients zu verwalten.

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

Benutzen:

// usage

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

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

Ich nehme an, dies ist dem Aufrufen von createClient() in der Wiederholungsstrategie sehr ähnlich, aber ich bin kein großer Fan dieser Methode.

Wenn Sie die Notwendigkeit reduzieren möchten, getClient() ständig aufzurufen, können Sie einen großen Wrapper erstellen, der alle Redis-Aufrufe wie get() , set() usw. umschließt und rufen Sie getClient() in jeder dieser Methoden auf. Diese Methode von getClient() ist jedoch eine ziemlich gängige Methode, um Verbindungen eher träge als eifrig zu verwalten.

sicher, aber warum bietet diese Bibliothek dann überhaupt eine Wiederholungsstrategie, wenn Sie sie trotzdem in einen zusätzlichen Wiederholungs-Wrapper einschließen müssen?

Ich bin nicht der Autor, daher bin ich mir der Beweggründe für die spezifische Implementierung der retry_strategy nicht sicher. Bei meiner eigenen Verwendung von node_redis habe ich festgestellt, dass die derzeitige Funktionalität von retry_strategy nützlich ist, aber ich stimme zu, dass es an der Fähigkeit fehlt, es automatisch zu wiederholen, egal was passiert. In meinen Anwendungen habe ich durchweg eine Zahl von der retry_strategy zurückgegeben und nie einen Fehler zurückgegeben, da ich immer möchte, dass der Verbindungsversuch durchgeführt wird. Ich protokolliere jedoch die Fehler innerhalb der retry_strategy.

Wie wäre es mit so etwas? https://github.com/viglucci/node_redis/tree/feature-reconnect-after-flush#retry_strategy -Beispiel

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

Macht konzeptionell sehr viel Sinn. Eine andere Möglichkeit, dies zu implementieren, könnte darin bestehen, dies in zwei Strategien aufzuteilen:

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

Ich mag den Namen flush_strategy nicht wirklich, aber man könnte sich etwas besseres einfallen lassen.

Oh, interessant. Was Sie hier vorschlagen, fühlt sich eher so an, als würden Sie das aktuelle retry_strategy in connection_strategy umbenennen und das vorhandene retry_strategy als das, was Sie als 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;
    },
});

Implementiert: https://github.com/viglucci/node_redis/tree/feature-connection-strategy#retry_strategy -example

@Janpot können Sie den Anwendungsfall erläutern, in dem dies erforderlich ist? Ich habe versucht, die Anzahl der Optionen zu verringern, um die Verwendung zu vereinfachen, und retry_strategy ist bereits eine leistungsstarke Option.

@viglucci danke für die Antwort hier und das gute Feedback! Am besten gefällt mir persönlich Ihr Vorschlag , ein Objekt zurückzugeben, falls dies umgesetzt wird. Dies wäre nicht wirklich eine bahnbrechende Änderung, obwohl ich plane, bald einen v3-Zweig zu eröffnen. Derzeit ist es einfach zu mühsam, viele neue Funktionen zu implementieren, und das Entfernen aller veralteten Dinge wird viel helfen.

@BridgeAR Es ist so ziemlich so, dass im bleiben , wenn die Redis-Verbindung aus irgendeinem Grund

Ich sehe einfach nicht den Sinn des aktuellen Verhaltens. Sie geben einen Fehler im Retry-Handler zurück und dann stirbt Ihr Client für immer. Wer braucht so ein Verhalten, wenn Sie nicht für jeden Befehl einen Client erstellen?

Oder ich sehe den Anwendungsfall dafür natürlich nicht 😄

@ BridgeAR Danke. Nun, wenn Sie eine "Road to V3"-Roadmap mit einfachen Aufgaben wie Entfernen der Unterstützung für Konfigurationsparameter/Entfernen von Warnungen vor Veraltung usw. haben, sind Sie daran interessiert, Pull-Requests einzureichen. Ich wäre wahrscheinlich nicht in der Lage, große Aufgaben wie das Hinzufügen von Unterstützung für zusätzliche Redis-Befehle zu bewältigen, aber einfache Hausreinigungsaufgaben könnte ich wahrscheinlich tun, wenn sie für PRs geöffnet wären, sobald ein V3-Feature-Zweig verfügbar ist.

@viglucci Ich habe als Mitarbeiter eingeladen. Ich werde demnächst einen Plan machen

+1 für eine Lösung dieses Problems.

Ich verwende Express, und während mein retry_strategy immer noch Ganzzahlen zurückgibt, um in Zukunft erneut zu versuchen, eine Verbindung herzustellen, werden die Befehle (und Webanforderungen) weiterhin gestapelt / gesichert, anstatt etwas zu werfen, damit sie erhalten können weiter mit ihrem Leben ... und hoffentlich wird eine Verbindung _eventuell_ wieder hergestellt, bevor die Strategie sagt, aufzugeben oder was auch immer.

Kann dies bewerkstelligt werden? @Janpot haben Sie derzeit etwas gefunden, um diese Situation zu lösen?

Vielleicht kann etwas mit den Optionen enable_offline_queue und/oder retry_unfulfilled_commands dies erreichen?

Danke an alle für eure Mühe und Hilfe!

Schnelles Update hier.
Ich habe die enable_offline_queue = false zu meinen createClient Optionen hinzugefügt, und es scheint das getan zu haben, was ich wollte, nämlich:
"Versuchen Sie weiterhin, eine Verbindung zu meinem Redis-Server basierend auf der Funktion retry_strategy herzustellen, geben Sie jedoch sofort Fehler aus, wenn Sie versuchen, den Client in der Zwischenzeit zu verwenden. Wenn Sie dann die Verbindung wiederherstellen können, beginnen Sie einfach wieder mit der Arbeit ".

Zu Ihrer Information: Zeigt den folgenden Fehler bei einem get Aufruf: AbortError: SET can't be processed. Stream not writeable.

Für meine Verwendung als gerade Schicht zwischen der DB und meiner App sollte dies in Ordnung sein. Wäre schön zu wissen, ob es eine "robustere" Lösung gibt, bei der:

  • Der Client versucht weiterhin, die Verbindung basierend auf retry_strategy
  • Anrufe/Verbraucher/Benutzer des Clients hängen nicht, während er wieder verbunden wird
  • Befehle können immer noch (bedingt?) in die Warteschlange gestellt werden, um ausgeführt zu werden, wenn / wenn eine Verbindung wiederhergestellt wird ... falls ich Dinge inkrementiere usw.

Wahrscheinlich sind hier viele schwierige Szenarien zu berücksichtigen...

@neuhaus ,

Danke für die Erwähnung. Wir wollten das gleiche Verhalten für unsere Anwendung und Ihre Lösung funktionierte, um sofort einen Fehler an den Anrufer zurückzugeben, aber erlauben Sie uns, auf unbestimmte Zeit erneut zu versuchen, eine Verbindung herzustellen.

Danke @newhouse , ich bin mir nicht sicher, ob ich das jemals gefunden hätte.

IMO sollte dies das Standardverhalten sein oder zumindest in den Dokumenten um retry_strategy explizit genannt werden. Lassen Sie es mich wissen, wenn Sie letzteres für angemessen halten, und ich werde eine PR öffnen, um die Dokumente hinzuzufügen.

@bwhitty eine Docs-PR von jedem wäre sehr willkommen

Ein Fallstrick bei der Verwendung von enable_offline_queue = false ist, dass Befehle, die direkt nach dem Aufrufen von createClient gesendet werden, aber auch fehlschlagen, bevor die Verbindung tatsächlich geöffnet wird.

Ein Fallstrick bei der Verwendung von enable_offline_queue = false ist, dass Befehle, die direkt nach dem Aufrufen von createClient gesendet werden, aber auch fehlschlagen, bevor die Verbindung tatsächlich geöffnet wird.

@jkirkwood Klingt so, als würden Sie versuchen, Ihre ersten Befehle zu senden, bis das ready Ereignis (oder ein ähnliches geeignetes Ereignis) ausgegeben wird?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen