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