Skip to content

BITOP failures in MULTI/EXEC transactions #1954

Description

@kevin-montrose

Describe the bug

Running BITOP commands in transactions fails under certain circumstance.

Steps to reproduce the bug

Two different symptoms, one for BITOP|NOT which appears to validation based in (in NetworkSKIP):

[Order(42)]
[Test]
[Category("BITFIELD")]
public async Task BitopNotTransactionAsync()
{
    const string Key = nameof(BitopNotTransactionAsync);

    using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig());
    var db = redis.GetDatabase();

    _ = await db.StringSetAsync(Key, new byte[] { 0 }).ConfigureAwait(false);

    // BITOP in transaction
    {
        var trans = db.CreateTransaction();
        var opRes = trans.ExecuteAsync("BITOP", "NOT", Key, Key).ConfigureAwait(false);

        _ = await trans.ExecuteAsync();

        _ = await opRes;
    }

    // Check result
    var res = (byte[])await db.StringGetAsync(Key).ConfigureAwait(false);
    ClassicAssert.IsTrue(new byte[] { 255 }.SequenceEqual(res));
}

This fails with EXECABORT Transaction discarded because of previous errors..

For binary ops (BITOP|AND in this case) we hit an assert failure:

[Order(43)]
[Test]
[Category("BITFIELD")]
public async Task BitopAndTransactionAsync()
{
    const string Key = nameof(BitopNotTransactionAsync);
    const string KeyA = nameof(BitopNotTransactionAsync) + "A";
    const string KeyB = nameof(BitopNotTransactionAsync) + "B";

    using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig());
    var db = redis.GetDatabase();

    _ = await db.StringSetAsync(KeyA, new byte[] { 0b1001_1001 }).ConfigureAwait(false);
    _ = await db.StringSetAsync(KeyB, new byte[] { 0b0111_1110 }).ConfigureAwait(false);

    // BITOP in transaction
    {
        var trans = db.CreateTransaction();
        var opRes = trans.ExecuteAsync("BITOP", "AND", Key, KeyA, KeyB).ConfigureAwait(false);

        _ = await trans.ExecuteAsync();

        _ = await opRes;
    }

    // Check result
    var res = (byte[])await db.StringGetAsync(Key).ConfigureAwait(false);
    ClassicAssert.IsTrue(new byte[] { 0b0001_1000 }.SequenceEqual(res));
}

Assert is Attempting to use a non-XLocked key in a Transactional context (requesting XLock): XLocked False, Slocked False.

Expected behavior

Both tests should pass - and both do if you remove the transactional logic.

Screenshots

No response

Release version

Latest main (2056547502693ad822f3531cf818849a17fcacc9)

IDE

No response

OS version

No response

Additional context

No response

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions