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