in phoenix-core-server/src/main/java/org/apache/phoenix/coprocessor/CompactionScanner.java [2285:2480]
private void getLastRowVersionInMaxLookbackWindow(List<Cell> result,
List<Cell> lastRowVersion, List<Cell> retainedCells, List<Cell> emptyColumn) {
long maxLookbackWindowStart = rowContext.getMaxLookbackWindowStart();
Cell currentColumnCell = null;
Cell deleteFamilyCell = null;
deleteFamilyVersionCellList.clear();
top:
for (int index = 0; index < result.size(); index++) {
Cell cell = result.get(index);
if (cell.getTimestamp() > maxLookbackWindowStart) {
// All cells within the max lookback window are retained. Here we retain all
// except the ones at the lower edge of the window. Those will be included in
// the last row version in the rest of the body of the loop
retainedCells.add(cell);
continue;
}
// The following section of the for loop processes an entire column in each
// iteration, that is, all cell versions for a given column will be processed in
// each iteration. Please note delete family markers (DeleteFamily and
// DeleteFamilyVersion) has their own column with the null column qualifier. The
// delete family column cells always forms the first column in a row of cells for a
// given column family
if (currentColumnCell == null) {
// This is the first column. If the row has any delete family markers, they will
// be in the first column
currentColumnCell = cell;
if (cell.getType() == Cell.Type.DeleteFamily) {
// The first delete family marker at the edge or outside the max lookback
// window is DeleteFamily.
deleteFamilyCell = cell;
if (cell.getTimestamp() == maxLookbackWindowStart) {
// it is at the edge of the window.So we need to include it in the last
// row version
lastRowVersion.add(cell);
} else if (!major) {
// Retain the delete markers if the compaction is not major
retainedCells.add(cell);
}
// Skip the rest of the delete family cells
index = skipColumn(result, currentColumnCell, retainedCells, index);
continue;
} else if (cell.getType() == Cell.Type.DeleteFamilyVersion) {
deleteFamilyVersionCellList.add(cell);
if (cell.getTimestamp() == maxLookbackWindowStart) {
lastRowVersion.add(cell);
} else if (!major) {
// Retain the delete markers if the compaction is not major
retainedCells.add(cell);
}
// Each DeleteFamilyVersion can delete at most one row version. There can be
// multiple of them, and we need to process each separately, and thus we
// need to track them in a list
for (int i = index + 1; i < result.size(); i++) {
cell = result.get(i);
if (cell.getType() == Cell.Type.DeleteFamilyVersion) {
index++;
deleteFamilyVersionCellList.add(cell);
if (!major) {
// Delete markers are retained if the compaction is not a major
// compaction
retainedCells.add(cell);
}
} else if (cell.getType() == Cell.Type.DeleteFamily) {
// After one or more DeleteFamilyVersion markers, there is a
// DeleteFamily marker. This marker deletes the rest of the cells
// and thus no need to process further delete family markers. Thus,
// we skip them using skipColumn
index++;
deleteFamilyCell = cell;
if (!major) {
retainedCells.add(cell);
}
// Skip the rest of the delete family cells
index = skipColumn(result, currentColumnCell, retainedCells, index);
continue top;
} else {
// Column changed as the current cell is not a delete family cell.
// Go back to the beginning of the for loop
continue top;
}
}
// All the cells in a row are processed
break top;
}
}
// All delete family markers are scanned and recorded above if there was any. Please
// note when we do region level compaction, each column family will have their owm
// delete family markers. Phoenix inserts the same set of delete markers to each
// column family. So, we need to keep track of the delete family markers of the
// first column family but apply these delete markers to all column families
currentColumnCell = cell;
// Is this cell masked by a delete column family version
if (!deleteFamilyVersionCellList.isEmpty()) {
// There could be back to back delete family version markers and thus we need a
// loop to check it
for (Cell deleteFamilyVersionCell : deleteFamilyVersionCellList) {
if (cell.getTimestamp() > deleteFamilyVersionCell.getTimestamp()) {
break;
}
if (cell.getTimestamp() == deleteFamilyVersionCell.getTimestamp()) {
// It is masked
if (cell.getType() != Cell.Type.Put) {
if (cell.getTimestamp() == maxLookbackWindowStart) {
lastRowVersion.add(cell);
} else if (!major) {
// Retain the delete markers if the compaction is not major
retainedCells.add(cell);
}
}
if (index + 1 < result.size()) {
cell = result.get(index + 1);
if (!CellUtil.matchingColumn(cell, currentColumnCell)) {
continue top;
}
index++;
} else {
break top;
}
}
}
}
if (deleteFamilyCell != null
&& deleteFamilyCell.getTimestamp() >= cell.getTimestamp()) {
// This column is deleted by a delete family marker. Skip this column
if (cell.getType() != Cell.Type.Put) {
if (cell.getTimestamp() == maxLookbackWindowStart) {
lastRowVersion.add(cell);
} else if (!major) {
// Retain the delete markers if the compaction is not major
retainedCells.add(cell);
}
}
index = skipColumn(result, currentColumnCell, retainedCells, index);
continue top;
}
// Process back-to-back deleted cell versions. Phoenix currently does not use delete
// cell version markers. This processing should not happen and is added for
// completeness
while (cell.getType() == Cell.Type.Delete) {
if (cell.getTimestamp() == maxLookbackWindowStart) {
lastRowVersion.add(cell);
} else if (!major) {
retainedCells.add(cell);
}
if (index + 1 < result.size()) {
Cell nextCell = result.get(index + 1);
if (!CellUtil.matchingColumn(currentColumnCell, nextCell)) {
continue top;
}
// Increment index by one as the delete cell should be consumed
index++;
if (nextCell.getType() == Cell.Type.Put
&& cell.getTimestamp() == nextCell.getTimestamp()) {
// This put cell is masked by the delete marker
index++;
if (index < result.size()) {
cell = result.get(index);
} else {
break top;
}
}
} else {
break top;
}
}
if (cell.getType() == Cell.Type.DeleteColumn) {
// The rest of the column is masked by this delete column cell
if (cell.getTimestamp() == maxLookbackWindowStart) {
lastRowVersion.add(cell);
} else if (!major) {
retainedCells.add(cell);
}
index = skipColumn(result, currentColumnCell, retainedCells, index);
continue top;
}
if (cell.getType() == Cell.Type.Put) {
lastRowVersion.add(cell);
if (ScanUtil.isEmptyColumn(cell, emptyCF, emptyCQ)) {
index = addEmptyColumn(result, currentColumnCell, index, emptyColumn);
} else {
index = skipColumn(result, currentColumnCell, retainedCells, index);
}
continue top;
}
// We can visit another delete family column for another column family if we are
// doing region level compaction. In that case, we should also retain delete family
// markers from that column family here. So we need to check if the cell type is
// DeleteFamily or DeleteFamilyVersion, the column family is the store under
// compaction and the compaction is not a major compaction.
if (!major && CellUtil.matchingFamily(cell, storeColumnFamily) &&
(cell.getType() == Cell.Type.DeleteFamily
|| cell.getType() == Cell.Type.DeleteFamilyVersion)) {
index = skipColumn(result, currentColumnCell, retainedCells, index);
}
}
}