2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.regionserver
;
20 import static org
.apache
.hadoop
.hbase
.HBaseTestingUtil
.COLUMNS
;
21 import static org
.junit
.Assert
.assertEquals
;
22 import static org
.junit
.Assert
.assertTrue
;
24 import java
.io
.IOException
;
25 import java
.util
.ArrayList
;
26 import java
.util
.List
;
27 import org
.apache
.hadoop
.hbase
.Cell
;
28 import org
.apache
.hadoop
.hbase
.CellUtil
;
29 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
30 import org
.apache
.hadoop
.hbase
.HBaseTestingUtil
;
31 import org
.apache
.hadoop
.hbase
.KeepDeletedCells
;
32 import org
.apache
.hadoop
.hbase
.TableName
;
33 import org
.apache
.hadoop
.hbase
.client
.ColumnFamilyDescriptor
;
34 import org
.apache
.hadoop
.hbase
.client
.ColumnFamilyDescriptorBuilder
;
35 import org
.apache
.hadoop
.hbase
.client
.Delete
;
36 import org
.apache
.hadoop
.hbase
.client
.Get
;
37 import org
.apache
.hadoop
.hbase
.client
.Put
;
38 import org
.apache
.hadoop
.hbase
.client
.Result
;
39 import org
.apache
.hadoop
.hbase
.client
.TableDescriptor
;
40 import org
.apache
.hadoop
.hbase
.client
.TableDescriptorBuilder
;
41 import org
.apache
.hadoop
.hbase
.filter
.TimestampsFilter
;
42 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
43 import org
.apache
.hadoop
.hbase
.testclassification
.RegionServerTests
;
44 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
45 import org
.apache
.hadoop
.hbase
.util
.EnvironmentEdgeManager
;
46 import org
.apache
.hadoop
.hbase
.util
.ManualEnvironmentEdge
;
47 import org
.junit
.Assert
;
48 import org
.junit
.ClassRule
;
49 import org
.junit
.Rule
;
50 import org
.junit
.Test
;
51 import org
.junit
.experimental
.categories
.Category
;
52 import org
.junit
.rules
.TestName
;
55 * Test Minimum Versions feature (HBASE-4071).
57 @Category({RegionServerTests
.class, MediumTests
.class})
58 public class TestMinVersions
{
61 public static final HBaseClassTestRule CLASS_RULE
=
62 HBaseClassTestRule
.forClass(TestMinVersions
.class);
64 HBaseTestingUtil hbu
= new HBaseTestingUtil();
65 private final byte[] T0
= Bytes
.toBytes("0");
66 private final byte[] T1
= Bytes
.toBytes("1");
67 private final byte[] T2
= Bytes
.toBytes("2");
68 private final byte[] T3
= Bytes
.toBytes("3");
69 private final byte[] T4
= Bytes
.toBytes("4");
70 private final byte[] T5
= Bytes
.toBytes("5");
72 private final byte[] c0
= COLUMNS
[0];
74 @Rule public TestName name
= new TestName();
77 * Verify behavior of getClosestBefore(...)
80 public void testGetClosestBefore() throws Exception
{
82 ColumnFamilyDescriptor cfd
=
83 ColumnFamilyDescriptorBuilder
.newBuilder(c0
)
84 .setMinVersions(1).setMaxVersions(1000).setTimeToLive(1).
85 setKeepDeletedCells(KeepDeletedCells
.FALSE
).build();
87 TableDescriptor htd
= TableDescriptorBuilder
.
88 newBuilder(TableName
.valueOf(name
.getMethodName())).setColumnFamily(cfd
).build();
89 HRegion region
= hbu
.createLocalHRegion(htd
, null, null);
93 long ts
= EnvironmentEdgeManager
.currentTime() - 2000;
95 Put p
= new Put(T1
, ts
);
96 p
.addColumn(c0
, c0
, T1
);
99 p
= new Put(T1
, ts
+1);
100 p
.addColumn(c0
, c0
, T4
);
104 p
.addColumn(c0
, c0
, T3
);
107 // now make sure that getClosestBefore(...) get can
108 // rows that would be expired without minVersion.
109 // also make sure it gets the latest version
110 Result r
= hbu
.getClosestRowBefore(region
, T1
, c0
);
111 checkResult(r
, c0
, T4
);
113 r
= hbu
.getClosestRowBefore(region
, T2
, c0
);
114 checkResult(r
, c0
, T4
);
118 region
.compact(true);
120 r
= hbu
.getClosestRowBefore(region
, T1
, c0
);
121 checkResult(r
, c0
, T4
);
123 r
= hbu
.getClosestRowBefore(region
, T2
, c0
);
124 checkResult(r
, c0
, T4
);
126 HBaseTestingUtil
.closeRegionAndWAL(region
);
131 * Test mixed memstore and storefile scanning
132 * with minimum versions.
135 public void testStoreMemStore() throws Exception
{
136 // keep 3 versions minimum
138 ColumnFamilyDescriptor cfd
=
139 ColumnFamilyDescriptorBuilder
.newBuilder(c0
)
140 .setMinVersions(3).setMaxVersions(1000).setTimeToLive(1).
141 setKeepDeletedCells(KeepDeletedCells
.FALSE
).build();
143 TableDescriptor htd
= TableDescriptorBuilder
.
144 newBuilder(TableName
.valueOf(name
.getMethodName())).setColumnFamily(cfd
).build();
146 HRegion region
= hbu
.createLocalHRegion(htd
, null, null);
148 long ts
= EnvironmentEdgeManager
.currentTime() - 2000;
151 Put p
= new Put(T1
, ts
-1);
152 p
.addColumn(c0
, c0
, T2
);
155 p
= new Put(T1
, ts
-3);
156 p
.addColumn(c0
, c0
, T0
);
161 region
.compact(true);
164 p
.addColumn(c0
, c0
, T3
);
167 p
= new Put(T1
, ts
-2);
168 p
.addColumn(c0
, c0
, T1
);
171 p
= new Put(T1
, ts
-3);
172 p
.addColumn(c0
, c0
, T0
);
175 // newest version in the memstore
176 // the 2nd oldest in the store file
177 // and the 3rd, 4th oldest also in the memstore
181 Result r
= region
.get(g
); // this'll use ScanWildcardColumnTracker
182 checkResult(r
, c0
, T3
,T2
,T1
);
187 r
= region
.get(g
); // this'll use ExplicitColumnTracker
188 checkResult(r
, c0
, T3
,T2
,T1
);
190 HBaseTestingUtil
.closeRegionAndWAL(region
);
195 * Make sure the Deletes behave as expected with minimum versions
198 public void testDelete() throws Exception
{
199 ColumnFamilyDescriptor cfd
=
200 ColumnFamilyDescriptorBuilder
.newBuilder(c0
)
201 .setMinVersions(3).setMaxVersions(1000).setTimeToLive(1).
202 setKeepDeletedCells(KeepDeletedCells
.FALSE
).build();
204 TableDescriptor htd
= TableDescriptorBuilder
.
205 newBuilder(TableName
.valueOf(name
.getMethodName())).setColumnFamily(cfd
).build();
207 HRegion region
= hbu
.createLocalHRegion(htd
, null, null);
210 long ts
= EnvironmentEdgeManager
.currentTime() - 2000;
213 Put p
= new Put(T1
, ts
-2);
214 p
.addColumn(c0
, c0
, T1
);
217 p
= new Put(T1
, ts
-1);
218 p
.addColumn(c0
, c0
, T2
);
222 p
.addColumn(c0
, c0
, T3
);
225 Delete d
= new Delete(T1
, ts
-1);
230 Result r
= region
.get(g
); // this'll use ScanWildcardColumnTracker
231 checkResult(r
, c0
, T3
);
236 r
= region
.get(g
); // this'll use ExplicitColumnTracker
237 checkResult(r
, c0
, T3
);
241 region
.compact(true);
246 r
= region
.get(g
); // this'll use ScanWildcardColumnTracker
247 checkResult(r
, c0
, T3
);
252 r
= region
.get(g
); // this'll use ExplicitColumnTracker
253 checkResult(r
, c0
, T3
);
255 HBaseTestingUtil
.closeRegionAndWAL(region
);
260 * Make sure the memstor behaves correctly with minimum versions
263 public void testMemStore() throws Exception
{
264 ColumnFamilyDescriptor cfd
=
265 ColumnFamilyDescriptorBuilder
.newBuilder(c0
)
266 .setMinVersions(2).setMaxVersions(1000).setTimeToLive(1).
267 setKeepDeletedCells(KeepDeletedCells
.FALSE
).build();
269 TableDescriptor htd
= TableDescriptorBuilder
.
270 newBuilder(TableName
.valueOf(name
.getMethodName())).setColumnFamily(cfd
).build();
271 HRegion region
= hbu
.createLocalHRegion(htd
, null, null);
274 long ts
= EnvironmentEdgeManager
.currentTime() - 2000;
278 Put p
= new Put(T1
, ts
-2);
279 p
.addColumn(c0
, c0
, T2
);
283 p
= new Put(T1
, ts
-1);
284 p
.addColumn(c0
, c0
, T3
);
289 p
.addColumn(c0
, c0
, T4
);
294 region
.compact(true);
296 // now put the first version (backdated)
297 p
= new Put(T1
, ts
-3);
298 p
.addColumn(c0
, c0
, T1
);
301 // now the latest change is in the memstore,
302 // but it is not the latest version
304 Result r
= region
.get(new Get(T1
));
305 checkResult(r
, c0
, T4
);
309 r
= region
.get(g
); // this'll use ScanWildcardColumnTracker
310 checkResult(r
, c0
, T4
,T3
);
315 r
= region
.get(g
); // this'll use ExplicitColumnTracker
316 checkResult(r
, c0
, T4
,T3
);
318 p
= new Put(T1
, ts
+1);
319 p
.addColumn(c0
, c0
, T5
);
322 // now the latest version is in the memstore
326 r
= region
.get(g
); // this'll use ScanWildcardColumnTracker
327 checkResult(r
, c0
, T5
,T4
);
332 r
= region
.get(g
); // this'll use ExplicitColumnTracker
333 checkResult(r
, c0
, T5
,T4
);
335 HBaseTestingUtil
.closeRegionAndWAL(region
);
340 * Verify basic minimum versions functionality
343 public void testBaseCase() throws Exception
{
344 // 2 version minimum, 1000 versions maximum, ttl = 1s
345 ColumnFamilyDescriptor cfd
=
346 ColumnFamilyDescriptorBuilder
.newBuilder(c0
)
347 .setMinVersions(2).setMaxVersions(1000).setTimeToLive(1).
348 setKeepDeletedCells(KeepDeletedCells
.FALSE
).build();
350 TableDescriptor htd
= TableDescriptorBuilder
.
351 newBuilder(TableName
.valueOf(name
.getMethodName())).setColumnFamily(cfd
).build();
352 HRegion region
= hbu
.createLocalHRegion(htd
, null, null);
356 long ts
= EnvironmentEdgeManager
.currentTime() - 2000;
359 Put p
= new Put(T1
, ts
-3);
360 p
.addColumn(c0
, c0
, T1
);
364 p
= new Put(T1
, ts
-2);
365 p
.addColumn(c0
, c0
, T2
);
369 p
= new Put(T1
, ts
-1);
370 p
.addColumn(c0
, c0
, T3
);
375 p
.addColumn(c0
, c0
, T4
);
378 Result r
= region
.get(new Get(T1
));
379 checkResult(r
, c0
, T4
);
382 g
.setTimeRange(0L, ts
+1);
384 checkResult(r
, c0
, T4
);
386 // oldest version still exists
387 g
.setTimeRange(0L, ts
-2);
389 checkResult(r
, c0
, T1
);
391 // gets see only available versions
392 // even before compactions
395 r
= region
.get(g
); // this'll use ScanWildcardColumnTracker
396 checkResult(r
, c0
, T4
,T3
);
401 r
= region
.get(g
); // this'll use ExplicitColumnTracker
402 checkResult(r
, c0
, T4
,T3
);
407 // with HBASE-4241 a flush will eliminate the expired rows
409 g
.setTimeRange(0L, ts
-2);
411 assertTrue(r
.isEmpty());
414 region
.compact(true);
416 // after compaction the 4th version is still available
418 g
.setTimeRange(0L, ts
+1);
420 checkResult(r
, c0
, T4
);
423 g
.setTimeRange(0L, ts
);
425 checkResult(r
, c0
, T3
);
427 // but the 2nd and earlier versions are gone
428 g
.setTimeRange(0L, ts
-1);
430 assertTrue(r
.isEmpty());
432 HBaseTestingUtil
.closeRegionAndWAL(region
);
437 * Verify that basic filters still behave correctly with
438 * minimum versions enabled.
441 public void testFilters() throws Exception
{
442 final byte [] c1
= COLUMNS
[1];
443 ColumnFamilyDescriptor cfd
=
444 ColumnFamilyDescriptorBuilder
.newBuilder(c0
)
445 .setMinVersions(2).setMaxVersions(1000).setTimeToLive(1).
446 setKeepDeletedCells(KeepDeletedCells
.FALSE
).build();
448 ColumnFamilyDescriptor cfd2
=
449 ColumnFamilyDescriptorBuilder
.newBuilder(c1
)
450 .setMinVersions(2).setMaxVersions(1000).setTimeToLive(1).
451 setKeepDeletedCells(KeepDeletedCells
.FALSE
).build();
452 List
<ColumnFamilyDescriptor
> cfdList
= new ArrayList();
456 TableDescriptor htd
= TableDescriptorBuilder
.
457 newBuilder(TableName
.valueOf(name
.getMethodName())).setColumnFamilies(cfdList
).build();
458 HRegion region
= hbu
.createLocalHRegion(htd
, null, null);
461 long ts
= EnvironmentEdgeManager
.currentTime() - 2000;
464 Put p
= new Put(T1
, ts
-3);
465 p
.addColumn(c0
, c0
, T0
);
466 p
.addColumn(c1
, c1
, T0
);
469 p
= new Put(T1
, ts
-2);
470 p
.addColumn(c0
, c0
, T1
);
471 p
.addColumn(c1
, c1
, T1
);
474 p
= new Put(T1
, ts
-1);
475 p
.addColumn(c0
, c0
, T2
);
476 p
.addColumn(c1
, c1
, T2
);
480 p
.addColumn(c0
, c0
, T3
);
481 p
.addColumn(c1
, c1
, T3
);
484 List
<Long
> tss
= new ArrayList
<>();
488 // Sholud only get T2, versions is 2, so T1 is gone from user view.
491 g
.setFilter(new TimestampsFilter(tss
));
493 Result r
= region
.get(g
);
494 checkResult(r
, c1
, T2
);
496 // Sholud only get T2, versions is 2, so T1 is gone from user view.
499 g
.setFilter(new TimestampsFilter(tss
));
502 checkResult(r
, c0
, T2
);
506 region
.compact(true);
508 // After flush/compact, the result should be consistent with previous result
511 g
.setFilter(new TimestampsFilter(tss
));
514 checkResult(r
, c1
, T2
);
516 // After flush/compact, the result should be consistent with previous result
519 g
.setFilter(new TimestampsFilter(tss
));
522 checkResult(r
, c0
, T2
);
524 HBaseTestingUtil
.closeRegionAndWAL(region
);
529 public void testMinVersionsWithKeepDeletedCellsTTL() throws Exception
{
531 ColumnFamilyDescriptor cfd
=
532 ColumnFamilyDescriptorBuilder
.newBuilder(c0
)
533 .setVersionsWithTimeToLive(ttl
, 2).build();
534 verifyVersionedCellKeyValues(ttl
, cfd
);
536 cfd
= ColumnFamilyDescriptorBuilder
.newBuilder(c0
)
538 .setMaxVersions(Integer
.MAX_VALUE
)
540 .setKeepDeletedCells(KeepDeletedCells
.TTL
)
542 verifyVersionedCellKeyValues(ttl
, cfd
);
545 private void verifyVersionedCellKeyValues(int ttl
, ColumnFamilyDescriptor cfd
)
547 TableDescriptor htd
= TableDescriptorBuilder
.
548 newBuilder(TableName
.valueOf(name
.getMethodName())).setColumnFamily(cfd
).build();
550 HRegion region
= hbu
.createLocalHRegion(htd
, null, null);
553 long startTS
= EnvironmentEdgeManager
.currentTime();
554 ManualEnvironmentEdge injectEdge
= new ManualEnvironmentEdge();
555 injectEdge
.setValue(startTS
);
556 EnvironmentEdgeManager
.injectEdge(injectEdge
);
558 long ts
= startTS
- 2000;
559 putFourVersions(region
, ts
);
564 //check we can still see all versions before compaction
566 get
.readAllVersions();
567 get
.setTimeRange(0, ts
);
568 result
= region
.get(get
);
569 checkResult(result
, c0
, T4
, T3
, T2
, T1
);
572 region
.compact(true);
573 Assert
.assertEquals(startTS
, EnvironmentEdgeManager
.currentTime());
574 long expiredTime
= EnvironmentEdgeManager
.currentTime() - ts
- 4;
575 Assert
.assertTrue("TTL for T1 has expired", expiredTime
< (ttl
* 1000));
576 //check that nothing was purged yet
577 verifyBeforeCompaction(region
, ts
);
579 injectEdge
.incValue(ttl
* 1000);
582 region
.compact(true);
583 verifyAfterTtl(region
, ts
);
585 HBaseTestingUtil
.closeRegionAndWAL(region
);
589 private void verifyAfterTtl(HRegion region
, long ts
) throws IOException
{
592 //check that after compaction (which is after TTL) that only T1 && T2 were purged
594 get
.readAllVersions();
595 get
.setTimeRange(0, ts
);
596 result
= region
.get(get
);
597 checkResult(result
, c0
, T4
, T3
);
600 get
.readAllVersions();
601 get
.setTimeRange(0, ts
- 1);
602 result
= region
.get(get
);
603 checkResult(result
, c0
, T3
);
606 get
.readAllVersions();
607 get
.setTimestamp(ts
- 2);
608 result
= region
.get(get
);
609 checkResult(result
, c0
, T3
);
612 get
.readAllVersions();
613 get
.setTimestamp(ts
- 3);
614 result
= region
.get(get
);
615 Assert
.assertEquals(result
.getColumnCells(c0
, c0
).size(), 0);
618 get
.readAllVersions();
619 get
.setTimeRange(0, ts
- 2);
620 result
= region
.get(get
);
621 Assert
.assertEquals(result
.getColumnCells(c0
, c0
).size(), 0);
624 private void verifyBeforeCompaction(HRegion region
, long ts
) throws IOException
{
628 get
.readAllVersions();
629 get
.setTimeRange(0, ts
);
630 result
= region
.get(get
);
631 checkResult(result
, c0
, T4
, T3
, T2
, T1
);
634 get
.readAllVersions();
635 get
.setTimeRange(0, ts
- 1);
636 result
= region
.get(get
);
637 checkResult(result
, c0
, T3
, T2
, T1
);
640 get
.readAllVersions();
641 get
.setTimeRange(0, ts
- 2);
642 result
= region
.get(get
);
643 checkResult(result
, c0
, T2
, T1
);
646 get
.readAllVersions();
647 get
.setTimeRange(0, ts
- 3);
648 result
= region
.get(get
);
649 checkResult(result
, c0
, T1
);
652 private void putFourVersions(HRegion region
, long ts
) throws IOException
{
654 Put put
= new Put(T1
, ts
- 4);
655 put
.addColumn(c0
, c0
, T1
);
659 put
= new Put(T1
, ts
- 3);
660 put
.addColumn(c0
, c0
, T2
);
664 put
= new Put(T1
, ts
- 2);
665 put
.addColumn(c0
, c0
, T3
);
669 put
= new Put(T1
, ts
- 1);
670 put
.addColumn(c0
, c0
, T4
);
674 private void checkResult(Result r
, byte[] col
, byte[] ... vals
) {
675 assertEquals(vals
.length
, r
.size());
676 List
<Cell
> kvs
= r
.getColumnCells(col
, col
);
677 assertEquals(kvs
.size(), vals
.length
);
678 for (int i
=0;i
<vals
.length
;i
++) {
679 String expected
= Bytes
.toString(vals
[i
]);
680 String actual
= Bytes
.toString(CellUtil
.cloneValue(kvs
.get(i
)));
681 assertTrue(expected
+ " was expected but doesn't match " + actual
,
682 CellUtil
.matchingValue(kvs
.get(i
), vals
[i
]));