Node-redis: retry_strategy tidak mengatur ulang setelah menghapus kesalahan

Dibuat pada 18 Feb 2017  ·  21Komentar  ·  Sumber: NodeRedis/node-redis

  • Versi : node_redis 2.6.5, redis 3.2.7
  • Platform : Node.js 7.5.0 di Mac OS 10.12.3
  • Deskripsi :

Saya menggunakan kode berikut

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

berjalan melawan redis lokal ( docker run --rm -p 6379:6379 redis )

Setelah saya melihat output pertama, saya mematikan redis untuk mensimulasikan disconnect. Ketika saya melihat kesalahan masuk, saya me-restart redis. Sambungan tidak muncul lagi. Saya berharap bahwa setelah mem-flush kesalahan ke penangan offline, klien akan mencoba menyambung kembali pada perintah berikutnya. sebaliknya itu tetap dalam keadaan tertutup. Juga kembali dengan AbortError alih-alih new Error('Fail') .

Feature Request

Komentar yang paling membantu

Pembaruan cepat di sini.
Saya menambahkan enable_offline_queue = false ke opsi createClient , dan tampaknya telah melakukan apa yang saya inginkan, yaitu:
"Lanjutkan untuk mencoba menyambung kembali ke server Redis saya berdasarkan fungsi retry_strategy , tetapi segera lakukan kesalahan sebagai tanggapan atas upaya untuk menggunakan klien sementara itu. Kemudian, jika dapat menyambungkan kembali, mulailah bekerja lagi ".

FYI: Menampilkan kesalahan berikut pada panggilan get : AbortError: SET can't be processed. Stream not writeable.

Untuk penggunaan saya sebagai lapisan lurus antara DB dan aplikasi saya, ini seharusnya baik-baik saja. Akan lebih baik untuk mengetahui apakah ada solusi yang lebih "kuat" di mana:

  • Klien terus mencoba menyambung kembali berdasarkan retry_strategy
  • Panggilan/konsumen/pengguna Klien tidak hang saat terhubung kembali
  • Perintah masih dapat diantrekan (dengan syarat?) untuk dieksekusi jika/ketika koneksi dibuat kembali ... jika saya menambah sesuatu, dll.

Mungkin banyak skenario sulit untuk dipertimbangkan di sini...

Semua 21 komentar

Melalui dokumentasi untuk retry_strategy, tampaknya node_redis hanya akan mencoba menyambung kembali jika fungsi mengembalikan angka.

Jika Anda mengembalikan angka dari fungsi ini, percobaan ulang akan terjadi tepat setelah waktu tersebut dalam milidetik.

Dalam kasus Anda, Anda mengembalikan nomor non, yang tidak memenuhi persyaratan untuk mencoba menyambung kembali.

Jika Anda mengembalikan non-angka, tidak ada percobaan ulang lebih lanjut yang akan terjadi dan semua perintah offline dipenuhi dengan kesalahan.

aliran yang saya cari adalah: saat putuskan, coba sambungkan lagi 3 kali dengan interval 1 detik. Jika pada ketiga kalinya masih terputus, itu harus mengembalikan semua perintah yang luar biasa dengan kesalahan. Saya tidak ingin itu berhenti mencoba untuk menyambung kembali.

Dengan versi modifikasi dari contoh yang diberikan di README.md, Anda seharusnya dapat mencapai apa yang Anda cari. Namun, setelah upaya maksimal 3 habis, klien tidak akan secara otomatis mencoba menyambung kembali pada perintah berikutnya. Saya yakin Anda perlu memanggil createClient() secara manual lagi.

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

Ya saya meninggalkan upaya karena itu tidak membuat perbedaan di sini.

Jadi pada dasarnya maksud Anda:

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

Yang berarti saya tidak bisa lagi menyebarkan klien redis saya dalam kode saya?

Anda dapat membuat pembungkus ringan untuk mengelola instance tunggal dari klien 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;
    }
};

