Uploaded image for project: 'Apache Cassandra'
  1. Apache Cassandra
  2. CASSANDRA-6426

Lost update in Prepared statement batch when a column is inserted with null value in the same batch just before

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Normal
    • Resolution: Not A Problem
    • None
    • None
    • None
    • Cassandra Server 2.0.3
      Java Driver Core 2.0.0-rc1

    • Normal

    Description

      Test environment

      Cassandra Server 2.0.3
      Java Driver Core 2.0.0-rc1

      While implementing batched prepared statements for Achilles, I ran into a very annoying bug.

      FailingUnitTest.java
      //Given
      Long id = RandomUtils.nextLong();
      session.execute("CREATE TABLE test(id bigint, name text,label text, PRIMARY KEY(id))");
      PreparedStatement insertPS = session.prepare("INSERT INTO test(id,name,label) VALUES (?,?,?)");
      PreparedStatement updatePS = session.prepare("UPDATE test SET label=? WHERE id=?");
      
      // Notice the "label" column is inserted first with null
      BoundStatement insertBS = insertPS.bind(id, "myName", null);
      // Then "label" is updated to "myLabel"
      BoundStatement updateBS = updatePS.bind("myLabel", id);
      
      //When
      BatchStatement batch = new BatchStatement();
      batch.add(insertBS);
      batch.add(updateBS);
      session.execute(batch);
      
      //Then
      Statement statement = new SimpleStatement("SELECT * from test where id=" + id);
      Row row = session.execute(statement).one();
      // Assertion FAIL, "label" is NULL
      assertThat(row.getString("label")).isEqualTo("myLabel");
      

      The above code always fails.

      Even if I switch the order of the bound statements, e.g. updateBS added to the batch before insertBS, the test still fails.

      WorkingUnitTest.java
      //Given
      Long id = RandomUtils.nextLong();
      session.execute("CREATE TABLE test(id bigint, name text,label text, PRIMARY KEY(id))");
      PreparedStatement insertPS = session.prepare("INSERT INTO test(id,name) VALUES (?,?)");
      PreparedStatement updatePS = session.prepare("UPDATE test SET label=? WHERE id=?");
      
      // Notice the "label" column is removed from the insert statement
      BoundStatement insertBS = insertPS.bind(id, "myName");
      BoundStatement updateBS = updatePS.bind("myLabel", id);
      
      //When
      BatchStatement batch = new BatchStatement();
      batch.add(updateBS);
      batch.add(insertBS);
      session.execute(batch);
      
      //Then
      Statement statement = new SimpleStatement("SELECT * from test where id=" + id);
      Row row = session.execute(statement).one();
      // Assertion SUCCEEDS here
      assertThat(row.getString("label")).isEqualTo("myLabel");
      

      Only removing column "label" from the first insert bound statement can make the test succeed. This is pretty annoying.

      The rationale for inserting null values is that in Achilles I prepare generic insert statements for all entities and setting NULL when the field is not filled at runtime.

      Currently, there is no guarantee for batch of prepared statement to be executed in the order they are declared.

      Attachments

        Activity

          People

            Unassigned Unassigned
            doanduyhai DuyHai Doan
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: