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

NPE on rollback of a family delete with ConflictDetection.ROW

Attach filesAttach ScreenshotVotersWatch issueWatchersCreate sub-taskLinkCloneUpdate Comment AuthorReplace String in CommentUpdate Comment VisibilityDelete Comments
    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

        Activity

          This comment will be Viewable by All Users Viewable by All Users
          Cancel

          People

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

            Dates

              Created:
              Updated:
              Resolved:

              Slack

                Issue deployment