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; 019 020import static org.apache.hadoop.hbase.HBaseTestingUtility.COLUMNS; 021import static org.junit.Assert.assertArrayEquals; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertFalse; 024import static org.junit.Assert.assertTrue; 025import static org.junit.Assert.fail; 026 027import java.io.IOException; 028import java.util.ArrayList; 029import java.util.List; 030import org.apache.hadoop.hbase.Cell; 031import org.apache.hadoop.hbase.CellUtil; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HBaseTestingUtility; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.HTableDescriptor; 036import org.apache.hadoop.hbase.KeepDeletedCells; 037import org.apache.hadoop.hbase.PrivateCellUtil; 038import org.apache.hadoop.hbase.client.Delete; 039import org.apache.hadoop.hbase.client.Get; 040import org.apache.hadoop.hbase.client.Put; 041import org.apache.hadoop.hbase.client.Result; 042import org.apache.hadoop.hbase.client.Scan; 043import org.apache.hadoop.hbase.testclassification.RegionServerTests; 044import org.apache.hadoop.hbase.testclassification.SmallTests; 045import org.apache.hadoop.hbase.util.Bytes; 046import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 047import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper; 048import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge; 049import org.junit.After; 050import org.junit.Before; 051import org.junit.ClassRule; 052import org.junit.Rule; 053import org.junit.Test; 054import org.junit.experimental.categories.Category; 055import org.junit.rules.TestName; 056 057@Category({RegionServerTests.class, SmallTests.class}) 058public class TestKeepDeletes { 059 060 @ClassRule 061 public static final HBaseClassTestRule CLASS_RULE = 062 HBaseClassTestRule.forClass(TestKeepDeletes.class); 063 064 HBaseTestingUtility hbu = HBaseTestingUtility.createLocalHTU(); 065 private final byte[] T0 = Bytes.toBytes("0"); 066 private final byte[] T1 = Bytes.toBytes("1"); 067 private final byte[] T2 = Bytes.toBytes("2"); 068 private final byte[] T3 = Bytes.toBytes("3"); 069 private final byte[] T4 = Bytes.toBytes("4"); 070 private final byte[] T5 = Bytes.toBytes("5"); 071 private final byte[] T6 = Bytes.toBytes("6"); 072 073 private final byte[] c0 = COLUMNS[0]; 074 private final byte[] c1 = COLUMNS[1]; 075 076 @Rule public TestName name = new TestName(); 077 078 @Before 079 public void setUp() throws Exception { 080 /* HBASE-6832: [WINDOWS] Tests should use explicit timestamp for Puts, and not rely on 081 * implicit RS timing. 082 * Use an explicit timer (IncrementingEnvironmentEdge) so that the put, delete 083 * compact timestamps are tracked. Otherwise, forced major compaction will not purge 084 * Delete's having the same timestamp. see ScanQueryMatcher.match(): 085 * if (retainDeletesInOutput 086 * || (!isUserScan && (EnvironmentEdgeManager.currentTime() - timestamp) 087 * <= timeToPurgeDeletes) ... ) 088 * 089 */ 090 EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge()); 091 } 092 093 @After 094 public void tearDown() throws Exception { 095 EnvironmentEdgeManager.reset(); 096 } 097 098 /** 099 * Make sure that deleted rows are retained. 100 * Family delete markers are deleted. 101 * Column Delete markers are versioned 102 * Time range scan of deleted rows are possible 103 */ 104 @Test 105 public void testBasicScenario() throws Exception { 106 // keep 3 versions, rows do not expire 107 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 108 HConstants.FOREVER, KeepDeletedCells.TRUE); 109 HRegion region = hbu.createLocalHRegion(htd, null, null); 110 111 long ts = EnvironmentEdgeManager.currentTime(); 112 Put p = new Put(T1, ts); 113 p.addColumn(c0, c0, T1); 114 region.put(p); 115 p = new Put(T1, ts+1); 116 p.addColumn(c0, c0, T2); 117 region.put(p); 118 p = new Put(T1, ts+2); 119 p.addColumn(c0, c0, T3); 120 region.put(p); 121 p = new Put(T1, ts+4); 122 p.addColumn(c0, c0, T4); 123 region.put(p); 124 125 // now place a delete marker at ts+2 126 Delete d = new Delete(T1, ts+2); 127 region.delete(d); 128 129 // a raw scan can see the delete markers 130 // (one for each column family) 131 assertEquals(3, countDeleteMarkers(region)); 132 133 // get something *before* the delete marker 134 Get g = new Get(T1); 135 g.setMaxVersions(); 136 g.setTimeRange(0L, ts+2); 137 Result r = region.get(g); 138 checkResult(r, c0, c0, T2,T1); 139 140 // flush 141 region.flush(true); 142 143 // yep, T2 still there, T1 gone 144 r = region.get(g); 145 checkResult(r, c0, c0, T2); 146 147 // major compact 148 region.compact(true); 149 region.compact(true); 150 151 // one delete marker left (the others did not 152 // have older puts) 153 assertEquals(1, countDeleteMarkers(region)); 154 155 // still there (even after multiple compactions) 156 r = region.get(g); 157 checkResult(r, c0, c0, T2); 158 159 // a timerange that includes the delete marker won't see past rows 160 g.setTimeRange(0L, ts+4); 161 r = region.get(g); 162 assertTrue(r.isEmpty()); 163 164 // two more puts, this will expire the older puts. 165 p = new Put(T1, ts+5); 166 p.addColumn(c0, c0, T5); 167 region.put(p); 168 p = new Put(T1, ts+6); 169 p.addColumn(c0, c0, T6); 170 region.put(p); 171 172 // also add an old put again 173 // (which is past the max versions) 174 p = new Put(T1, ts); 175 p.addColumn(c0, c0, T1); 176 region.put(p); 177 r = region.get(g); 178 assertTrue(r.isEmpty()); 179 180 region.flush(true); 181 region.compact(true); 182 region.compact(true); 183 184 // verify that the delete marker itself was collected 185 region.put(p); 186 r = region.get(g); 187 checkResult(r, c0, c0, T1); 188 assertEquals(0, countDeleteMarkers(region)); 189 190 HBaseTestingUtility.closeRegionAndWAL(region); 191 } 192 193 /** 194 * Even when the store does not keep deletes a "raw" scan will 195 * return everything it can find (unless discarding cells is guaranteed 196 * to have no effect). 197 * Assuming this the desired behavior. Could also disallow "raw" scanning 198 * if the store does not have KEEP_DELETED_CELLS enabled. 199 * (can be changed easily) 200 */ 201 @Test 202 public void testRawScanWithoutKeepingDeletes() throws Exception { 203 // KEEP_DELETED_CELLS is NOT enabled 204 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 205 HConstants.FOREVER, KeepDeletedCells.FALSE); 206 HRegion region = hbu.createLocalHRegion(htd, null, null); 207 208 long ts = EnvironmentEdgeManager.currentTime(); 209 Put p = new Put(T1, ts); 210 p.addColumn(c0, c0, T1); 211 region.put(p); 212 213 Delete d = new Delete(T1, ts); 214 d.addColumn(c0, c0, ts); 215 region.delete(d); 216 217 // scan still returns delete markers and deletes rows 218 Scan s = new Scan(); 219 s.setRaw(true); 220 s.setMaxVersions(); 221 InternalScanner scan = region.getScanner(s); 222 List<Cell> kvs = new ArrayList<>(); 223 scan.next(kvs); 224 assertEquals(2, kvs.size()); 225 226 region.flush(true); 227 region.compact(true); 228 229 // after compaction they are gone 230 // (note that this a test with a Store without 231 // KEEP_DELETED_CELLS) 232 s = new Scan(); 233 s.setRaw(true); 234 s.setMaxVersions(); 235 scan = region.getScanner(s); 236 kvs = new ArrayList<>(); 237 scan.next(kvs); 238 assertTrue(kvs.isEmpty()); 239 240 HBaseTestingUtility.closeRegionAndWAL(region); 241 } 242 243 /** 244 * basic verification of existing behavior 245 */ 246 @Test 247 public void testWithoutKeepingDeletes() throws Exception { 248 // KEEP_DELETED_CELLS is NOT enabled 249 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 250 HConstants.FOREVER, KeepDeletedCells.FALSE); 251 HRegion region = hbu.createLocalHRegion(htd, null, null); 252 253 long ts = EnvironmentEdgeManager.currentTime(); 254 Put p = new Put(T1, ts); 255 p.addColumn(c0, c0, T1); 256 region.put(p); 257 258 Get gOne = new Get(T1); 259 gOne.setMaxVersions(); 260 gOne.setTimeRange(0L, ts + 1); 261 Result rOne = region.get(gOne); 262 assertFalse(rOne.isEmpty()); 263 264 265 Delete d = new Delete(T1, ts+2); 266 d.addColumn(c0, c0, ts); 267 region.delete(d); 268 269 // "past" get does not see rows behind delete marker 270 Get g = new Get(T1); 271 g.setMaxVersions(); 272 g.setTimeRange(0L, ts+1); 273 Result r = region.get(g); 274 assertTrue(r.isEmpty()); 275 276 // "past" scan does not see rows behind delete marker 277 Scan s = new Scan(); 278 s.setMaxVersions(); 279 s.setTimeRange(0L, ts+1); 280 InternalScanner scanner = region.getScanner(s); 281 List<Cell> kvs = new ArrayList<>(); 282 while (scanner.next(kvs)) 283 ; 284 assertTrue(kvs.isEmpty()); 285 286 // flushing and minor compaction keep delete markers 287 region.flush(true); 288 region.compact(false); 289 assertEquals(1, countDeleteMarkers(region)); 290 region.compact(true); 291 // major compaction deleted it 292 assertEquals(0, countDeleteMarkers(region)); 293 294 HBaseTestingUtility.closeRegionAndWAL(region); 295 } 296 297 /** 298 * The ExplicitColumnTracker does not support "raw" scanning. 299 */ 300 @Test 301 public void testRawScanWithColumns() throws Exception { 302 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 303 HConstants.FOREVER, KeepDeletedCells.TRUE); 304 Region region = hbu.createLocalHRegion(htd, null, null); 305 306 Scan s = new Scan(); 307 s.setRaw(true); 308 s.setMaxVersions(); 309 s.addColumn(c0, c0); 310 311 try { 312 region.getScanner(s); 313 fail("raw scanner with columns should have failed"); 314 } catch (org.apache.hadoop.hbase.DoNotRetryIOException dnre) { 315 // ok! 316 } 317 318 HBaseTestingUtility.closeRegionAndWAL(region); 319 } 320 321 /** 322 * Verify that "raw" scanning mode return delete markers and deletes rows. 323 */ 324 @Test 325 public void testRawScan() throws Exception { 326 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 327 HConstants.FOREVER, KeepDeletedCells.TRUE); 328 Region region = hbu.createLocalHRegion(htd, null, null); 329 330 long ts = EnvironmentEdgeManager.currentTime(); 331 Put p = new Put(T1, ts); 332 p.addColumn(c0, c0, T1); 333 region.put(p); 334 p = new Put(T1, ts+2); 335 p.addColumn(c0, c0, T2); 336 region.put(p); 337 p = new Put(T1, ts+4); 338 p.addColumn(c0, c0, T3); 339 region.put(p); 340 341 Delete d = new Delete(T1, ts+1); 342 region.delete(d); 343 344 d = new Delete(T1, ts+2); 345 d.addColumn(c0, c0, ts+2); 346 region.delete(d); 347 348 d = new Delete(T1, ts+3); 349 d.addColumns(c0, c0, ts+3); 350 region.delete(d); 351 352 Scan s = new Scan(); 353 s.setRaw(true); 354 s.setMaxVersions(); 355 InternalScanner scan = region.getScanner(s); 356 List<Cell> kvs = new ArrayList<>(); 357 scan.next(kvs); 358 assertEquals(8, kvs.size()); 359 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(0))); 360 assertArrayEquals(CellUtil.cloneValue(kvs.get(1)), T3); 361 assertTrue(CellUtil.isDelete(kvs.get(2))); 362 assertTrue(CellUtil.isDelete(kvs.get(3))); // .isDeleteType()); 363 assertArrayEquals(CellUtil.cloneValue(kvs.get(4)), T2); 364 assertArrayEquals(CellUtil.cloneValue(kvs.get(5)), T1); 365 // we have 3 CFs, so there are two more delete markers 366 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(6))); 367 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(7))); 368 369 // verify that raw scans honor the passed timerange 370 s = new Scan(); 371 s.setRaw(true); 372 s.setMaxVersions(); 373 s.setTimeRange(0, 1); 374 scan = region.getScanner(s); 375 kvs = new ArrayList<>(); 376 scan.next(kvs); 377 // nothing in this interval, not even delete markers 378 assertTrue(kvs.isEmpty()); 379 380 // filter new delete markers 381 s = new Scan(); 382 s.setRaw(true); 383 s.setMaxVersions(); 384 s.setTimeRange(0, ts+2); 385 scan = region.getScanner(s); 386 kvs = new ArrayList<>(); 387 scan.next(kvs); 388 assertEquals(4, kvs.size()); 389 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(0))); 390 assertArrayEquals(CellUtil.cloneValue(kvs.get(1)), T1); 391 // we have 3 CFs 392 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(2))); 393 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(3))); 394 395 // filter old delete markers 396 s = new Scan(); 397 s.setRaw(true); 398 s.setMaxVersions(); 399 s.setTimeRange(ts+3, ts+5); 400 scan = region.getScanner(s); 401 kvs = new ArrayList<>(); 402 scan.next(kvs); 403 assertEquals(2, kvs.size()); 404 assertArrayEquals(CellUtil.cloneValue(kvs.get(0)), T3); 405 assertTrue(CellUtil.isDelete(kvs.get(1))); 406 407 408 HBaseTestingUtility.closeRegionAndWAL(region); 409 } 410 411 /** 412 * Verify that delete markers are removed from an otherwise empty store. 413 */ 414 @Test 415 public void testDeleteMarkerExpirationEmptyStore() throws Exception { 416 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 417 HConstants.FOREVER, KeepDeletedCells.TRUE); 418 HRegion region = hbu.createLocalHRegion(htd, null, null); 419 420 long ts = EnvironmentEdgeManager.currentTime(); 421 422 Delete d = new Delete(T1, ts); 423 d.addColumns(c0, c0, ts); 424 region.delete(d); 425 426 d = new Delete(T1, ts); 427 d.addFamily(c0); 428 region.delete(d); 429 430 d = new Delete(T1, ts); 431 d.addColumn(c0, c0, ts+1); 432 region.delete(d); 433 434 d = new Delete(T1, ts); 435 d.addColumn(c0, c0, ts+2); 436 region.delete(d); 437 438 // 1 family marker, 1 column marker, 2 version markers 439 assertEquals(4, countDeleteMarkers(region)); 440 441 // neither flush nor minor compaction removes any marker 442 region.flush(true); 443 assertEquals(4, countDeleteMarkers(region)); 444 region.compact(false); 445 assertEquals(4, countDeleteMarkers(region)); 446 447 // major compaction removes all, since there are no puts they affect 448 region.compact(true); 449 assertEquals(0, countDeleteMarkers(region)); 450 451 HBaseTestingUtility.closeRegionAndWAL(region); 452 } 453 454 /** 455 * Test delete marker removal from store files. 456 */ 457 @Test 458 public void testDeleteMarkerExpiration() throws Exception { 459 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 460 HConstants.FOREVER, KeepDeletedCells.TRUE); 461 HRegion region = hbu.createLocalHRegion(htd, null, null); 462 463 long ts = EnvironmentEdgeManager.currentTime(); 464 465 Put p = new Put(T1, ts); 466 p.addColumn(c0, c0, T1); 467 region.put(p); 468 469 // a put into another store (CF) should have no effect 470 p = new Put(T1, ts-10); 471 p.addColumn(c1, c0, T1); 472 region.put(p); 473 474 // all the following deletes affect the put 475 Delete d = new Delete(T1, ts); 476 d.addColumns(c0, c0, ts); 477 region.delete(d); 478 479 d = new Delete(T1, ts); 480 d.addFamily(c0, ts); 481 region.delete(d); 482 483 d = new Delete(T1, ts); 484 d.addColumn(c0, c0, ts+1); 485 region.delete(d); 486 487 d = new Delete(T1, ts); 488 d.addColumn(c0, c0, ts+2); 489 region.delete(d); 490 491 // 1 family marker, 1 column marker, 2 version markers 492 assertEquals(4, countDeleteMarkers(region)); 493 494 region.flush(true); 495 assertEquals(4, countDeleteMarkers(region)); 496 region.compact(false); 497 assertEquals(4, countDeleteMarkers(region)); 498 499 // another put will push out the earlier put... 500 p = new Put(T1, ts+3); 501 p.addColumn(c0, c0, T1); 502 region.put(p); 503 504 region.flush(true); 505 // no markers are collected, since there is an affected put 506 region.compact(true); 507 assertEquals(4, countDeleteMarkers(region)); 508 509 // the last collections collected the earlier put 510 // so after this collection all markers 511 region.compact(true); 512 assertEquals(0, countDeleteMarkers(region)); 513 514 HBaseTestingUtility.closeRegionAndWAL(region); 515 } 516 517 /** 518 * Test delete marker removal from store files. 519 */ 520 @Test 521 public void testWithOldRow() throws Exception { 522 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 523 HConstants.FOREVER, KeepDeletedCells.TRUE); 524 HRegion region = hbu.createLocalHRegion(htd, null, null); 525 526 long ts = EnvironmentEdgeManager.currentTime(); 527 528 Put p = new Put(T1, ts); 529 p.addColumn(c0, c0, T1); 530 region.put(p); 531 532 // a put another (older) row in the same store 533 p = new Put(T2, ts-10); 534 p.addColumn(c0, c0, T1); 535 region.put(p); 536 537 // all the following deletes affect the put 538 Delete d = new Delete(T1, ts); 539 d.addColumns(c0, c0, ts); 540 region.delete(d); 541 542 d = new Delete(T1, ts); 543 d.addFamily(c0, ts); 544 region.delete(d); 545 546 d = new Delete(T1, ts); 547 d.addColumn(c0, c0, ts+1); 548 region.delete(d); 549 550 d = new Delete(T1, ts); 551 d.addColumn(c0, c0, ts+2); 552 region.delete(d); 553 554 // 1 family marker, 1 column marker, 2 version markers 555 assertEquals(4, countDeleteMarkers(region)); 556 557 region.flush(true); 558 assertEquals(4, countDeleteMarkers(region)); 559 region.compact(false); 560 assertEquals(4, countDeleteMarkers(region)); 561 562 // another put will push out the earlier put... 563 p = new Put(T1, ts+3); 564 p.addColumn(c0, c0, T1); 565 region.put(p); 566 567 region.flush(true); 568 // no markers are collected, since there is an affected put 569 region.compact(true); 570 assertEquals(4, countDeleteMarkers(region)); 571 572 // all markers remain, since we have the older row 573 // and we haven't pushed the inlined markers past MAX_VERSIONS 574 region.compact(true); 575 assertEquals(4, countDeleteMarkers(region)); 576 577 // another put will push out the earlier put... 578 p = new Put(T1, ts+4); 579 p.addColumn(c0, c0, T1); 580 region.put(p); 581 582 // this pushed out the column and version marker 583 // but the family markers remains. THIS IS A PROBLEM! 584 region.compact(true); 585 assertEquals(1, countDeleteMarkers(region)); 586 587 // no amount of compacting is getting this of this one 588 // KEEP_DELETED_CELLS=>TTL is an option to avoid this. 589 region.compact(true); 590 assertEquals(1, countDeleteMarkers(region)); 591 592 HBaseTestingUtility.closeRegionAndWAL(region); 593 } 594 595 /** 596 * Verify correct range demarcation 597 */ 598 @Test 599 public void testRanges() throws Exception { 600 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 601 HConstants.FOREVER, KeepDeletedCells.TRUE); 602 Region region = hbu.createLocalHRegion(htd, null, null); 603 604 long ts = EnvironmentEdgeManager.currentTime(); 605 Put p = new Put(T1, ts); 606 p.addColumn(c0, c0, T1); 607 p.addColumn(c0, c1, T1); 608 p.addColumn(c1, c0, T1); 609 p.addColumn(c1, c1, T1); 610 region.put(p); 611 612 p = new Put(T2, ts); 613 p.addColumn(c0, c0, T1); 614 p.addColumn(c0, c1, T1); 615 p.addColumn(c1, c0, T1); 616 p.addColumn(c1, c1, T1); 617 region.put(p); 618 619 p = new Put(T1, ts+1); 620 p.addColumn(c0, c0, T2); 621 p.addColumn(c0, c1, T2); 622 p.addColumn(c1, c0, T2); 623 p.addColumn(c1, c1, T2); 624 region.put(p); 625 626 p = new Put(T2, ts+1); 627 p.addColumn(c0, c0, T2); 628 p.addColumn(c0, c1, T2); 629 p.addColumn(c1, c0, T2); 630 p.addColumn(c1, c1, T2); 631 region.put(p); 632 633 Delete d = new Delete(T1, ts+2); 634 d.addColumns(c0, c0, ts+2); 635 region.delete(d); 636 637 d = new Delete(T1, ts+2); 638 d.addFamily(c1, ts+2); 639 region.delete(d); 640 641 d = new Delete(T2, ts+2); 642 d.addFamily(c0, ts+2); 643 region.delete(d); 644 645 // add an older delete, to make sure it is filtered 646 d = new Delete(T1, ts-10); 647 d.addFamily(c1, ts-10); 648 region.delete(d); 649 650 // ts + 2 does NOT include the delete at ts+2 651 checkGet(region, T1, c0, c0, ts+2, T2, T1); 652 checkGet(region, T1, c0, c1, ts+2, T2, T1); 653 checkGet(region, T1, c1, c0, ts+2, T2, T1); 654 checkGet(region, T1, c1, c1, ts+2, T2, T1); 655 656 checkGet(region, T2, c0, c0, ts+2, T2, T1); 657 checkGet(region, T2, c0, c1, ts+2, T2, T1); 658 checkGet(region, T2, c1, c0, ts+2, T2, T1); 659 checkGet(region, T2, c1, c1, ts+2, T2, T1); 660 661 // ts + 3 does 662 checkGet(region, T1, c0, c0, ts+3); 663 checkGet(region, T1, c0, c1, ts+3, T2, T1); 664 checkGet(region, T1, c1, c0, ts+3); 665 checkGet(region, T1, c1, c1, ts+3); 666 667 checkGet(region, T2, c0, c0, ts+3); 668 checkGet(region, T2, c0, c1, ts+3); 669 checkGet(region, T2, c1, c0, ts+3, T2, T1); 670 checkGet(region, T2, c1, c1, ts+3, T2, T1); 671 672 HBaseTestingUtility.closeRegionAndWAL(region); 673 } 674 675 /** 676 * Verify that column/version delete makers are sorted 677 * with their respective puts and removed correctly by 678 * versioning (i.e. not relying on the store earliestPutTS). 679 */ 680 @Test 681 public void testDeleteMarkerVersioning() throws Exception { 682 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 683 HConstants.FOREVER, KeepDeletedCells.TRUE); 684 HRegion region = hbu.createLocalHRegion(htd, null, null); 685 686 long ts = EnvironmentEdgeManager.currentTime(); 687 Put p = new Put(T1, ts); 688 p.addColumn(c0, c0, T1); 689 region.put(p); 690 691 // this prevents marker collection based on earliestPut 692 // (cannot keep earliest put per column in the store file) 693 p = new Put(T1, ts-10); 694 p.addColumn(c0, c1, T1); 695 region.put(p); 696 697 Delete d = new Delete(T1, ts); 698 // test corner case (Put and Delete have same TS) 699 d.addColumns(c0, c0, ts); 700 region.delete(d); 701 702 d = new Delete(T1, ts+1); 703 d.addColumn(c0, c0, ts+1); 704 region.delete(d); 705 706 d = new Delete(T1, ts+3); 707 d.addColumn(c0, c0, ts+3); 708 region.delete(d); 709 710 region.flush(true); 711 region.compact(true); 712 region.compact(true); 713 assertEquals(3, countDeleteMarkers(region)); 714 715 // add two more puts, since max version is 1 716 // the 2nd put (and all delete markers following) 717 // will be removed. 718 p = new Put(T1, ts+2); 719 p.addColumn(c0, c0, T2); 720 region.put(p); 721 722 // delete, put, delete, delete, put 723 assertEquals(3, countDeleteMarkers(region)); 724 725 p = new Put(T1, ts+3); 726 p.addColumn(c0, c0, T3); 727 region.put(p); 728 729 // This is potentially questionable behavior. 730 // This could be changed by not letting the ScanQueryMatcher 731 // return SEEK_NEXT_COL if a put is past VERSIONS, but instead 732 // return SKIP if the store has KEEP_DELETED_CELLS set. 733 // 734 // As it stands, the 1 here is correct here. 735 // There are two puts, VERSIONS is one, so after the 1st put the scanner 736 // knows that there can be no more KVs (put or delete) that have any effect. 737 // 738 // delete, put, put | delete, delete 739 assertEquals(1, countDeleteMarkers(region)); 740 741 // flush cache only sees what is in the memstore 742 region.flush(true); 743 744 // Here we have the three markers again, because the flush above 745 // removed the 2nd put before the file is written. 746 // So there's only one put, and hence the deletes already in the store 747 // files cannot be removed safely. 748 // delete, put, delete, delete 749 assertEquals(3, countDeleteMarkers(region)); 750 751 region.compact(true); 752 assertEquals(3, countDeleteMarkers(region)); 753 754 // add one more put 755 p = new Put(T1, ts+4); 756 p.addColumn(c0, c0, T4); 757 region.put(p); 758 759 region.flush(true); 760 // one trailing delete marker remains (but only one) 761 // because delete markers do not increase the version count 762 assertEquals(1, countDeleteMarkers(region)); 763 region.compact(true); 764 region.compact(true); 765 assertEquals(1, countDeleteMarkers(region)); 766 767 HBaseTestingUtility.closeRegionAndWAL(region); 768 } 769 770 /** 771 * Verify scenarios with multiple CFs and columns 772 */ 773 @Test 774 public void testWithMixedCFs() throws Exception { 775 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 776 HConstants.FOREVER, KeepDeletedCells.TRUE); 777 Region region = hbu.createLocalHRegion(htd, null, null); 778 779 long ts = EnvironmentEdgeManager.currentTime(); 780 781 Put p = new Put(T1, ts); 782 p.addColumn(c0, c0, T1); 783 p.addColumn(c0, c1, T1); 784 p.addColumn(c1, c0, T1); 785 p.addColumn(c1, c1, T1); 786 region.put(p); 787 788 p = new Put(T2, ts+1); 789 p.addColumn(c0, c0, T2); 790 p.addColumn(c0, c1, T2); 791 p.addColumn(c1, c0, T2); 792 p.addColumn(c1, c1, T2); 793 region.put(p); 794 795 // family markers are each family 796 Delete d = new Delete(T1, ts+1); 797 region.delete(d); 798 799 d = new Delete(T2, ts+2); 800 region.delete(d); 801 802 Scan s = new Scan(T1); 803 s.setTimeRange(0, ts+1); 804 InternalScanner scanner = region.getScanner(s); 805 List<Cell> kvs = new ArrayList<>(); 806 scanner.next(kvs); 807 assertEquals(4, kvs.size()); 808 scanner.close(); 809 810 s = new Scan(T2); 811 s.setTimeRange(0, ts+2); 812 scanner = region.getScanner(s); 813 kvs = new ArrayList<>(); 814 scanner.next(kvs); 815 assertEquals(4, kvs.size()); 816 scanner.close(); 817 818 HBaseTestingUtility.closeRegionAndWAL(region); 819 } 820 821 /** 822 * Test keeping deleted rows together with min versions set 823 * @throws Exception 824 */ 825 @Test 826 public void testWithMinVersions() throws Exception { 827 HTableDescriptor htd = 828 hbu.createTableDescriptor(name.getMethodName(), 3, 1000, 1, KeepDeletedCells.TRUE); 829 HRegion region = hbu.createLocalHRegion(htd, null, null); 830 831 long ts = EnvironmentEdgeManager.currentTime() - 2000; // 2s in the past 832 833 Put p = new Put(T1, ts); 834 p.addColumn(c0, c0, T3); 835 region.put(p); 836 p = new Put(T1, ts-1); 837 p.addColumn(c0, c0, T2); 838 region.put(p); 839 p = new Put(T1, ts-3); 840 p.addColumn(c0, c0, T1); 841 region.put(p); 842 p = new Put(T1, ts-4); 843 p.addColumn(c0, c0, T0); 844 region.put(p); 845 846 // all puts now are just retained because of min versions = 3 847 848 // place a family delete marker 849 Delete d = new Delete(T1, ts-1); 850 region.delete(d); 851 // and a column delete marker 852 d = new Delete(T1, ts-2); 853 d.addColumns(c0, c0, ts-1); 854 region.delete(d); 855 856 Get g = new Get(T1); 857 g.setMaxVersions(); 858 g.setTimeRange(0L, ts-2); 859 Result r = region.get(g); 860 checkResult(r, c0, c0, T1,T0); 861 862 // 3 families, one column delete marker 863 assertEquals(4, countDeleteMarkers(region)); 864 865 region.flush(true); 866 // no delete marker removes by the flush 867 assertEquals(4, countDeleteMarkers(region)); 868 869 r = region.get(g); 870 checkResult(r, c0, c0, T1); 871 p = new Put(T1, ts+1); 872 p.addColumn(c0, c0, T4); 873 region.put(p); 874 region.flush(true); 875 876 assertEquals(4, countDeleteMarkers(region)); 877 878 r = region.get(g); 879 checkResult(r, c0, c0, T1); 880 881 // this will push out the last put before 882 // family delete marker 883 p = new Put(T1, ts+2); 884 p.addColumn(c0, c0, T5); 885 region.put(p); 886 887 region.flush(true); 888 region.compact(true); 889 // the two family markers without puts are gone 890 assertEquals(2, countDeleteMarkers(region)); 891 892 // the last compactStores updated the earliestPutTs, 893 // so after the next compaction the last family delete marker is also gone 894 region.compact(true); 895 assertEquals(0, countDeleteMarkers(region)); 896 897 HBaseTestingUtility.closeRegionAndWAL(region); 898 } 899 900 /** 901 * Test keeping deleted rows together with min versions set 902 * @throws Exception 903 */ 904 @Test 905 public void testWithTTL() throws Exception { 906 HTableDescriptor htd = 907 hbu.createTableDescriptor(name.getMethodName(), 1, 1000, 1, KeepDeletedCells.TTL); 908 HRegion region = hbu.createLocalHRegion(htd, null, null); 909 910 long ts = EnvironmentEdgeManager.currentTime() - 2000; // 2s in the past 911 912 Put p = new Put(T1, ts); 913 p.addColumn(c0, c0, T3); 914 region.put(p); 915 916 // place an old row, to make the family marker expires anyway 917 p = new Put(T2, ts-10); 918 p.addColumn(c0, c0, T1); 919 region.put(p); 920 921 checkGet(region, T1, c0, c0, ts+1, T3); 922 // place a family delete marker 923 Delete d = new Delete(T1, ts+2); 924 region.delete(d); 925 926 checkGet(region, T1, c0, c0, ts+1, T3); 927 928 // 3 families, one column delete marker 929 assertEquals(3, countDeleteMarkers(region)); 930 931 region.flush(true); 932 // no delete marker removes by the flush 933 assertEquals(3, countDeleteMarkers(region)); 934 935 // but the Put is gone 936 checkGet(region, T1, c0, c0, ts+1); 937 938 region.compact(true); 939 // all delete marker gone 940 assertEquals(0, countDeleteMarkers(region)); 941 942 HBaseTestingUtility.closeRegionAndWAL(region); 943 } 944 945 private void checkGet(Region region, byte[] row, byte[] fam, byte[] col, 946 long time, byte[]... vals) throws IOException { 947 Get g = new Get(row); 948 g.addColumn(fam, col); 949 g.setMaxVersions(); 950 g.setTimeRange(0L, time); 951 Result r = region.get(g); 952 checkResult(r, fam, col, vals); 953 954 } 955 956 private int countDeleteMarkers(HRegion region) throws IOException { 957 Scan s = new Scan(); 958 s.setRaw(true); 959 // use max versions from the store(s) 960 s.setMaxVersions(region.getStores().iterator().next().getScanInfo().getMaxVersions()); 961 InternalScanner scan = region.getScanner(s); 962 List<Cell> kvs = new ArrayList<>(); 963 int res = 0; 964 boolean hasMore; 965 do { 966 hasMore = scan.next(kvs); 967 for (Cell kv : kvs) { 968 if(CellUtil.isDelete(kv)) res++; 969 } 970 kvs.clear(); 971 } while (hasMore); 972 scan.close(); 973 return res; 974 } 975 976 private void checkResult(Result r, byte[] fam, byte[] col, byte[] ... vals) { 977 assertEquals(r.size(), vals.length); 978 List<Cell> kvs = r.getColumnCells(fam, col); 979 assertEquals(kvs.size(), vals.length); 980 for (int i=0;i<vals.length;i++) { 981 assertArrayEquals(CellUtil.cloneValue(kvs.get(i)), vals[i]); 982 } 983 } 984 985 986} 987