Плохие новости, хорошие новости и все остальное ...
Плохие новости
Это не работает, потому что 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 в драйверах кеша? , чтобы узнать больше об этом.