简体   繁体   中英

Transactions failing when all conditions are met in StackExchange.Redis

I'm using Stackexchange.Redis on a cluster ASP.Net Core servers, and having some trouble with transactions.

The following code is supposed to delete a field with the key "hashItemID" from two hashes, "HashA" and "HashB", but only if it exists in both:

var hashAKey = "HashA";
var hashBKey = "HashB";
var id = "hashItemID";

var tran = redis.Database.CreateTransaction();

// Only delete the item if it exists in both hashes
var hashBCondition = tran.AddCondition(Condition.HashExists(hashBKey, id));
var hashACondition = tran.AddCondition(Condition.HashExists(hashAKey, id));

tran.HashDeleteAsync(hashBKey, id);
tran.HashDeleteAsync(hashAKey, id);

var deleted = await tran.ExecuteAsync();

if (!deleted)
{
    logger.LogWarning("Failed to delete '{ID}'. HashAResult: {A}, HashBResult: {B}", id, hashACondition.WasSatisfied, hashBCondition.WasSatisfied);
}

Occasionally this code will fail with the log:

Failed to delete 'hashItemID'. HashAResult: True, HashBResult: True

I was under the impression that transactions only fail if their conditions aren't met, is that true?

Looking at network and performance metrics, there's no timeouts or high memory usage that could be contributing to this.

EDIT: Unfortunately at higher loads this approach started to fall apart. Sometimes a transaction would require 50 attempts, hurting performance and taking a long time. Still looking for a better solution.

I implemented a workaround that does a good enough job.

In the case of !deleted && hashACondition.WasSatisfied && hashBCondition.WasSatisfied I just immediately retry the deletion, up to 4 times.

According to tests, this has mostly fixed it. Of 6000 attempts, 430 needed to be retried once, 62 needed to be retried twice, etc.

This still seems like a bug in redis or stackexchange.redis to me though. It would be nice to hear from someone who knows better than me!

Since I can't add comments because of limits in reputation, You may try this to see if it works for you or not:

var hashAKey = "HashA";
var hashBKey = "HashB";
var id = "hashItemID";

var tran = redis.Database.CreateTransaction();

// Only delete the item if it exists in both hashes
var hashBCondition = tran.AddCondition(Condition.HashExists(hashBKey, id));
var hashACondition = tran.AddCondition(Condition.HashExists(hashAKey, id));

tran.HashDeleteAsync(hashBKey, id);
tran.HashDeleteAsync(hashAKey, id);

bool deleted = await tran.ExecuteAsync();

if (!deleted)
{
    logger.LogWarning("Failed to delete '{ID}'. HashAResult: {A}, HashBResult: {B}", id, hashACondition.WasSatisfied, hashBCondition.WasSatisfied);
}

Use bool deleted instead of var deleted

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM