Search code examples
c#redisbooksleeve

Redis transactions in Booksleve


The 3 commands below work perfectly when they are executed as is.

using (var redis = NewRedisConnection)
{
    await redis.Open();
    var allKeys = await redis.Keys.Find(db, "searchPattern");
    var allVals = await redis.Strings.GetString(db, allKeys);
    await redis.Keys.Remove(db, allKeys);

    //process the data I pull from redis
}

But when I try to wrap them in a transaction it stops working. Exceptions aren't thrown, but if I debug the execution seems to stop at var allKeys = ... Am I missing something While setting up the transaction?

using (var redis = NewRedisConnection)
{
    await redis.Open();
    var tran = redis.Createtransaction();

    var allKeys = await tran.Keys.Find(db, "searchPattern");
    var allVals = await tran.Strings.GetString(db, allKeys);
    await tran.Keys.Remove(db, allKeys);

    await tran.Execute();
    //process the data I pull from redis
}

Solution

  • Transactions are buffered locally until you call "exec", which is then sent in an atomic unit. If you "await" before the exec, you'll never send anything. It is necessary to only "await" the operations in a transaction only after execute is called.

    Secondly, you cannot query data during a transaction. To be more specific, you can, but you only get the results when you call "execute". This is a fundamental part of how redis works: simply, a redis transaction is not the same as a SQL transaction. This means you can't make decisions based on data queried during a transation. You can, however, query the data outside the transaction, and then ensure it doesn't change. With booksleeve, the AddConstraint method on a transaction can add a number of common assertions which can be safely validated during the transaction.

    Thirdly, Keys.Find should not be used as part of routine code - that should be used rarely, typically during debug and database analysis.

    In your case, I wonder whether a "hash" would be a better option - so rather than "find all keys matching this pattern, fetch all values from those keys, delete all those keys", it could be done as "get all values from this hash, delete this hash".

    so something like (note: mobile phone code! Excuse typos)

    using(var tran = conn.CreateTransaction())
    {
        valsPending = tran.Hashes.GetAll(db, key);
        tran.Keys.Remove(db, key);
        await tran.Execute();
        var vals = await valsPending;
        // iterate dictionary in "vals", decoding each .Value with UTF-8
    }
    

    I can add a GetAllString if it makes the above easier.