Menggunakan:

// usage

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

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

Saya kira ini sangat mirip dengan memanggil createClient() dalam strategi coba lagi, tetapi saya bukan penggemar berat metode itu.

Jika Anda ingin mengurangi kebutuhan untuk memanggil getClient() sepanjang waktu, Anda dapat membuat pembungkus besar yang membungkus semua panggilan redis seperti get() , set() , dll, dan panggil getClient() di masing-masing metode tersebut. Namun, metode getClient() ini adalah metode yang cukup umum untuk mengelola koneksi dengan malas daripada bersemangat.

tentu, tetapi mengapa perpustakaan ini menyediakan strategi coba lagi sama sekali jika Anda harus membungkusnya dengan pembungkus coba lagi?

Saya bukan penulisnya, jadi saya tidak yakin dengan motivasi di balik implementasi spesifik dari retry_strategy. Dalam penggunaan node_redis saya sendiri, saya telah menemukan fungsionalitas retry_strategy saat ini berguna, tetapi saya setuju itu kurang dalam kemampuan untuk mencoba lagi secara otomatis apa pun yang terjadi. Dalam penggunaan saya, saya secara konsisten mengembalikan nomor dari retry_strategy dan tidak pernah mengembalikan kesalahan, karena saya selalu ingin upaya koneksi dilakukan. Namun saya mencatat kesalahan dari dalam retry_strategy.

Bagaimana dengan sesuatu yang seperti ini? https://github.com/viglucci/node_redis/tree/feature-reconnect-after-flush#retry_strategy -contoh

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

Sangat masuk akal secara konseptual. Cara lain untuk menerapkan ini bisa dengan membagi ini menjadi dua strategi:

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

Saya tidak terlalu suka nama flush_strategy tetapi orang bisa menemukan sesuatu yang lebih baik.

Oh Menarik. Apa yang Anda sarankan di sini terasa lebih seperti mengganti nama retry_strategy menjadi connection_strategy dan menggunakan retry_strategy sebagai apa yang Anda sebut sebagai 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;
    },
});

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

@Janpot dapatkah Anda menguraikan kasus penggunaan di mana ini diperlukan? Saya mencoba mengurangi jumlah opsi untuk membuatnya lebih mudah digunakan dan retry_strategy sudah menjadi opsi yang kuat.

@viglucci terima kasih telah menjawab di sini dan memberikan umpan balik yang baik! Saya pribadi menyukai saran Anda untuk mengembalikan objek jika ini akan diterapkan. Ini tidak akan benar-benar menjadi perubahan besar, meskipun saya berencana untuk segera membuka cabang v3. Saat ini sangat merepotkan untuk menerapkan banyak fitur baru dan menghapus semua hal yang sudah usang akan sangat membantu.

@BridgeAR Cukup banyak saat ini, jika koneksi redis terputus karena suatu alasan, semua perintah saya hanya hang. Misalkan saya memiliki fallback untuk operasi yang dilakukan redis maka fallback itu juga hang. Saya lebih suka redis mengembalikan kesalahan setelah beberapa percobaan ulang, yang dapat saya tangkap dan gunakan fallback. Itu tidak berarti saya tidak ingin klien berhenti mencoba terhubung. Redis mungkin akan muncul kembali dalam satu menit.

Saya hanya tidak mengerti maksud dari perilaku saat ini. Anda mengembalikan kesalahan di penangan coba lagi dan kemudian klien Anda mati selamanya. Siapa yang membutuhkan perilaku semacam itu kecuali Anda membuat klien untuk setiap perintah?

Atau saya mungkin tidak melihat kasus penggunaan untuk itu tentu saja

