org.apache.hadoop.hbase.regionserver.GetClosestRowBeforeTracker Maven / Gradle / Ivy
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.regionserver;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.KeyValue.KVComparator;
import org.apache.hadoop.hbase.KeyValueUtil;
import org.apache.hadoop.hbase.util.Bytes;
/**
* State and utility processing {@link HRegion#getClosestRowBefore(byte[], byte[])}.
* Like {@link ScanQueryMatcher} and {@link ScanDeleteTracker} but does not
* implement the {@link DeleteTracker} interface since state spans rows (There
* is no update nor reset method).
*/
@InterfaceAudience.Private
class GetClosestRowBeforeTracker {
private final KeyValue targetkey;
// Any cell w/ a ts older than this is expired.
private final long now;
private final long oldestUnexpiredTs;
private Cell candidate = null;
private final KVComparator kvcomparator;
// Flag for whether we're doing getclosest on a metaregion.
private final boolean metaregion;
// Offset and length into targetkey demarking table name (if in a metaregion).
private final int rowoffset;
private final int tablenamePlusDelimiterLength;
// Deletes keyed by row. Comparator compares on row portion of KeyValue only.
private final NavigableMap> deletes;
/**
* @param c
* @param kv Presume first on row: i.e. empty column, maximum timestamp and
* a type of Type.Maximum
* @param ttl Time to live in ms for this Store
* @param metaregion True if this is hbase:meta or -ROOT- region.
*/
GetClosestRowBeforeTracker(final KVComparator c, final KeyValue kv,
final long ttl, final boolean metaregion) {
super();
this.metaregion = metaregion;
this.targetkey = kv;
// If we are in a metaregion, then our table name is the prefix on the
// targetkey.
this.rowoffset = kv.getRowOffset();
int l = -1;
if (metaregion) {
l = KeyValue.getDelimiter(kv.getRowArray(), rowoffset, kv.getRowLength(),
HConstants.DELIMITER) - this.rowoffset;
}
this.tablenamePlusDelimiterLength = metaregion? l + 1: -1;
this.now = System.currentTimeMillis();
this.oldestUnexpiredTs = now - ttl;
this.kvcomparator = c;
KeyValue.RowOnlyComparator rc = new KeyValue.RowOnlyComparator(this.kvcomparator);
this.deletes = new TreeMap>(rc);
}
/*
* Add the specified KeyValue to the list of deletes.
* @param kv
*/
private void addDelete(final Cell kv) {
NavigableSet rowdeletes = this.deletes.get(kv);
if (rowdeletes == null) {
rowdeletes = new TreeSet(this.kvcomparator);
this.deletes.put(KeyValueUtil.ensureKeyValue(kv), rowdeletes);
}
rowdeletes.add(KeyValueUtil.ensureKeyValue(kv));
}
/*
* @param kv Adds candidate if nearer the target than previous candidate.
* @return True if updated candidate.
*/
private boolean addCandidate(final Cell kv) {
if (!isDeleted(kv) && isBetterCandidate(kv)) {
this.candidate = kv;
return true;
}
return false;
}
boolean isBetterCandidate(final Cell contender) {
return this.candidate == null ||
(this.kvcomparator.compareRows(this.candidate, contender) < 0 &&
this.kvcomparator.compareRows(contender, this.targetkey) <= 0);
}
/*
* Check if specified KeyValue buffer has been deleted by a previously
* seen delete.
* @param kv
* @return true is the specified KeyValue is deleted, false if not
*/
private boolean isDeleted(final Cell kv) {
if (this.deletes.isEmpty()) return false;
NavigableSet rowdeletes = this.deletes.get(kv);
if (rowdeletes == null || rowdeletes.isEmpty()) return false;
return isDeleted(kv, rowdeletes);
}
/**
* Check if the specified KeyValue buffer has been deleted by a previously
* seen delete.
* @param kv
* @param ds
* @return True is the specified KeyValue is deleted, false if not
*/
public boolean isDeleted(final Cell kv, final NavigableSet ds) {
if (deletes == null || deletes.isEmpty()) return false;
for (KeyValue d: ds) {
long kvts = kv.getTimestamp();
long dts = d.getTimestamp();
if (CellUtil.isDeleteFamily(d)) {
if (kvts <= dts) return true;
continue;
}
// Check column
int ret = Bytes.compareTo(kv.getQualifierArray(), kv.getQualifierOffset(),
kv.getQualifierLength(),
d.getQualifierArray(), d.getQualifierOffset(), d.getQualifierLength());
if (ret <= -1) {
// This delete is for an earlier column.
continue;
} else if (ret >= 1) {
// Beyond this kv.
break;
}
// Check Timestamp
if (kvts > dts) return false;
// Check Type
switch (KeyValue.Type.codeToType(d.getType())) {
case Delete: return kvts == dts;
case DeleteColumn: return true;
default: continue;
}
}
return false;
}
/**
* @param cell
* @return true if the cell is expired
*/
public boolean isExpired(final Cell cell) {
return cell.getTimestamp() < this.oldestUnexpiredTs ||
HStore.isCellTTLExpired(cell, this.oldestUnexpiredTs, this.now);
}
/*
* Handle keys whose values hold deletes.
* Add to the set of deletes and then if the candidate keys contain any that
* might match, then check for a match and remove it. Implies candidates
* is made with a Comparator that ignores key type.
* @param kv
* @return True if we removed k
from candidates
.
*/
boolean handleDeletes(final Cell kv) {
addDelete(kv);
boolean deleted = false;
if (!hasCandidate()) return deleted;
if (isDeleted(this.candidate)) {
this.candidate = null;
deleted = true;
}
return deleted;
}
/**
* Do right thing with passed key, add to deletes or add to candidates.
* @param kv
* @return True if we added a candidate
*/
boolean handle(final Cell kv) {
if (KeyValueUtil.ensureKeyValue(kv).isDelete()) {
handleDeletes(kv);
return false;
}
return addCandidate(kv);
}
/**
* @return True if has candidate
*/
public boolean hasCandidate() {
return this.candidate != null;
}
/**
* @return Best candidate or null.
*/
public Cell getCandidate() {
return this.candidate;
}
public KeyValue getTargetKey() {
return this.targetkey;
}
/**
* @param kv Current kv
* @param firstOnRow on row kv.
* @return True if we went too far, past the target key.
*/
boolean isTooFar(final Cell kv, final Cell firstOnRow) {
return this.kvcomparator.compareRows(kv, firstOnRow) > 0;
}
boolean isTargetTable(final Cell kv) {
if (!metaregion) return true;
// Compare start of keys row. Compare including delimiter. Saves having
// to calculate where tablename ends in the candidate kv.
return Bytes.compareTo(this.targetkey.getRowArray(), this.rowoffset,
this.tablenamePlusDelimiterLength,
kv.getRowArray(), kv.getRowOffset(), this.tablenamePlusDelimiterLength) == 0;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy