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