Как использовать Redis WATCH в Node.js?

Фон

У меня есть атомарная операция, и мне нужно использовать блокировку, чтобы другие клиенты не могли прочитать нестабильное значение.

  • Платформа: узел 10.1.0.
  • Библиотека: redis

Решение

Согласно официальной документации, решением для этого является использование WATCH вместе с MULTI:

Проблема

Теперь использование MULTI задокументировано, и у меня есть общее представление о том, как я могу его использовать.

var redis = require( "redis" );
var bluebird = require( "bluebird" );
var client = redis.createClient();
var multi = client.multi();

multi.hsetAsync( "test", "array", "[1, 2]" );
multi.hgetAsync( "test", "array" );
multi.execAsync( ).then( console.log ); // [ 0, "[1, 2]" ]

Я понимаю, что это правильная реализация multi. Сначала мне нужно создать клиента, а затем я создаю множественный запрос.

Я понимаю, что multi и client используют один и тот же интерфейс, но также неясно, какие преимущества (если есть) я получаю от использования hgetAsync вместо hget в запросе multi, поскольку я предполагаю, что все, что делает multi, это добавляет указанные запросы в очередь синхронно (поэтому мне не нужен асинхронный вариант).

При вызове multi.execAsync( ) выполнение запроса произойдет атомарно.

Но я не понимаю, как WATCH должен войти сюда. Я не смог найти никаких упоминаний об этом в документации, ничего о системе оптимистической блокировки, которая есть в REDIS.

Вопросы

Итак, у меня есть следующие вопросы:

  1. Поддерживается ли WATCH с MULTI?
  2. Если да, то не могли бы вы поделиться фрагментом кода?
  3. Имеет ли смысл в этом примере использовать multi.hgetAsync( "test", "array" ); вместо multi.hget( "test", "array" );?

person Flame_Phoenix    schedule 06.06.2018    source источник


Ответы (1)


ответ

Документации по использованию WATCH в node-redis нет. Однако я нашел чрезвычайно полезный набор советов в MDN:

https://developer.mozilla.org/en-US/docs/Mozilla/Redis_Tips

В целом, WATCH следует использовать следующим образом:

var redis  = require("redis"),
client = redis.createClient({ ... });

client.watch("foo", function( err ){
    if(err) throw err;

    client.get("foo", function(err, result) {
        if(err) throw err;

        // Process result
        // Heavy and time consuming operation here

        client.multi()
            .set("foo", "some heavy computation")
            .exec(function(err, results) {

                /**
                 * If err is null, it means Redis successfully attempted 
                 * the operation.
                 */ 
                if(err) throw err;

                /**
                 * If results === null, it means that a concurrent client
                 * changed the key while we were processing it and thus 
                 * the execution of the MULTI command was not performed.
                 * 
                 * NOTICE: Failing an execution of MULTI is not considered
                 * an error. So you will have err === null and results === null
                 */

            });
    });
});

Итак, отвечая на мои вопросы:

  1. Да, хотя watch вызывается на прототипе RedisClient, а не на прототипе Multi.
  2. Фрагмент кода приведен выше.
  3. Поскольку каждый метод из объекта, который имеет прототип Multi, возвращает сам объект, использование Async версий методов не приносит никакой пользы, за исключением execAsync, который позволяет вам выполнять множественные запросы и обрабатывать ответ в промисе вместо перезвонить.

Важные заметки

Еще одна очень важная вещь заключается в том, что watch работает только для KEYS, не для хэшей. Так что в моем случае нельзя смотреть поле array хэша test. Вы можете просмотреть весь набор test, но не конкретное поле.

Итак, потому что в моем коде я действительно хочу наблюдать за полем в хэше. Это невозможно. Вместо этого я должен использовать систему именования ключей, которая позволит это сделать:

var redis = require( "redis" );
var bluebird = require( "bluebird" );
var client = redis.createClient();
var multi = client.multi();

client.watchAsync( "test_array" )
    then( ( ) =>
        multi.set( "test_array", "[1, 2]" )
            .get( "test_array" )
            .execAsync( ) 
    )
    .then( console.log ); // [ 0, "[1, 2]" ]

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


Вы читаете это из будущего?

Если вы читаете это из будущего, теперь вы можете насладиться моим личным вкладом в проект node_redis и проверить обновленную документацию:

person Flame_Phoenix    schedule 07.06.2018
comment
Возможно ли иметь состояние гонки с использованием того же клиента Redis? Я думаю о том, чтобы запустить тот же MULTI, например, с async.parallel. - person Giggioz; 25.10.2018