blob: 84eb25ea49b9ba3efd5136185333ae8a60e7b042 [file] [log] [blame]
/**
* 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 static org.apache.hadoop.hbase.HBaseTestingUtility.COLUMNS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.KeepDeletedCells;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
import org.apache.hadoop.hbase.filter.TimestampsFilter;
import org.apache.hadoop.hbase.testclassification.RegionServerTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.ManualEnvironmentEdge;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TestName;
/**
* Test Minimum Versions feature (HBASE-4071).
*/
@Category({RegionServerTests.class, SmallTests.class})
public class TestMinVersions {
@ClassRule
public static final HBaseClassTestRule CLASS_RULE =
HBaseClassTestRule.forClass(TestMinVersions.class);
HBaseTestingUtility hbu = new HBaseTestingUtility();
private final byte[] T0 = Bytes.toBytes("0");
private final byte[] T1 = Bytes.toBytes("1");
private final byte[] T2 = Bytes.toBytes("2");
private final byte[] T3 = Bytes.toBytes("3");
private final byte[] T4 = Bytes.toBytes("4");
private final byte[] T5 = Bytes.toBytes("5");
private final byte[] c0 = COLUMNS[0];
@Rule public TestName name = new TestName();
/**
* Verify behavior of getClosestBefore(...)
*/
@Test
public void testGetClosestBefore() throws Exception {
ColumnFamilyDescriptor cfd =
ColumnFamilyDescriptorBuilder.newBuilder(c0)
.setMinVersions(1).setMaxVersions(1000).setTimeToLive(1).
setKeepDeletedCells(KeepDeletedCells.FALSE).build();
TableDescriptor htd = TableDescriptorBuilder.
newBuilder(TableName.valueOf(name.getMethodName())).setColumnFamily(cfd).build();
HRegion region = hbu.createLocalHRegion(htd, null, null);
try {
// 2s in the past
long ts = EnvironmentEdgeManager.currentTime() - 2000;
Put p = new Put(T1, ts);
p.addColumn(c0, c0, T1);
region.put(p);
p = new Put(T1, ts+1);
p.addColumn(c0, c0, T4);
region.put(p);
p = new Put(T3, ts);
p.addColumn(c0, c0, T3);
region.put(p);
// now make sure that getClosestBefore(...) get can
// rows that would be expired without minVersion.
// also make sure it gets the latest version
Result r = hbu.getClosestRowBefore(region, T1, c0);
checkResult(r, c0, T4);
r = hbu.getClosestRowBefore(region, T2, c0);
checkResult(r, c0, T4);
// now flush/compact
region.flush(true);
region.compact(true);
r = hbu.getClosestRowBefore(region, T1, c0);
checkResult(r, c0, T4);
r = hbu.getClosestRowBefore(region, T2, c0);
checkResult(r, c0, T4);
} finally {
HBaseTestingUtility.closeRegionAndWAL(region);
}
}
/**
* Test mixed memstore and storefile scanning
* with minimum versions.
*/
@Test
public void testStoreMemStore() throws Exception {
// keep 3 versions minimum
ColumnFamilyDescriptor cfd =
ColumnFamilyDescriptorBuilder.newBuilder(c0)
.setMinVersions(3).setMaxVersions(1000).setTimeToLive(1).
setKeepDeletedCells(KeepDeletedCells.FALSE).build();
TableDescriptor htd = TableDescriptorBuilder.
newBuilder(TableName.valueOf(name.getMethodName())).setColumnFamily(cfd).build();
HRegion region = hbu.createLocalHRegion(htd, null, null);
// 2s in the past
long ts = EnvironmentEdgeManager.currentTime() - 2000;
try {
Put p = new Put(T1, ts-1);
p.addColumn(c0, c0, T2);
region.put(p);
p = new Put(T1, ts-3);
p.addColumn(c0, c0, T0);
region.put(p);
// now flush/compact
region.flush(true);
region.compact(true);
p = new Put(T1, ts);
p.addColumn(c0, c0, T3);
region.put(p);
p = new Put(T1, ts-2);
p.addColumn(c0, c0, T1);
region.put(p);
p = new Put(T1, ts-3);
p.addColumn(c0, c0, T0);
region.put(p);
// newest version in the memstore
// the 2nd oldest in the store file
// and the 3rd, 4th oldest also in the memstore
Get g = new Get(T1);
g.readAllVersions();
Result r = region.get(g); // this'll use ScanWildcardColumnTracker
checkResult(r, c0, T3,T2,T1);
g = new Get(T1);
g.readAllVersions();
g.addColumn(c0, c0);
r = region.get(g); // this'll use ExplicitColumnTracker
checkResult(r, c0, T3,T2,T1);
} finally {
HBaseTestingUtility.closeRegionAndWAL(region);
}
}
/**
* Make sure the Deletes behave as expected with minimum versions
*/
@Test
public void testDelete() throws Exception {
ColumnFamilyDescriptor cfd =
ColumnFamilyDescriptorBuilder.newBuilder(c0)
.setMinVersions(3).setMaxVersions(1000).setTimeToLive(1).
setKeepDeletedCells(KeepDeletedCells.FALSE).build();
TableDescriptor htd = TableDescriptorBuilder.
newBuilder(TableName.valueOf(name.getMethodName())).setColumnFamily(cfd).build();
HRegion region = hbu.createLocalHRegion(htd, null, null);
// 2s in the past
long ts = EnvironmentEdgeManager.currentTime() - 2000;
try {
Put p = new Put(T1, ts-2);
p.addColumn(c0, c0, T1);
region.put(p);
p = new Put(T1, ts-1);
p.addColumn(c0, c0, T2);
region.put(p);
p = new Put(T1, ts);
p.addColumn(c0, c0, T3);
region.put(p);
Delete d = new Delete(T1, ts-1);
region.delete(d);
Get g = new Get(T1);
g.readAllVersions();
Result r = region.get(g); // this'll use ScanWildcardColumnTracker
checkResult(r, c0, T3);
g = new Get(T1);
g.readAllVersions();
g.addColumn(c0, c0);
r = region.get(g); // this'll use ExplicitColumnTracker
checkResult(r, c0, T3);
// now flush/compact
region.flush(true);
region.compact(true);
// try again
g = new Get(T1);
g.readAllVersions();
r = region.get(g); // this'll use ScanWildcardColumnTracker
checkResult(r, c0, T3);
g = new Get(T1);
g.readAllVersions();
g.addColumn(c0, c0);
r = region.get(g); // this'll use ExplicitColumnTracker
checkResult(r, c0, T3);
} finally {
HBaseTestingUtility.closeRegionAndWAL(region);
}
}
/**
* Make sure the memstor behaves correctly with minimum versions
*/
@Test
public void testMemStore() throws Exception {
ColumnFamilyDescriptor cfd =
ColumnFamilyDescriptorBuilder.newBuilder(c0)
.setMinVersions(2).setMaxVersions(1000).setTimeToLive(1).
setKeepDeletedCells(KeepDeletedCells.FALSE).build();
TableDescriptor htd = TableDescriptorBuilder.
newBuilder(TableName.valueOf(name.getMethodName())).setColumnFamily(cfd).build();
HRegion region = hbu.createLocalHRegion(htd, null, null);
// 2s in the past
long ts = EnvironmentEdgeManager.currentTime() - 2000;
try {
// 2nd version
Put p = new Put(T1, ts-2);
p.addColumn(c0, c0, T2);
region.put(p);
// 3rd version
p = new Put(T1, ts-1);
p.addColumn(c0, c0, T3);
region.put(p);
// 4th version
p = new Put(T1, ts);
p.addColumn(c0, c0, T4);
region.put(p);
// now flush/compact
region.flush(true);
region.compact(true);
// now put the first version (backdated)
p = new Put(T1, ts-3);
p.addColumn(c0, c0, T1);
region.put(p);
// now the latest change is in the memstore,
// but it is not the latest version
Result r = region.get(new Get(T1));
checkResult(r, c0, T4);
Get g = new Get(T1);
g.readAllVersions();
r = region.get(g); // this'll use ScanWildcardColumnTracker
checkResult(r, c0, T4,T3);
g = new Get(T1);
g.readAllVersions();
g.addColumn(c0, c0);
r = region.get(g); // this'll use ExplicitColumnTracker
checkResult(r, c0, T4,T3);
p = new Put(T1, ts+1);
p.addColumn(c0, c0, T5);
region.put(p);
// now the latest version is in the memstore
g = new Get(T1);
g.readAllVersions();
r = region.get(g); // this'll use ScanWildcardColumnTracker
checkResult(r, c0, T5,T4);
g = new Get(T1);
g.readAllVersions();
g.addColumn(c0, c0);
r = region.get(g); // this'll use ExplicitColumnTracker
checkResult(r, c0, T5,T4);
} finally {
HBaseTestingUtility.closeRegionAndWAL(region);
}
}
/**
* Verify basic minimum versions functionality
*/
@Test
public void testBaseCase() throws Exception {
// 2 version minimum, 1000 versions maximum, ttl = 1s
ColumnFamilyDescriptor cfd =
ColumnFamilyDescriptorBuilder.newBuilder(c0)
.setMinVersions(2).setMaxVersions(1000).setTimeToLive(1).
setKeepDeletedCells(KeepDeletedCells.FALSE).build();
TableDescriptor htd = TableDescriptorBuilder.
newBuilder(TableName.valueOf(name.getMethodName())).setColumnFamily(cfd).build();
HRegion region = hbu.createLocalHRegion(htd, null, null);
try {
// 2s in the past
long ts = EnvironmentEdgeManager.currentTime() - 2000;
// 1st version
Put p = new Put(T1, ts-3);
p.addColumn(c0, c0, T1);
region.put(p);
// 2nd version
p = new Put(T1, ts-2);
p.addColumn(c0, c0, T2);
region.put(p);
// 3rd version
p = new Put(T1, ts-1);
p.addColumn(c0, c0, T3);
region.put(p);
// 4th version
p = new Put(T1, ts);
p.addColumn(c0, c0, T4);
region.put(p);
Result r = region.get(new Get(T1));
checkResult(r, c0, T4);
Get g = new Get(T1);
g.setTimeRange(0L, ts+1);
r = region.get(g);
checkResult(r, c0, T4);
// oldest version still exists
g.setTimeRange(0L, ts-2);
r = region.get(g);
checkResult(r, c0, T1);
// gets see only available versions
// even before compactions
g = new Get(T1);
g.readAllVersions();
r = region.get(g); // this'll use ScanWildcardColumnTracker
checkResult(r, c0, T4,T3);
g = new Get(T1);
g.readAllVersions();
g.addColumn(c0, c0);
r = region.get(g); // this'll use ExplicitColumnTracker
checkResult(r, c0, T4,T3);
// now flush
region.flush(true);
// with HBASE-4241 a flush will eliminate the expired rows
g = new Get(T1);
g.setTimeRange(0L, ts-2);
r = region.get(g);
assertTrue(r.isEmpty());
// major compaction
region.compact(true);
// after compaction the 4th version is still available
g = new Get(T1);
g.setTimeRange(0L, ts+1);
r = region.get(g);
checkResult(r, c0, T4);
// so is the 3rd
g.setTimeRange(0L, ts);
r = region.get(g);
checkResult(r, c0, T3);
// but the 2nd and earlier versions are gone
g.setTimeRange(0L, ts-1);
r = region.get(g);
assertTrue(r.isEmpty());
} finally {
HBaseTestingUtility.closeRegionAndWAL(region);
}
}
/**
* Verify that basic filters still behave correctly with
* minimum versions enabled.
*/
@Test
public void testFilters() throws Exception {
final byte [] c1 = COLUMNS[1];
ColumnFamilyDescriptor cfd =
ColumnFamilyDescriptorBuilder.newBuilder(c0)
.setMinVersions(2).setMaxVersions(1000).setTimeToLive(1).
setKeepDeletedCells(KeepDeletedCells.FALSE).build();
ColumnFamilyDescriptor cfd2 =
ColumnFamilyDescriptorBuilder.newBuilder(c1)
.setMinVersions(2).setMaxVersions(1000).setTimeToLive(1).
setKeepDeletedCells(KeepDeletedCells.FALSE).build();
List<ColumnFamilyDescriptor> cfdList = new ArrayList();
cfdList.add(cfd);
cfdList.add(cfd2);
TableDescriptor htd = TableDescriptorBuilder.
newBuilder(TableName.valueOf(name.getMethodName())).setColumnFamilies(cfdList).build();
HRegion region = hbu.createLocalHRegion(htd, null, null);
// 2s in the past
long ts = EnvironmentEdgeManager.currentTime() - 2000;
try {
Put p = new Put(T1, ts-3);
p.addColumn(c0, c0, T0);
p.addColumn(c1, c1, T0);
region.put(p);
p = new Put(T1, ts-2);
p.addColumn(c0, c0, T1);
p.addColumn(c1, c1, T1);
region.put(p);
p = new Put(T1, ts-1);
p.addColumn(c0, c0, T2);
p.addColumn(c1, c1, T2);
region.put(p);
p = new Put(T1, ts);
p.addColumn(c0, c0, T3);
p.addColumn(c1, c1, T3);
region.put(p);
List<Long> tss = new ArrayList<>();
tss.add(ts-1);
tss.add(ts-2);
// Sholud only get T2, versions is 2, so T1 is gone from user view.
Get g = new Get(T1);
g.addColumn(c1,c1);
g.setFilter(new TimestampsFilter(tss));
g.readAllVersions();
Result r = region.get(g);
checkResult(r, c1, T2);
// Sholud only get T2, versions is 2, so T1 is gone from user view.
g = new Get(T1);
g.addColumn(c0,c0);
g.setFilter(new TimestampsFilter(tss));
g.readAllVersions();
r = region.get(g);
checkResult(r, c0, T2);
// now flush/compact
region.flush(true);
region.compact(true);
// After flush/compact, the result should be consistent with previous result
g = new Get(T1);
g.addColumn(c1,c1);
g.setFilter(new TimestampsFilter(tss));
g.readAllVersions();
r = region.get(g);
checkResult(r, c1, T2);
// After flush/compact, the result should be consistent with previous result
g = new Get(T1);
g.addColumn(c0,c0);
g.setFilter(new TimestampsFilter(tss));
g.readAllVersions();
r = region.get(g);
checkResult(r, c0, T2);
} finally {
HBaseTestingUtility.closeRegionAndWAL(region);
}
}
@Test
public void testMinVersionsWithKeepDeletedCellsTTL() throws Exception {
int ttl = 4;
ColumnFamilyDescriptor cfd =
ColumnFamilyDescriptorBuilder.newBuilder(c0)
.setMinVersions(2).setMaxVersions(Integer.MAX_VALUE).setTimeToLive(ttl).
setKeepDeletedCells(KeepDeletedCells.TTL).build();
TableDescriptor htd = TableDescriptorBuilder.
newBuilder(TableName.valueOf(name.getMethodName())).setColumnFamily(cfd).build();
HRegion region = hbu.createLocalHRegion(htd, null, null);
long startTS = EnvironmentEdgeManager.currentTime();
ManualEnvironmentEdge injectEdge = new ManualEnvironmentEdge();
injectEdge.setValue(startTS);
EnvironmentEdgeManager.injectEdge(injectEdge);
long ts = startTS - 2000;
// 1st version
Put p = new Put(T1, ts-3);
p.addColumn(c0, c0, T1);
region.put(p);
// 2nd version
p = new Put(T1, ts-2);
p.addColumn(c0, c0, T2);
region.put(p);
// 3rd version
p = new Put(T1, ts-1);
p.addColumn(c0, c0, T3);
region.put(p);
Get g;
Result r;
//check we can still see all versions before compaction
g = new Get(T1);
g.readAllVersions();
g.setTimeRange(0, ts);
r = region.get(g);
checkResult(r, c0, T3, T2, T1);
region.flush(true);
region.compact(true);
Assert.assertEquals(startTS, EnvironmentEdgeManager.currentTime());
long expiredTime = EnvironmentEdgeManager.currentTime() - ts - 3;
Assert.assertTrue("TTL for T1 has expired", expiredTime < (ttl * 1000));
//check that nothing was purged yet
g = new Get(T1);
g.readAllVersions();
g.setTimeRange(0, ts);
r = region.get(g);
checkResult(r, c0, T3, T2, T1);
g = new Get(T1);
g.readAllVersions();
g.setTimeRange(0, ts -1);
r = region.get(g);
checkResult(r, c0, T2, T1);
injectEdge.incValue(ttl * 1000);
region.flush(true);
region.compact(true);
//check that after compaction (which is after TTL) that only T1 was purged
g = new Get(T1);
g.readAllVersions();
g.setTimeRange(0, ts);
r = region.get(g);
checkResult(r, c0, T3, T2);
g = new Get(T1);
g.readAllVersions();
g.setTimestamp(ts -2);
r = region.get(g);
checkResult(r, c0, T2);
}
private void checkResult(Result r, byte[] col, byte[] ... vals) {
assertEquals(vals.length, r.size());
List<Cell> kvs = r.getColumnCells(col, col);
assertEquals(kvs.size(), vals.length);
for (int i=0;i<vals.length;i++) {
String expected = Bytes.toString(vals[i]);
String actual = Bytes.toString(CellUtil.cloneValue(kvs.get(i)));
assertTrue(expected + " was expected but doesn't match " + actual,
CellUtil.matchingValue(kvs.get(i), vals[i]));
}
}
}