Uploaded image for project: 'Phoenix Tephra'
  1. Phoenix Tephra
  2. TEPHRA-131

NPE on rollback of a family delete with ConflictDetection.ROW

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Major
    • Resolution: Fixed
    • None
    • 0.6.3
    • None

    Description

      An NPE occurs when attempting to rollback a transaction in which a family delete occurs with a ConflictDetection.ROW. This happens because the ActionChange is not capturing the familyName in this code:

        private Delete transactionalizeAction(Delete delete) throws IOException {
          long transactionTimestamp = tx.getWritePointer();
      
          byte[] deleteRow = delete.getRow();
          Delete txDelete = new Delete(deleteRow, transactionTimestamp);
      
          Map<byte[], List<Cell>> familyToDelete = delete.getFamilyCellMap();
          if (familyToDelete.isEmpty()) {
            // perform a row delete is we are using row-level conflict detection
            if (conflictLevel == TxConstants.ConflictDetection.ROW ||
                conflictLevel == TxConstants.ConflictDetection.NONE) {
              // no need to identify individual columns deleted
              addToChangeSet(deleteRow, null, null);  // FIXME (will lead to NPE)
            } else {
              Result result = get(new Get(delete.getRow()));
              // Delete everything
              NavigableMap<byte[], NavigableMap<byte[], byte[]>> resultMap = result.getNoVersionMap();
              for (Map.Entry<byte[], NavigableMap<byte[], byte[]>> familyEntry : resultMap.entrySet()) {
                NavigableMap<byte[], byte[]> familyColumns = result.getFamilyMap(familyEntry.getKey());
                for (Map.Entry<byte[], byte[]> column : familyColumns.entrySet()) {
                  txDelete.deleteColumns(familyEntry.getKey(), column.getKey(), transactionTimestamp);
                  addToChangeSet(deleteRow, familyEntry.getKey(), column.getKey());
                }
              }
            }
          } else {
            for (Map.Entry<byte [], List<Cell>> familyEntry : familyToDelete.entrySet()) {
              byte[] family = familyEntry.getKey();
              List<Cell> entries = familyEntry.getValue();
              boolean isFamilyDelete = false;
              if (entries.size() == 1) {
                Cell cell = entries.get(0);
                isFamilyDelete = CellUtil.isDeleteFamily(cell);
              }
              if (isFamilyDelete) {
                if (conflictLevel == TxConstants.ConflictDetection.ROW ||
                    conflictLevel == TxConstants.ConflictDetection.NONE) {
                  // no need to identify individual columns deleted
                  txDelete.deleteFamily(family);
                  addToChangeSet(deleteRow, null, null);  // FIXME
                } else {
      

      Then when the rollback occurs in the code below, an NPE will occur because the ActionChange has a null familyName:

        @Override
        protected boolean doRollback() throws Exception {
          try {
            // pre-size arraylist of deletes
            int size = 0;
            for (Set<ActionChange> cs : changeSets.values()) {
              size += cs.size();
            }
            List<Delete> rollbackDeletes = new ArrayList<Delete>(size);
            for (Map.Entry<Long, Set<ActionChange>> entry : changeSets.entrySet()) {
              long transactionTimestamp = entry.getKey();
              for (ActionChange change : entry.getValue()) {
                byte[] row = change.getRow();
                byte[] family = change.getFamily();
                byte[] qualifier = change.getQualifier();
                Delete rollbackDelete = new Delete(row);
                rollbackDelete.setAttribute(TxConstants.TX_ROLLBACK_ATTRIBUTE_KEY, new byte[0]);
                switch (conflictLevel) {
                  case ROW:
                  case NONE:
                    // issue family delete for the tx write pointer
                    rollbackDelete.deleteFamilyVersion(change.getFamily(), transactionTimestamp);  // NPE will occur here
      

      Not sure how you want to handle the case when familyToDelete.isEmpty() is true (i.e. a row delete - see first FIXME above). Maybe lookup the column families? FWIW, Phoenix will only do family deletes, so we wouldn't hit that code path.

      Attachments

        1. TEPHRA-131.patch
          0.9 kB
          James R. Taylor

        Activity

          People

            gokulavasan Gokul Gunasekaran
            jamestaylor James R. Taylor
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: