Node-redis: retry_strategy ne se réinitialise pas après avoir vidé l'erreur

Créé le 18 févr. 2017  ·  21Commentaires  ·  Source: NodeRedis/node-redis

  • Version : node_redis 2.6.5, redis 3.2.7
  • Plateforme : Node.js 7.5.0 sur Mac OS 10.12.3
  • Descriptif :

j'utilise le code suivant

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

s'exécutant sur un redis local ( docker run --rm -p 6379:6379 redis )

Après avoir vu la première sortie, je tue le redis pour simuler la déconnexion. Quand j'ai vu l'erreur arriver, je redémarre le redis. La connexion ne revient plus. Je m'attendrais à ce qu'après avoir vidé l'erreur vers les gestionnaires hors ligne, le client essaie de se reconnecter à la prochaine commande. au lieu de cela, il reste à l'état fermé. Il revient également avec AbortError au lieu de new Error('Fail') .

Feature Request

Commentaire le plus utile

Mise à jour rapide ici.
J'ai ajouté le enable_offline_queue = false à mes options createClient , et il semble avoir fait ce que je voulais, c'est-à-dire :
"Continuez à essayer de vous reconnecter à mon serveur Redis en fonction de la fonction retry_strategy , mais lancez immédiatement des erreurs en réponse aux tentatives d'utilisation du client entre-temps. Ensuite, si vous pouvez vous reconnecter, recommencez simplement à travailler ".

Pour info : affiche l'erreur suivante sur un appel get : AbortError: SET can't be processed. Stream not writeable.

Pour mon utilisation en tant que couche droite entre la base de données et mon application, cela devrait convenir. Ce serait bien de savoir s'il existe une solution plus "robuste" possible où :

  • Le client continue d'essayer de se reconnecter sur la base de retry_strategy
  • Les appels/consommateurs/utilisateurs du Client ne se bloquent pas lors de la reconnexion
  • Les commandes peuvent toujours être mises en file d'attente (conditionnellement ?) pour être exécutées si/quand une connexion est rétablie... au cas où j'incrémenterais des choses, etc.

Probablement beaucoup de scénarios difficiles à considérer ici...

Tous les 21 commentaires

Via la documentation de retry_strategy, il apparaît que node_redis ne tentera de se reconnecter que si la fonction renvoie un nombre.

Si vous retournez un nombre à partir de cette fonction, la nouvelle tentative aura lieu exactement après ce temps en millisecondes.

Dans votre cas, vous retournez un numéro non, qui ne satisfait pas aux exigences pour tenter de se reconnecter.

Si vous retournez un non-numéro, aucune nouvelle tentative n'aura lieu et toutes les commandes hors ligne sont vidées avec des erreurs.

le flux que je recherche est le suivant : lors de la déconnexion, réessayez de vous connecter 3 fois avec un intervalle de 1 seconde. Si, à la troisième fois, il est toujours déconnecté, il devrait renvoyer toutes les commandes en attente avec une erreur. Je ne veux pas qu'il arrête d'essayer de se reconnecter.

Avec une version modifiée de l'exemple donné dans le README.md, vous devriez pouvoir réaliser ce que vous recherchez. Cependant, une fois le nombre maximum de tentatives de 3 épuisé, le client ne tentera pas automatiquement de se reconnecter à la prochaine commande. Je pense que vous devrez à nouveau appeler manuellement 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;
    }
});

Oui, j'ai laissé de côté les tentatives car cela ne fait pas de différence ici.

Donc en gros tu veux dire :

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

Ce qui implique que je ne peux plus faire circuler mon client redis dans mon code ?

Vous pouvez créer un wrapper léger pour gérer une instance singleton du client 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;
    }
};

Utiliser:

// usage

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

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

Je suppose que cela est très similaire à l'appel de createClient() dans la stratégie de nouvelle tentative, mais je ne suis pas un grand fan de cette méthode.

Si vous vouliez réduire le besoin d'appeler getClient() tout le temps, vous pouvez créer un grand wrapper qui enveloppe tous les appels redis comme get() , set() , etc, et appelez getClient() dans chacune de ces méthodes. Cependant, cette méthode de getClient() est une méthode assez courante pour gérer les connexions paresseusement plutôt qu'avec empressement.

bien sûr, mais alors pourquoi cette bibliothèque fournit-elle une stratégie de nouvelle tentative si vous devez quand même l'envelopper dans un wrapper de nouvelle tentative supplémentaire?

Je ne suis pas l'auteur, donc je ne suis pas sûr des motivations derrière la mise en œuvre spécifique de la retry_strategy. Dans mes propres utilisations de node_redis, j'ai trouvé que la fonctionnalité actuelle de retry_strategy était utile, mais je reconnais qu'il manque la possibilité de réessayer automatiquement quoi qu'il arrive. Dans mes utilisations, j'ai toujours renvoyé un nombre de retry_strategy et je ne renvoie jamais d'erreur, car je veux toujours que la tentative de connexion soit effectuée. J'enregistre cependant les erreurs à partir de retry_strategy.

Que diriez-vous quelque chose comme ça? https://github.com/viglucci/node_redis/tree/feature-reconnect-after-flush#retry_strategy -exemple

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

Cela a beaucoup de sens conceptuellement. Une autre façon de mettre en œuvre cela pourrait être de diviser cela en deux stratégies :

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

Je n'aime pas vraiment le nom flush_strategy mais on pourrait trouver quelque chose de mieux.

Oh intéressant. Ce que vous suggérez ici ressemble plus à renommer le retry_strategy actuel en connection_strategy et à utiliser le retry_strategy existant comme ce que vous avez appelé le 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;
    },
});

