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