001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.regionserver.querymatcher; 019 020import java.io.IOException; 021import org.apache.hadoop.hbase.CellUtil; 022import org.apache.hadoop.hbase.ExtendedCell; 023import org.apache.hadoop.hbase.KeepDeletedCells; 024import org.apache.hadoop.hbase.KeyValue; 025import org.apache.hadoop.hbase.KeyValueUtil; 026import org.apache.hadoop.hbase.PrivateCellUtil; 027import org.apache.hadoop.hbase.client.Scan; 028import org.apache.hadoop.hbase.regionserver.ScanInfo; 029import org.apache.yetus.audience.InterfaceAudience; 030 031/** 032 * Query matcher for normal user scan. 033 */ 034@InterfaceAudience.Private 035public abstract class NormalUserScanQueryMatcher extends UserScanQueryMatcher { 036 037 /** 038 * Number of consecutive range delete markers (DeleteColumn/DeleteFamily) to skip before switching 039 * to seek. Seeking is more expensive than skipping for a single marker, but much faster when 040 * markers accumulate. This threshold avoids the seek overhead for the common case (one delete per 041 * row/column) while still kicking in when markers pile up. 042 */ 043 static final int SEEK_ON_DELETE_MARKER_THRESHOLD = 10; 044 045 /** Keeps track of deletes */ 046 private final DeleteTracker deletes; 047 048 /** True if we are doing a 'Get' Scan. Every Get is actually a one-row Scan. */ 049 private final boolean get; 050 051 /** whether time range queries can see rows "behind" a delete */ 052 protected final boolean seePastDeleteMarkers; 053 054 /** Whether seek optimization for range delete markers is applicable */ 055 private final boolean canSeekOnDeleteMarker; 056 057 /** Count of consecutive range delete markers seen for the same column */ 058 private int rangeDeleteCount; 059 060 /** Last range delete cell, for qualifier comparison across consecutive markers */ 061 private ExtendedCell lastDelete; 062 063 protected NormalUserScanQueryMatcher(Scan scan, ScanInfo scanInfo, ColumnTracker columns, 064 boolean hasNullColumn, DeleteTracker deletes, long oldestUnexpiredTS, long now) { 065 super(scan, scanInfo, columns, hasNullColumn, oldestUnexpiredTS, now); 066 this.deletes = deletes; 067 this.get = scan.isGetScan(); 068 this.seePastDeleteMarkers = scanInfo.getKeepDeletedCells() != KeepDeletedCells.FALSE; 069 this.canSeekOnDeleteMarker = 070 !seePastDeleteMarkers && deletes.getClass() == ScanDeleteTracker.class; 071 } 072 073 @Override 074 public void beforeShipped() throws IOException { 075 super.beforeShipped(); 076 deletes.beforeShipped(); 077 if (lastDelete != null) { 078 lastDelete = KeyValueUtil.toNewKeyCell(lastDelete); 079 } 080 } 081 082 @Override 083 public MatchCode match(ExtendedCell cell) throws IOException { 084 if (filter != null && filter.filterAllRemaining()) { 085 return MatchCode.DONE_SCAN; 086 } 087 MatchCode returnCode = preCheck(cell); 088 if (returnCode != null) { 089 return returnCode; 090 } 091 long timestamp = cell.getTimestamp(); 092 byte typeByte = cell.getTypeByte(); 093 if (PrivateCellUtil.isDelete(typeByte)) { 094 boolean includeDeleteMarker = 095 seePastDeleteMarkers ? tr.withinTimeRange(timestamp) : tr.withinOrAfterTimeRange(timestamp); 096 if (includeDeleteMarker) { 097 this.deletes.add(cell); 098 } 099 100 // A DeleteColumn or DeleteFamily masks all remaining cells for this column/family. 101 // Seek past them instead of skipping one cell at a time, but only after seeing 102 // enough consecutive markers for the same column to justify the seek overhead. 103 // Only safe with plain ScanDeleteTracker. Not safe with newVersionBehavior (sequence 104 // IDs determine visibility), visibility labels (delete/put label mismatch), or 105 // seePastDeleteMarkers (KEEP_DELETED_CELLS). 106 if ( 107 canSeekOnDeleteMarker && (typeByte == KeyValue.Type.DeleteFamily.getCode() 108 || (typeByte == KeyValue.Type.DeleteColumn.getCode() && cell.getQualifierLength() > 0)) 109 ) { 110 if (lastDelete != null && !CellUtil.matchingQualifier(cell, lastDelete)) { 111 rangeDeleteCount = 0; 112 } 113 lastDelete = cell; 114 if (++rangeDeleteCount >= SEEK_ON_DELETE_MARKER_THRESHOLD) { 115 rangeDeleteCount = 0; 116 return columns.getNextRowOrNextColumn(cell); 117 } 118 } else { 119 rangeDeleteCount = 0; 120 } 121 return MatchCode.SKIP; 122 } 123 rangeDeleteCount = 0; 124 returnCode = checkDeleted(deletes, cell); 125 if (returnCode != null) { 126 return returnCode; 127 } 128 return matchColumn(cell, timestamp, typeByte); 129 } 130 131 @Override 132 protected void reset() { 133 deletes.reset(); 134 rangeDeleteCount = 0; 135 lastDelete = null; 136 } 137 138 @Override 139 protected boolean isGet() { 140 return get; 141 } 142 143 public static NormalUserScanQueryMatcher create(Scan scan, ScanInfo scanInfo, 144 ColumnTracker columns, DeleteTracker deletes, boolean hasNullColumn, long oldestUnexpiredTS, 145 long now) throws IOException { 146 if (scan.isReversed()) { 147 if (scan.includeStopRow()) { 148 return new NormalUserScanQueryMatcher(scan, scanInfo, columns, hasNullColumn, deletes, 149 oldestUnexpiredTS, now) { 150 151 @Override 152 protected boolean moreRowsMayExistsAfter(int cmpToStopRow) { 153 return cmpToStopRow >= 0; 154 } 155 }; 156 } else { 157 return new NormalUserScanQueryMatcher(scan, scanInfo, columns, hasNullColumn, deletes, 158 oldestUnexpiredTS, now) { 159 160 @Override 161 protected boolean moreRowsMayExistsAfter(int cmpToStopRow) { 162 return cmpToStopRow > 0; 163 } 164 }; 165 } 166 } else { 167 if (scan.includeStopRow()) { 168 return new NormalUserScanQueryMatcher(scan, scanInfo, columns, hasNullColumn, deletes, 169 oldestUnexpiredTS, now) { 170 171 @Override 172 protected boolean moreRowsMayExistsAfter(int cmpToStopRow) { 173 return cmpToStopRow <= 0; 174 } 175 }; 176 } else { 177 return new NormalUserScanQueryMatcher(scan, scanInfo, columns, hasNullColumn, deletes, 178 oldestUnexpiredTS, now) { 179 180 @Override 181 protected boolean moreRowsMayExistsAfter(int cmpToStopRow) { 182 return cmpToStopRow < 0; 183 } 184 }; 185 } 186 } 187 } 188}