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

Adding a field to an UDT can corrupte the tables using it

    XMLWordPrintableJSON

    Details

    • Bug Category:
      Correctness - Unrecoverable Corruption / Loss
    • Severity:
      Critical

      Description

      Adding a field to an UDT which is used as a Set element or as a Map element can corrupt the table.
      The problem can be reproduced using the following test case:

          @Test
          public void testReadAfterAlteringUserTypeNestedWithinSet() throws Throwable
          {
              String ut1 = createType("CREATE TYPE %s (a int)");
              String columnType = KEYSPACE + "." + ut1;
      
              try
              {
                  createTable("CREATE TABLE %s (x int PRIMARY KEY, y set<frozen<" + columnType + ">>)");
                  disableCompaction();
      
                  execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType(1), userType(2)));
                  assertRows(execute("SELECT * FROM %s"), row(1, set(userType(1), userType(2))));
                  flush();
      
                  assertRows(execute("SELECT * FROM %s WHERE x = 1"),
                             row(1, set(userType(1), userType(2))));
      
                  execute("ALTER TYPE " + KEYSPACE + "." + ut1 + " ADD b int");
                  execute("UPDATE %s SET y = y + ? WHERE x = 1",
                          set(userType(1, 1), userType(1, 2), userType(2, 1)));
      
                  flush();
                  assertRows(execute("SELECT * FROM %s WHERE x = 1"),
                                 row(1, set(userType(1),
                                            userType(1, 1),
                                            userType(1, 2),
                                            userType(2),
                                            userType(2, 1))));
      
                  compact();
      
                  assertRows(execute("SELECT * FROM %s WHERE x = 1"),
                             row(1, set(userType(1),
                                        userType(1, 1),
                                        userType(1, 2),
                                        userType(2),
                                        userType(2, 1))));
              }
              finally
              {
                  enableCompaction();
              }
          }
      

      There are in fact 2 problems:

      1. When the sets from the 2 versions are merged the ColumnDefinition being picked up can be the older one. In which case when the tuples are sorted it my lead to an IndexOutOfBoundsException.
      2. During compaction, the old column definition can be the one being kept for the SSTable metadata. If it is the case the SSTable will not be readable any more and will be marked as corrupted.

      If one of the tables using the type has a Materialized View attached to it, the MV updates can also fail with IndexOutOfBoundsException.

      This problem can be reproduced using the following test:

          @Test
          public void testAlteringUserTypeNestedWithinSetWithView() throws Throwable
          {
              String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
      
              createTable("CREATE TABLE %s (pk int, c int, v int, s set<frozen<" + columnType + ">>, PRIMARY KEY (pk, c))");
              execute("CREATE MATERIALIZED VIEW " + keyspace() + ".view1 AS SELECT c, pk, v FROM %s WHERE pk IS NOT NULL AND c IS NOT NULL AND v IS NOT NULL PRIMARY KEY (c, pk)");
      
              execute("INSERT INTO %s (pk, c, v, s) VALUES(?, ?, ?, ?)", 1, 1, 1, set(userType(1), userType(2)));
              flush();
      
              execute("ALTER TYPE " + columnType + " ADD b int");
              execute("UPDATE %s SET s = s + ?, v = ? WHERE pk = ? AND c = ?",
                      set(userType(1, 1), userType(1, 2), userType(2, 1)), 2, 1, 1);
      
      
              assertRows(execute("SELECT * FROM %s WHERE pk = ? AND c = ?", 1, 1),
                             row(1, 1, 2, set(userType(1),
                                              userType(1, 1),
                                              userType(1, 2),
                                              userType(2),
                                              userType(2, 1))));
          }
      

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                blerer Benjamin Lerer
                Reporter:
                blerer Benjamin Lerer
                Authors:
                Benjamin Lerer
                Reviewers:
                Robert Stupp
              • Votes:
                0 Vote for this issue
                Watchers:
                6 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: