View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.regionserver;
20  
21  import static org.apache.hadoop.hbase.HBaseTestingUtility.COLUMNS;
22  import static org.apache.hadoop.hbase.HBaseTestingUtility.fam1;
23  import static org.apache.hadoop.hbase.HBaseTestingUtility.fam2;
24  import static org.apache.hadoop.hbase.HBaseTestingUtility.fam3;
25  import static org.junit.Assert.assertEquals;
26  import static org.junit.Assert.assertTrue;
27  
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  import org.apache.hadoop.hbase.Cell;
32  import org.apache.hadoop.hbase.CellUtil;
33  import org.apache.hadoop.hbase.HBaseTestingUtility;
34  import org.apache.hadoop.hbase.HColumnDescriptor;
35  import org.apache.hadoop.hbase.HTableDescriptor;
36  import org.apache.hadoop.hbase.KeepDeletedCells;
37  import org.apache.hadoop.hbase.TableName;
38  import org.apache.hadoop.hbase.testclassification.SmallTests;
39  import org.apache.hadoop.hbase.client.Delete;
40  import org.apache.hadoop.hbase.client.Get;
41  import org.apache.hadoop.hbase.client.Put;
42  import org.apache.hadoop.hbase.client.Result;
43  import org.apache.hadoop.hbase.filter.TimestampsFilter;
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.Rule;
48  import org.junit.Test;
49  import org.junit.experimental.categories.Category;
50  import org.junit.rules.TestName;
51  
52  /**
53   * Test Minimum Versions feature (HBASE-4071).
54   */
55  @Category(SmallTests.class)
56  public class TestMinVersions {
57    HBaseTestingUtility hbu = HBaseTestingUtility.createLocalHTU();
58    private final byte[] T0 = Bytes.toBytes("0");
59    private final byte[] T1 = Bytes.toBytes("1");
60    private final byte[] T2 = Bytes.toBytes("2");
61    private final byte[] T3 = Bytes.toBytes("3");
62    private final byte[] T4 = Bytes.toBytes("4");
63    private final byte[] T5 = Bytes.toBytes("5");
64  
65    private final byte[] c0 = COLUMNS[0];
66  
67    @Rule public TestName name = new TestName();
68  
69    /**
70     * Verify behavior of getClosestBefore(...)
71     */
72    @Test
73    public void testGetClosestBefore() throws Exception {
74      HTableDescriptor htd =
75          hbu.createTableDescriptor(name.getMethodName(), 1, 1000, 1, KeepDeletedCells.FALSE);
76      HRegion region = hbu.createLocalHRegion(htd, null, null);
77      try {
78  
79        // 2s in the past
80        long ts = EnvironmentEdgeManager.currentTime() - 2000;
81  
82        Put p = new Put(T1, ts);
83        p.add(c0, c0, T1);
84        region.put(p);
85  
86        p = new Put(T1, ts+1);
87        p.add(c0, c0, T4);
88        region.put(p);
89  
90        p = new Put(T3, ts);
91        p.add(c0, c0, T3);
92        region.put(p);
93  
94        // now make sure that getClosestBefore(...) get can
95        // rows that would be expired without minVersion.
96        // also make sure it gets the latest version
97        Result r = region.getClosestRowBefore(T1, c0);
98        checkResult(r, c0, T4);
99  
100       r = region.getClosestRowBefore(T2, c0);
101       checkResult(r, c0, T4);
102 
103       // now flush/compact
104       region.flush(true);
105       region.compact(true);
106 
107       r = region.getClosestRowBefore(T1, c0);
108       checkResult(r, c0, T4);
109 
110       r = region.getClosestRowBefore(T2, c0);
111       checkResult(r, c0, T4);
112     } finally {
113       HRegion.closeHRegion(region);
114     }
115   }
116 
117   /**
118    * Test mixed memstore and storefile scanning
119    * with minimum versions.
120    */
121   @Test
122   public void testStoreMemStore() throws Exception {
123     // keep 3 versions minimum
124     HTableDescriptor htd =
125         hbu.createTableDescriptor(name.getMethodName(), 3, 1000, 1, KeepDeletedCells.FALSE);
126     HRegion region = hbu.createLocalHRegion(htd, null, null);
127     // 2s in the past
128     long ts = EnvironmentEdgeManager.currentTime() - 2000;
129 
130     try {
131       Put p = new Put(T1, ts-1);
132       p.add(c0, c0, T2);
133       region.put(p);
134 
135       p = new Put(T1, ts-3);
136       p.add(c0, c0, T0);
137       region.put(p);
138 
139       // now flush/compact
140       region.flush(true);
141       region.compact(true);
142 
143       p = new Put(T1, ts);
144       p.add(c0, c0, T3);
145       region.put(p);
146 
147       p = new Put(T1, ts-2);
148       p.add(c0, c0, T1);
149       region.put(p);
150 
151       p = new Put(T1, ts-3);
152       p.add(c0, c0, T0);
153       region.put(p);
154 
155       // newest version in the memstore
156       // the 2nd oldest in the store file
157       // and the 3rd, 4th oldest also in the memstore
158 
159       Get g = new Get(T1);
160       g.setMaxVersions();
161       Result r = region.get(g); // this'll use ScanWildcardColumnTracker
162       checkResult(r, c0, T3,T2,T1);
163 
164       g = new Get(T1);
165       g.setMaxVersions();
166       g.addColumn(c0, c0);
167       r = region.get(g);  // this'll use ExplicitColumnTracker
168       checkResult(r, c0, T3,T2,T1);
169     } finally {
170       HRegion.closeHRegion(region);
171     }
172   }
173 
174   /**
175    * Make sure the Deletes behave as expected with minimum versions
176    */
177   @Test
178   public void testDelete() throws Exception {
179     HTableDescriptor htd =
180         hbu.createTableDescriptor(name.getMethodName(), 3, 1000, 1, KeepDeletedCells.FALSE);
181     HRegion region = hbu.createLocalHRegion(htd, null, null);
182 
183     // 2s in the past
184     long ts = EnvironmentEdgeManager.currentTime() - 2000;
185 
186     try {
187       Put p = new Put(T1, ts-2);
188       p.add(c0, c0, T1);
189       region.put(p);
190 
191       p = new Put(T1, ts-1);
192       p.add(c0, c0, T2);
193       region.put(p);
194 
195       p = new Put(T1, ts);
196       p.add(c0, c0, T3);
197       region.put(p);
198 
199       Delete d = new Delete(T1, ts-1);
200       region.delete(d);
201 
202       Get g = new Get(T1);
203       g.setMaxVersions();
204       Result r = region.get(g);  // this'll use ScanWildcardColumnTracker
205       checkResult(r, c0, T3);
206 
207       g = new Get(T1);
208       g.setMaxVersions();
209       g.addColumn(c0, c0);
210       r = region.get(g);  // this'll use ExplicitColumnTracker
211       checkResult(r, c0, T3);
212 
213       // now flush/compact
214       region.flush(true);
215       region.compact(true);
216 
217       // try again
218       g = new Get(T1);
219       g.setMaxVersions();
220       r = region.get(g);  // this'll use ScanWildcardColumnTracker
221       checkResult(r, c0, T3);
222 
223       g = new Get(T1);
224       g.setMaxVersions();
225       g.addColumn(c0, c0);
226       r = region.get(g);  // this'll use ExplicitColumnTracker
227       checkResult(r, c0, T3);
228     } finally {
229       HRegion.closeHRegion(region);
230     }
231   }
232 
233   /**
234    * Make sure the memstor behaves correctly with minimum versions
235    */
236   @Test
237   public void testMemStore() throws Exception {
238     HTableDescriptor htd =
239         hbu.createTableDescriptor(name.getMethodName(), 2, 1000, 1, KeepDeletedCells.FALSE);
240     HRegion region = hbu.createLocalHRegion(htd, null, null);
241 
242     // 2s in the past
243     long ts = EnvironmentEdgeManager.currentTime() - 2000;
244 
245     try {
246       // 2nd version
247       Put p = new Put(T1, ts-2);
248       p.add(c0, c0, T2);
249       region.put(p);
250 
251       // 3rd version
252       p = new Put(T1, ts-1);
253       p.add(c0, c0, T3);
254       region.put(p);
255 
256       // 4th version
257       p = new Put(T1, ts);
258       p.add(c0, c0, T4);
259       region.put(p);
260 
261       // now flush/compact
262       region.flush(true);
263       region.compact(true);
264 
265       // now put the first version (backdated)
266       p = new Put(T1, ts-3);
267       p.add(c0, c0, T1);
268       region.put(p);
269 
270       // now the latest change is in the memstore,
271       // but it is not the latest version
272 
273       Result r = region.get(new Get(T1));
274       checkResult(r, c0, T4);
275 
276       Get g = new Get(T1);
277       g.setMaxVersions();
278       r = region.get(g); // this'll use ScanWildcardColumnTracker
279       checkResult(r, c0, T4,T3);
280 
281       g = new Get(T1);
282       g.setMaxVersions();
283       g.addColumn(c0, c0);
284       r = region.get(g);  // this'll use ExplicitColumnTracker
285       checkResult(r, c0, T4,T3);
286 
287       p = new Put(T1, ts+1);
288       p.add(c0, c0, T5);
289       region.put(p);
290 
291       // now the latest version is in the memstore
292 
293       g = new Get(T1);
294       g.setMaxVersions();
295       r = region.get(g);  // this'll use ScanWildcardColumnTracker
296       checkResult(r, c0, T5,T4);
297 
298       g = new Get(T1);
299       g.setMaxVersions();
300       g.addColumn(c0, c0);
301       r = region.get(g);  // this'll use ExplicitColumnTracker
302       checkResult(r, c0, T5,T4);
303     } finally {
304       HRegion.closeHRegion(region);
305     }
306   }
307 
308   /**
309    * Verify basic minimum versions functionality
310    */
311   @Test
312   public void testBaseCase() throws Exception {
313     // 1 version minimum, 1000 versions maximum, ttl = 1s
314     HTableDescriptor htd =
315         hbu.createTableDescriptor(name.getMethodName(), 2, 1000, 1, KeepDeletedCells.FALSE);
316     HRegion region = hbu.createLocalHRegion(htd, null, null);
317     try {
318 
319       // 2s in the past
320       long ts = EnvironmentEdgeManager.currentTime() - 2000;
321 
322        // 1st version
323       Put p = new Put(T1, ts-3);
324       p.add(c0, c0, T1);
325       region.put(p);
326 
327       // 2nd version
328       p = new Put(T1, ts-2);
329       p.add(c0, c0, T2);
330       region.put(p);
331 
332       // 3rd version
333       p = new Put(T1, ts-1);
334       p.add(c0, c0, T3);
335       region.put(p);
336 
337       // 4th version
338       p = new Put(T1, ts);
339       p.add(c0, c0, T4);
340       region.put(p);
341 
342       Result r = region.get(new Get(T1));
343       checkResult(r, c0, T4);
344 
345       Get g = new Get(T1);
346       g.setTimeRange(0L, ts+1);
347       r = region.get(g);
348       checkResult(r, c0, T4);
349 
350   // oldest version still exists
351       g.setTimeRange(0L, ts-2);
352       r = region.get(g);
353       checkResult(r, c0, T1);
354 
355       // gets see only available versions
356       // even before compactions
357       g = new Get(T1);
358       g.setMaxVersions();
359       r = region.get(g); // this'll use ScanWildcardColumnTracker
360       checkResult(r, c0, T4,T3);
361 
362       g = new Get(T1);
363       g.setMaxVersions();
364       g.addColumn(c0, c0);
365       r = region.get(g);  // this'll use ExplicitColumnTracker
366       checkResult(r, c0, T4,T3);
367 
368       // now flush
369       region.flush(true);
370 
371       // with HBASE-4241 a flush will eliminate the expired rows
372       g = new Get(T1);
373       g.setTimeRange(0L, ts-2);
374       r = region.get(g);
375       assertTrue(r.isEmpty());
376 
377       // major compaction
378       region.compact(true);
379 
380       // after compaction the 4th version is still available
381       g = new Get(T1);
382       g.setTimeRange(0L, ts+1);
383       r = region.get(g);
384       checkResult(r, c0, T4);
385 
386       // so is the 3rd
387       g.setTimeRange(0L, ts);
388       r = region.get(g);
389       checkResult(r, c0, T3);
390 
391       // but the 2nd and earlier versions are gone
392       g.setTimeRange(0L, ts-1);
393       r = region.get(g);
394       assertTrue(r.isEmpty());
395     } finally {
396       HRegion.closeHRegion(region);
397     }
398   }
399 
400   /**
401    * Verify that basic filters still behave correctly with
402    * minimum versions enabled.
403    */
404   @Test
405   public void testFilters() throws Exception {
406     HTableDescriptor htd =
407         hbu.createTableDescriptor(name.getMethodName(), 2, 1000, 1, KeepDeletedCells.FALSE);
408     HRegion region = hbu.createLocalHRegion(htd, null, null);
409     final byte [] c1 = COLUMNS[1];
410 
411     // 2s in the past
412     long ts = EnvironmentEdgeManager.currentTime() - 2000;
413     try {
414 
415       Put p = new Put(T1, ts-3);
416       p.add(c0, c0, T0);
417       p.add(c1, c1, T0);
418       region.put(p);
419 
420       p = new Put(T1, ts-2);
421       p.add(c0, c0, T1);
422       p.add(c1, c1, T1);
423       region.put(p);
424 
425       p = new Put(T1, ts-1);
426       p.add(c0, c0, T2);
427       p.add(c1, c1, T2);
428       region.put(p);
429 
430       p = new Put(T1, ts);
431       p.add(c0, c0, T3);
432       p.add(c1, c1, T3);
433       region.put(p);
434 
435       List<Long> tss = new ArrayList<Long>();
436       tss.add(ts-1);
437       tss.add(ts-2);
438 
439       Get g = new Get(T1);
440       g.addColumn(c1,c1);
441       g.setFilter(new TimestampsFilter(tss));
442       g.setMaxVersions();
443       Result r = region.get(g);
444       checkResult(r, c1, T2,T1);
445 
446       g = new Get(T1);
447       g.addColumn(c0,c0);
448       g.setFilter(new TimestampsFilter(tss));
449       g.setMaxVersions();
450       r = region.get(g);
451       checkResult(r, c0, T2,T1);
452 
453       // now flush/compact
454       region.flush(true);
455       region.compact(true);
456 
457       g = new Get(T1);
458       g.addColumn(c1,c1);
459       g.setFilter(new TimestampsFilter(tss));
460       g.setMaxVersions();
461       r = region.get(g);
462       checkResult(r, c1, T2);
463 
464       g = new Get(T1);
465       g.addColumn(c0,c0);
466       g.setFilter(new TimestampsFilter(tss));
467       g.setMaxVersions();
468       r = region.get(g);
469       checkResult(r, c0, T2);
470     } finally {
471       HRegion.closeHRegion(region);
472     }
473   }
474 
475   @Test
476   public void testMinVersionsWithKeepDeletedCellsTTL() throws Exception {
477     int ttl = 4;
478     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
479     for (byte[] cfName : new byte[][]{fam1, fam2, fam3}) {
480       htd.addFamily(new HColumnDescriptor(cfName)
481         .setVersionsWithTimeToLive(ttl, 2));
482     }
483 
484     HRegion region = hbu.createLocalHRegion(htd, null, null);
485 
486     long startTS = EnvironmentEdgeManager.currentTime();
487     ManualEnvironmentEdge injectEdge = new ManualEnvironmentEdge();
488     injectEdge.setValue(startTS);
489     EnvironmentEdgeManager.injectEdge(injectEdge);
490 
491     long ts = startTS - 2000;
492     // 1st version
493     Put p = new Put(T1, ts-3);
494     p.addColumn(c0, c0, T1);
495     region.put(p);
496 
497     // 2nd version
498     p = new Put(T1, ts-2);
499     p.addColumn(c0, c0, T2);
500     region.put(p);
501 
502     // 3rd version
503     p = new Put(T1, ts-1);
504     p.addColumn(c0, c0, T3);
505     region.put(p);
506 
507     Get g;
508     Result r;
509 
510     //check we can still see all versions before compaction
511     g = new Get(T1);
512     g.setMaxVersions();
513     g.setTimeRange(0, ts);
514     r = region.get(g);
515     checkResult(r, c0, T3, T2, T1);
516 
517     region.flush(true);
518     region.compact(true);
519     assertEquals(startTS, EnvironmentEdgeManager.currentTime());
520     long expiredTime = EnvironmentEdgeManager.currentTime() - ts - 3;
521     assertTrue("TTL for T1 has expired", expiredTime < (ttl * 1000));
522     //check that nothing was purged yet
523     g = new Get(T1);
524     g.setMaxVersions();
525     g.setTimeRange(0, ts);
526     r = region.get(g);
527     checkResult(r, c0, T3, T2, T1);
528 
529     g = new Get(T1);
530     g.setMaxVersions();
531     g.setTimeRange(0, ts -1);
532     r = region.get(g);
533     checkResult(r, c0, T2, T1);
534 
535     injectEdge.incValue(ttl * 1000);
536 
537     region.flush(true);
538     region.compact(true);
539 
540     //check that after compaction (which is after TTL) that only T1 was purged
541     g = new Get(T1);
542     g.setMaxVersions();
543     g.setTimeRange(0, ts);
544     r = region.get(g);
545     checkResult(r, c0, T3, T2);
546 
547     g = new Get(T1);
548     g.setMaxVersions();
549     g.setTimeStamp(ts -2);
550     r = region.get(g);
551     checkResult(r, c0, T2);
552   }
553 
554 
555   private void checkResult(Result r, byte[] col, byte[] ... vals) {
556     assertEquals(r.size(), vals.length);
557     List<Cell> kvs = r.getColumnCells(col, col);
558     assertEquals(kvs.size(), vals.length);
559     for (int i=0;i<vals.length;i++) {
560       String expected = Bytes.toString(vals[i]);
561       String actual = Bytes.toString(CellUtil.cloneValue(kvs.get(i)));
562       assertTrue(expected + " was expected but doesn't match " + actual,
563         CellUtil.matchingValue(kvs.get(i), vals[i]));
564     }
565   }
566 
567 }
568