Mis en œuvre : https://github.com/viglucci/node_redis/tree/feature-connection-strategy#retry_strategy -example

@Janpot pouvez-vous élaborer le cas d'utilisation où cela est nécessaire ? J'ai essayé de réduire le nombre d'options pour le rendre plus simple à utiliser et le retry_strategy est déjà une option puissante.

@viglucci merci d'avoir répondu ici et d'avoir donné de bons commentaires ! Personnellement, j'aime le plus votre suggestion de retourner un objet au cas où cela serait mis en œuvre. Ce ne serait pas vraiment un changement décisif, même si je prévois d'ouvrir une branche v3 bientôt. Actuellement, il est trop compliqué d'implémenter de nombreuses nouvelles fonctionnalités et la suppression de toutes les choses obsolètes aidera beaucoup.

@BridgeAR C'est à peu près qu'en ce moment, si la connexion Redis se rompt pour une raison quelconque, toutes mes commandes se bloquent. Supposons que j'ai une solution de secours pour l'opération que redis effectue, cette solution de secours se bloque également. Je préfère que redis renvoie une erreur après quelques tentatives, que je peux attraper et utiliser le repli. Cela ne veut pas dire que je ne veux pas que le client arrête d'essayer de se connecter. Redis pourrait revenir dans une minute.

Je ne vois tout simplement pas l'intérêt du comportement actuel. Vous retournez une erreur dans le gestionnaire de nouvelles tentatives, puis votre client meurt pour toujours. Qui a besoin de ce genre de comportement à moins que vous ne créiez un client pour chaque commande ?

Ou je pourrais ne pas voir le cas d'utilisation pour cela bien sûr 😄

@BridgeAR Merci. Eh bien, si vous avez une feuille de route "Road to V3" avec des tâches simples telles que la suppression de la prise en charge des paramètres de configuration / la suppression des avertissements de dépréciation, etc., je serais intéressé par la soumission de demandes d'extraction. Je ne serais probablement pas en mesure de faire face à de gros travaux comme l'ajout de la prise en charge de commandes redis supplémentaires, mais des tâches simples de nettoyage de la maison que je pourrais probablement faire si elles étaient ouvertes pour les relations publiques une fois qu'une branche de fonctionnalité V3 est disponible.

@viglucci J'ai invité en tant que collaborateur. Je vais faire un plan bientôt

+1 pour une solution à ce problème.

J'utilise Express, et tandis que mon retry_strategy renvoie toujours des entiers pour essayer de se reconnecter à l'avenir, les commandes (et les requêtes Web) continuent à s'empiler/sauvegarder au lieu de lancer quelque chose pour qu'elles puissent obtenir sur leur vie... et j'espère qu'une connexion est _éventuellement_ rétablie avant que la stratégie ne dise d'abandonner ou quoi que ce soit d'autre.

Cela peut-il être accompli? @Janpot avez-vous trouvé quelque chose actuellement disponible pour résoudre cette situation ?

Peut-être que quelque chose avec les options enable_offline_queue et/ou retry_unfulfilled_commands peut accomplir cela ?

Merci à tous pour votre travail acharné et votre aide!

Mise à jour rapide ici.
J'ai ajouté le enable_offline_queue = false à mes options createClient , et il semble avoir fait ce que je voulais, c'est-à-dire :
"Continuez à essayer de vous reconnecter à mon serveur Redis en fonction de la fonction retry_strategy , mais lancez immédiatement des erreurs en réponse aux tentatives d'utilisation du client entre-temps. Ensuite, si vous pouvez vous reconnecter, recommencez simplement à travailler ".

Pour info : affiche l'erreur suivante sur un appel get : AbortError: SET can't be processed. Stream not writeable.

Pour mon utilisation en tant que couche droite entre la base de données et mon application, cela devrait convenir. Ce serait bien de savoir s'il existe une solution plus "robuste" possible où :

  • Le client continue d'essayer de se reconnecter sur la base de retry_strategy
  • Les appels/consommateurs/utilisateurs du Client ne se bloquent pas lors de la reconnexion
  • Les commandes peuvent toujours être mises en file d'attente (conditionnellement ?) pour être exécutées si/quand une connexion est rétablie... au cas où j'incrémenterais des choses, etc.

Probablement beaucoup de scénarios difficiles à considérer ici...

@nouvelle maison ,

Merci d'avoir mentionné cela. Nous voulions le même comportement pour notre application et votre solution a fonctionné pour renvoyer immédiatement une erreur à l'appelant, mais nous permet de réessayer de nous connecter indéfiniment.

Merci @newhouse , je ne suis pas sûr que j'aurais jamais trouvé ça.

IMO, cela devrait être le comportement par défaut, ou au moins explicitement appelé dans la documentation autour de retry_strategy . Faites-moi savoir si vous pensez que ce dernier est raisonnable, et j'ouvrirai un PR pour ajouter les documents.

@bWhitty un docs PR de n'importe qui serait le bienvenu 👍

Un problème avec l'utilisation de enable_offline_queue = false est que les commandes envoyées juste après l'appel de createClient , mais avant que la connexion ne soit réellement ouverte, échoueront également.

Un problème avec l'utilisation de enable_offline_queue = false est que les commandes envoyées juste après l'appel de createClient , mais avant que la connexion ne soit réellement ouverte, échoueront également.

@jkirkwood Il semble que vous voudriez retarder l'envoi de vos premières commandes jusqu'à ce que l'événement ready (ou un événement approprié similaire) soit émis ?

Cette page vous a été utile?
0 / 5 - 0 notes