Bug: Using counter column type in Accord transactions leads to Atomicity / Consistency violations
Status: Resolved
Priority: Normal
Resolution: Fixed
Created: 31/Oct/23 18:24
Resolved: 30/Apr/24 21:56

*System configuration and information:*

Single node Cassandra with Accord transactions enabled running on docker

Built from commit: [a7cd114435704b988c81f47ef53d0bfd6441f38b|https://github.com/apache/cassandra/commit/a7cd114435704b988c81f47ef53d0bfd6441f38b]

CQLSH: [cqlsh 6.2.0 | Cassandra 5.0-alpha2-SNAPSHOT | CQL spec 3.4.7 | Native protocol v5]

 

*Steps to reproduce in CQLSH:*
{code:java}
CREATE KEYSPACE accord WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'} AND durable_writes = true;{code}
{code:java}
CREATE TABLE accord.accounts (
    partition text,
    account_id int,
    balance counter,
    PRIMARY KEY (partition, account_id)
) WITH CLUSTERING ORDER BY (account_id ASC);
{code}
{code:java}
BEGIN TRANSACTION
  UPDATE accord.accounts
    SET balance += 100
  WHERE
    partition = 'default'
    AND account_id = 0;
  UPDATE accord.accounts
    SET balance += 100
  WHERE
    partition = 'default'
    AND account_id =1;
COMMIT TRANSACTION;{code}
bug happens after executing the following statement:

Based on [Cassandra documentation|https://cassandra.apache.org/doc/trunk/cassandra/developing/cql/types.html#counters] regarding the use of counters, I expect the following results:

Transaction A: subtract 10 from the balance of account 1 (total ending balance of 90) and add 10 to the balance of account 0 (total ending balance of 110)

{*}Bug A{*}: Neither account's balance is updated and the state of the rows is left unchanged
{code:java}
/* Transaction A */
BEGIN TRANSACTION
    UPDATE accord.accounts
    SET balance -= 10
    WHERE
      partition = 'default'
      AND account_id = 1;
    UPDATE accord.accounts
    SET balance += 10
    WHERE
      partition = 'default'
      AND account_id = 0;
COMMIT TRANSACTION;{code}
Transaction B: subtract 10 from the balance of account 1 (total ending balance of 90) and add 10 to the balance of a new account 2 (total ending balance of 10)

{*}Bug B{*}: Only the new account 2 is created. The balance of account 1 is left unchanged
{code:java}
/* Transaction B */
BEGIN TRANSACTION
    UPDATE accord.accounts
    SET balance -= 10
    WHERE
      partition = 'default'
      AND account_id = 1;
    UPDATE accord.accounts
    SET balance += 10
    WHERE
      partition = 'default'
      AND account_id = 2;
COMMIT TRANSACTION;{code}
Bug / Error:
======================================================================

The result of performing a table read after executing each buggy transaction is:
{code:java}
/* Transaction / Bug A */
 partition | account_id | balance
-----------+------------+---------
   default |          0 |     100
   default |          1 |     100{code}
{code:java}
/* Transaction / Bug B */
 partition | account_id | balance
-----------+------------+---------
   default |          0 |     100
   default |          1 |     100
   default |          2 |      10 {code}
Note that performing the above statements without transaction blocks works as expected.

{color:#172b4d}This was found while testing Accord transactions with [~henrik.ingo] and team.{color}