@BridgeAR Terima kasih. Nah jika Anda memiliki peta jalan "jalan ke V3" dengan tugas-tugas sederhana seperti menghapus dukungan untuk params konfigurasi / menghapus peringatan penghentian, dll, ide tertarik untuk mengirimkan permintaan tarik. Mungkin tidak akan dapat menangani pekerjaan besar seperti menambahkan dukungan untuk perintah redis tambahan, tetapi hal-hal pembersihan rumah sederhana yang mungkin bisa saya lakukan jika mereka terbuka untuk PR setelah cabang fitur V3 tersedia.

@viglucci saya undang sebagai kolaborator. Saya akan membuat rencana dalam waktu dekat

+1 untuk solusi masalah ini.

Saya menggunakan Express, dan sementara retry_strategy masih mengembalikan bilangan bulat untuk mencoba dan terhubung lagi di masa mendatang, perintah (dan permintaan web) terus menumpuk/mencadangkan alih-alih melempar sesuatu sehingga mereka bisa mendapatkan melanjutkan hidup mereka... dan mudah-mudahan koneksi _akhirnya_ terjalin kembali sebelum strategi mengatakan untuk menyerah atau apa pun.

Bisakah ini tercapai? @Janpot apakah Anda menemukan sesuatu yang saat ini tersedia untuk menyelesaikan situasi ini?

Mungkin sesuatu dengan opsi enable_offline_queue dan/atau retry_unfulfilled_commands dapat menyelesaikan ini?

Terima kasih kepada semua atas kerja keras dan bantuan Anda!

Pembaruan cepat di sini.
Saya menambahkan enable_offline_queue = false ke opsi createClient , dan tampaknya telah melakukan apa yang saya inginkan, yaitu:
"Lanjutkan untuk mencoba menyambung kembali ke server Redis saya berdasarkan fungsi retry_strategy , tetapi segera lakukan kesalahan sebagai tanggapan atas upaya untuk menggunakan klien sementara itu. Kemudian, jika dapat menyambungkan kembali, mulailah bekerja lagi ".

FYI: Menampilkan kesalahan berikut pada panggilan get : AbortError: SET can't be processed. Stream not writeable.

Untuk penggunaan saya sebagai lapisan lurus antara DB dan aplikasi saya, ini seharusnya baik-baik saja. Akan lebih baik untuk mengetahui apakah ada solusi yang lebih "kuat" di mana:

  • Klien terus mencoba menyambung kembali berdasarkan retry_strategy
  • Panggilan/konsumen/pengguna Klien tidak hang saat terhubung kembali
  • Perintah masih dapat diantrekan (dengan syarat?) untuk dieksekusi jika/ketika koneksi dibuat kembali ... jika saya menambah sesuatu, dll.

Mungkin banyak skenario sulit untuk dipertimbangkan di sini...

@rumah baru ,

Terima kasih telah menyebutkan ini. Kami menginginkan perilaku yang sama untuk aplikasi kami dan solusi Anda berfungsi untuk segera mengembalikan kesalahan ke penelepon, tetapi izinkan kami mencoba lagi untuk terhubung tanpa batas.

Terima kasih @newhouse , saya tidak yakin saya akan pernah menemukan ini.

IMO ini harus menjadi perilaku default, atau setidaknya secara eksplisit dipanggil dalam dokumen sekitar retry_strategy . Beri tahu saya jika menurut Anda yang terakhir masuk akal, dan saya akan membuka PR untuk menambahkan dokumen.

@bwhitty PR dokumen dari siapa pun akan sangat diterima 👍

Salah satu masalah dengan menggunakan enable_offline_queue = false adalah perintah yang dikirim tepat setelah memanggil createClient , tetapi sebelum koneksi benar-benar dibuka akan gagal juga.

Salah satu masalah dengan menggunakan enable_offline_queue = false adalah perintah yang dikirim tepat setelah memanggil createClient , tetapi sebelum koneksi benar-benar dibuka akan gagal juga.

@jkirkwood Kedengarannya Anda ingin menunda mencoba mengirim perintah pertama Anda sampai acara ready (atau acara serupa yang sesuai) dipancarkan?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat