Как сделать несколько операций atomi c, используя StackExchange.Redis - PullRequest
1 голос
/ 29 января 2020

Здравствуйте, у меня есть следующая проблема:

У меня есть ха sh, который содержит строки. Этот ха sh будет запрошен несколькими пользователями.

Когда пользователь приходит с Key чтобы сначала проверить, существует ли он в этом ха sh, а если нет, то добавьте его.

Как я могу сделать операции "проверить, существует ли ха sh", "добавить, если не существует "атоми c? Чтение документации redis кажется Watch - это то, что мне нужно. В основном начинайте транзакцию и завершайте ее, если переменная изменится.

Я пытался использовать Condition.HashNotExists безрезультатно:

class Program {

        public static async Task<bool> LockFileForEditAsync(int fileId) {
            var database = ConnectionMultiplexer.Connect(CON).GetDatabase();
            var exists = await database.HashExistsAsync("files", fileId); //this line is for  shorting the transaction if hash exists 

            if (exists) {
                return false;
            }

            var tran = database.CreateTransaction();
            tran.AddCondition(Condition.HashNotExists("files", fileId));
            var setKey = tran.HashSetAsync("files", new HashEntry[] { new HashEntry(fileId, 1) });
            var existsTsc = tran.HashExistsAsync("files", fileId);

            if (!await tran.ExecuteAsync()) {
                return false;
            }

            var rezult = await existsTsc;
            return rezult;
        }

        public const string CON = "127.0.0.1:6379,ssl=False,allowAdmin=True,abortConnect=False,defaultDatabase=0";

        static async Task Main(string[] args) {
            int fid = 1;
            var locked = await LockFileForEditAsync(fid);
        }
    }

Если я подключаюсь через redis-cli и выдаю в cli: hset files {fileId} 1, вправо ДО Я выдаю ExecuteAsync (в отладчике) Я ожидаю, что эта транзакция завершится неудачно, так как я поместил Condition. Однако этого не происходит.

Как я могу в основном использовать команды redis для установки чего-то вроде блокировки на две операции:

  1. Проверить, существует ли hashentry
  2. Добавить hashentry

1 Ответ

1 голос
/ 31 января 2020

Плохие новости, хорошие новости и все остальное ...

Плохие новости

Это не работает, потому что SE.Redis конвейеризирует транзакцию. Это означает, что все команды транзакции отправляются на сервер одновременно при вызове ExecuteAsync(). Однако условия оцениваются первыми.

tran.AddCondition(Condition.HashNotExists("files", fileId)); переводится в:

WATCH "files"
HEXISTS "files" "1"

И эти две команды отправляются первыми, когда ExecuteAsync() вызывается для оценки условия. Если условие выполняется (HEXISTS "files" "1" = 0), то отправляются остальные команды транзакции.

Это эффективно гарантирует отсутствие ложных срабатываний , потому что если ключ files изменен между ними (пока SE.Redis оценивает условие), WATCH вызовет сбой транзакции.

Проблема в ложных отрицаниях . Например, транзакция также завершится неудачей, если было установлено другое поле ha sh, пока SE.Redis оценивает условие. WATCH "files" делает это так.

Я проверил это, запустив redis-benchmark -c 5 HSET files 2 1 при вызове ExecuteAsync(). Условие выполнено, но транзакция не удалась, хотя поле «1» не существовало, поскольку поле «2» было установлено между ними.

Я проверил, используя команду MONITOR в отдельное окно redis-cli. Это удобно для устранения проблем, которые не оправдались, или просто для просмотра того, что на самом деле идет на сервер, и когда.

A WATCH бесполезно, если вы заботитесь о поле ha sh, так как создаст ложные негативы при прикосновении к другим полям.

Решение этой проблемы - вместо этого использовать обычный ключ (files:1).

Хорошие новости

Существует команда для выполнения именно того, что вы хотите: HSETNX .

Это полностью упрощает LockFileForEditAsyn c ():

    public static async Task<bool> LockFileForEditAsync(int fileId)
    {
        var database = ConnectionMultiplexer.Connect(CON).GetDatabase();
        var setKey = await database.HashSetAsync("files", fileId, 1, When.NotExists);
        return setKey;
    }

Примечание параметр When.NotExists. Это приводит к отправке команды HSETNX "files" "1" "1".

Для всего остального ...

Вы можете использовать Lua сценариев в ситуациях, подобных здесь, где вы хотите, чтобы некоторые условные действия выполнялись атомарно, как в как использовать команду spop с счетчиком, если set имеет столько элементов (count) в наборе .

Кажется, что вы пытаемся сделать распределенную блокировку. См. Распределенные блокировки с Redis для других аспектов, которые вы можете рассмотреть. См. Что такое распределенная блокировка Atomi c в драйверах кеша? , чтобы узнать больше об этом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...