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.apache.hadoop.hbase.HBaseTestingUtility.fam1;
022import static org.apache.hadoop.hbase.HBaseTestingUtility.fam2;
023import static org.apache.hadoop.hbase.HBaseTestingUtility.fam3;
024import static org.junit.Assert.assertArrayEquals;
025import static org.junit.Assert.assertEquals;
026import static org.junit.Assert.assertFalse;
027import static org.junit.Assert.assertNotNull;
028import static org.junit.Assert.assertNull;
029import static org.junit.Assert.assertTrue;
030import static org.junit.Assert.fail;
031import static org.mockito.ArgumentMatchers.any;
032import static org.mockito.ArgumentMatchers.anyBoolean;
033import static org.mockito.ArgumentMatchers.anyLong;
034import static org.mockito.Mockito.doThrow;
035import static org.mockito.Mockito.mock;
036import static org.mockito.Mockito.never;
037import static org.mockito.Mockito.spy;
038import static org.mockito.Mockito.times;
039import static org.mockito.Mockito.verify;
040import static org.mockito.Mockito.when;
041
042import java.io.IOException;
043import java.io.InterruptedIOException;
044import java.math.BigDecimal;
045import java.nio.charset.StandardCharsets;
046import java.security.PrivilegedExceptionAction;
047import java.util.ArrayList;
048import java.util.Arrays;
049import java.util.Collection;
050import java.util.List;
051import java.util.Map;
052import java.util.NavigableMap;
053import java.util.Objects;
054import java.util.TreeMap;
055import java.util.UUID;
056import java.util.concurrent.Callable;
057import java.util.concurrent.CountDownLatch;
058import java.util.concurrent.ExecutorService;
059import java.util.concurrent.Executors;
060import java.util.concurrent.Future;
061import java.util.concurrent.TimeUnit;
062import java.util.concurrent.atomic.AtomicBoolean;
063import java.util.concurrent.atomic.AtomicInteger;
064import java.util.concurrent.atomic.AtomicReference;
065import org.apache.commons.lang3.RandomStringUtils;
066import org.apache.hadoop.conf.Configuration;
067import org.apache.hadoop.fs.FSDataOutputStream;
068import org.apache.hadoop.fs.FileStatus;
069import org.apache.hadoop.fs.FileSystem;
070import org.apache.hadoop.fs.Path;
071import org.apache.hadoop.hbase.ArrayBackedTag;
072import org.apache.hadoop.hbase.Cell;
073import org.apache.hadoop.hbase.Cell.Type;
074import org.apache.hadoop.hbase.CellBuilderFactory;
075import org.apache.hadoop.hbase.CellBuilderType;
076import org.apache.hadoop.hbase.CellUtil;
077import org.apache.hadoop.hbase.CompareOperator;
078import org.apache.hadoop.hbase.CompatibilitySingletonFactory;
079import org.apache.hadoop.hbase.DroppedSnapshotException;
080import org.apache.hadoop.hbase.HBaseClassTestRule;
081import org.apache.hadoop.hbase.HBaseConfiguration;
082import org.apache.hadoop.hbase.HBaseTestingUtility;
083import org.apache.hadoop.hbase.HColumnDescriptor;
084import org.apache.hadoop.hbase.HConstants;
085import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
086import org.apache.hadoop.hbase.HDFSBlocksDistribution;
087import org.apache.hadoop.hbase.HRegionInfo;
088import org.apache.hadoop.hbase.HTableDescriptor;
089import org.apache.hadoop.hbase.KeyValue;
090import org.apache.hadoop.hbase.KeyValueUtil;
091import org.apache.hadoop.hbase.MiniHBaseCluster;
092import org.apache.hadoop.hbase.MultithreadedTestUtil;
093import org.apache.hadoop.hbase.MultithreadedTestUtil.RepeatingTestThread;
094import org.apache.hadoop.hbase.MultithreadedTestUtil.TestThread;
095import org.apache.hadoop.hbase.NotServingRegionException;
096import org.apache.hadoop.hbase.PrivateCellUtil;
097import org.apache.hadoop.hbase.RegionTooBusyException;
098import org.apache.hadoop.hbase.ServerName;
099import org.apache.hadoop.hbase.TableName;
100import org.apache.hadoop.hbase.TagType;
101import org.apache.hadoop.hbase.Waiter;
102import org.apache.hadoop.hbase.client.Append;
103import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
104import org.apache.hadoop.hbase.client.Delete;
105import org.apache.hadoop.hbase.client.Durability;
106import org.apache.hadoop.hbase.client.Get;
107import org.apache.hadoop.hbase.client.Increment;
108import org.apache.hadoop.hbase.client.Mutation;
109import org.apache.hadoop.hbase.client.Put;
110import org.apache.hadoop.hbase.client.RegionInfo;
111import org.apache.hadoop.hbase.client.RegionInfoBuilder;
112import org.apache.hadoop.hbase.client.Result;
113import org.apache.hadoop.hbase.client.RowMutations;
114import org.apache.hadoop.hbase.client.Scan;
115import org.apache.hadoop.hbase.client.Table;
116import org.apache.hadoop.hbase.client.TableDescriptor;
117import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
118import org.apache.hadoop.hbase.exceptions.FailedSanityCheckException;
119import org.apache.hadoop.hbase.filter.BigDecimalComparator;
120import org.apache.hadoop.hbase.filter.BinaryComparator;
121import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;
122import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
123import org.apache.hadoop.hbase.filter.Filter;
124import org.apache.hadoop.hbase.filter.FilterBase;
125import org.apache.hadoop.hbase.filter.FilterList;
126import org.apache.hadoop.hbase.filter.NullComparator;
127import org.apache.hadoop.hbase.filter.PrefixFilter;
128import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter;
129import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
130import org.apache.hadoop.hbase.filter.SubstringComparator;
131import org.apache.hadoop.hbase.filter.ValueFilter;
132import org.apache.hadoop.hbase.io.hfile.HFile;
133import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
134import org.apache.hadoop.hbase.monitoring.MonitoredTask;
135import org.apache.hadoop.hbase.monitoring.TaskMonitor;
136import org.apache.hadoop.hbase.regionserver.HRegion.MutationBatchOperation;
137import org.apache.hadoop.hbase.regionserver.HRegion.RegionScannerImpl;
138import org.apache.hadoop.hbase.regionserver.Region.RowLock;
139import org.apache.hadoop.hbase.regionserver.TestHStore.FaultyFileSystem;
140import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequestImpl;
141import org.apache.hadoop.hbase.regionserver.wal.FSHLog;
142import org.apache.hadoop.hbase.regionserver.wal.MetricsWALSource;
143import org.apache.hadoop.hbase.regionserver.wal.WALUtil;
144import org.apache.hadoop.hbase.replication.regionserver.ReplicationObserver;
145import org.apache.hadoop.hbase.security.User;
146import org.apache.hadoop.hbase.test.MetricsAssertHelper;
147import org.apache.hadoop.hbase.testclassification.LargeTests;
148import org.apache.hadoop.hbase.testclassification.VerySlowRegionServerTests;
149import org.apache.hadoop.hbase.util.Bytes;
150import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
151import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper;
152import org.apache.hadoop.hbase.util.FSUtils;
153import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge;
154import org.apache.hadoop.hbase.util.ManualEnvironmentEdge;
155import org.apache.hadoop.hbase.util.Threads;
156import org.apache.hadoop.hbase.wal.AbstractFSWALProvider;
157import org.apache.hadoop.hbase.wal.FaultyFSLog;
158import org.apache.hadoop.hbase.wal.WAL;
159import org.apache.hadoop.hbase.wal.WALEdit;
160import org.apache.hadoop.hbase.wal.WALFactory;
161import org.apache.hadoop.hbase.wal.WALKeyImpl;
162import org.apache.hadoop.hbase.wal.WALProvider;
163import org.apache.hadoop.hbase.wal.WALProvider.Writer;
164import org.apache.hadoop.hbase.wal.WALSplitter;
165import org.junit.After;
166import org.junit.Assert;
167import org.junit.Before;
168import org.junit.ClassRule;
169import org.junit.Rule;
170import org.junit.Test;
171import org.junit.experimental.categories.Category;
172import org.junit.rules.ExpectedException;
173import org.junit.rules.TestName;
174import org.mockito.ArgumentCaptor;
175import org.mockito.ArgumentMatcher;
176import org.mockito.Mockito;
177import org.mockito.invocation.InvocationOnMock;
178import org.mockito.stubbing.Answer;
179import org.slf4j.Logger;
180import org.slf4j.LoggerFactory;
181
182import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
183import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
184
185import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
186import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.CompactionDescriptor;
187import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.FlushDescriptor;
188import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.FlushDescriptor.FlushAction;
189import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.FlushDescriptor.StoreFlushDescriptor;
190import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.RegionEventDescriptor;
191import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.StoreDescriptor;
192
193/**
194 * Basic stand-alone testing of HRegion.  No clusters!
195 *
196 * A lot of the meta information for an HRegion now lives inside other HRegions
197 * or in the HBaseMaster, so only basic testing is possible.
198 */
199@Category({VerySlowRegionServerTests.class, LargeTests.class})
200@SuppressWarnings("deprecation")
201public class TestHRegion {
202
203  @ClassRule
204  public static final HBaseClassTestRule CLASS_RULE =
205      HBaseClassTestRule.forClass(TestHRegion.class);
206
207  // Do not spin up clusters in here. If you need to spin up a cluster, do it
208  // over in TestHRegionOnCluster.
209  private static final Logger LOG = LoggerFactory.getLogger(TestHRegion.class);
210  @Rule
211  public TestName name = new TestName();
212  @Rule public final ExpectedException thrown = ExpectedException.none();
213
214  private static final String COLUMN_FAMILY = "MyCF";
215  private static final byte [] COLUMN_FAMILY_BYTES = Bytes.toBytes(COLUMN_FAMILY);
216
217  HRegion region = null;
218  // Do not run unit tests in parallel (? Why not?  It don't work?  Why not?  St.Ack)
219  protected static HBaseTestingUtility TEST_UTIL;
220  public static Configuration CONF ;
221  private String dir;
222  private static FileSystem FILESYSTEM;
223  private final int MAX_VERSIONS = 2;
224
225  // Test names
226  protected TableName tableName;
227  protected String method;
228  protected final byte[] qual = Bytes.toBytes("qual");
229  protected final byte[] qual1 = Bytes.toBytes("qual1");
230  protected final byte[] qual2 = Bytes.toBytes("qual2");
231  protected final byte[] qual3 = Bytes.toBytes("qual3");
232  protected final byte[] value = Bytes.toBytes("value");
233  protected final byte[] value1 = Bytes.toBytes("value1");
234  protected final byte[] value2 = Bytes.toBytes("value2");
235  protected final byte[] row = Bytes.toBytes("rowA");
236  protected final byte[] row2 = Bytes.toBytes("rowB");
237
238  protected final MetricsAssertHelper metricsAssertHelper = CompatibilitySingletonFactory
239      .getInstance(MetricsAssertHelper.class);
240
241  @Before
242  public void setup() throws IOException {
243    TEST_UTIL = HBaseTestingUtility.createLocalHTU();
244    FILESYSTEM = TEST_UTIL.getTestFileSystem();
245    CONF = TEST_UTIL.getConfiguration();
246    dir = TEST_UTIL.getDataTestDir("TestHRegion").toString();
247    method = name.getMethodName();
248    tableName = TableName.valueOf(method);
249    CONF.set(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, String.valueOf(0.09));
250  }
251
252  @After
253  public void tearDown() throws Exception {
254    EnvironmentEdgeManagerTestHelper.reset();
255    LOG.info("Cleaning test directory: " + TEST_UTIL.getDataTestDir());
256    TEST_UTIL.cleanupTestDir();
257  }
258
259  /**
260   * Test that I can use the max flushed sequence id after the close.
261   * @throws IOException
262   */
263  @Test
264  public void testSequenceId() throws IOException {
265    HRegion region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES);
266    assertEquals(HConstants.NO_SEQNUM, region.getMaxFlushedSeqId());
267    // Weird. This returns 0 if no store files or no edits. Afraid to change it.
268    assertEquals(0, (long)region.getMaxStoreSeqId().get(COLUMN_FAMILY_BYTES));
269    region.close();
270    assertEquals(HConstants.NO_SEQNUM, region.getMaxFlushedSeqId());
271    assertEquals(0, (long)region.getMaxStoreSeqId().get(COLUMN_FAMILY_BYTES));
272    // Open region again.
273    region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES);
274    byte [] value = Bytes.toBytes(method);
275    // Make a random put against our cf.
276    Put put = new Put(value);
277    put.addColumn(COLUMN_FAMILY_BYTES, null, value);
278    region.put(put);
279    // No flush yet so init numbers should still be in place.
280    assertEquals(HConstants.NO_SEQNUM, region.getMaxFlushedSeqId());
281    assertEquals(0, (long)region.getMaxStoreSeqId().get(COLUMN_FAMILY_BYTES));
282    region.flush(true);
283    long max = region.getMaxFlushedSeqId();
284    region.close();
285    assertEquals(max, region.getMaxFlushedSeqId());
286  }
287
288  /**
289   * Test for Bug 2 of HBASE-10466.
290   * "Bug 2: Conditions for the first flush of region close (so-called pre-flush) If memstoreSize
291   * is smaller than a certain value, or when region close starts a flush is ongoing, the first
292   * flush is skipped and only the second flush takes place. However, two flushes are required in
293   * case previous flush fails and leaves some data in snapshot. The bug could cause loss of data
294   * in current memstore. The fix is removing all conditions except abort check so we ensure 2
295   * flushes for region close."
296   * @throws IOException
297   */
298  @Test
299  public void testCloseCarryingSnapshot() throws IOException {
300    HRegion region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES);
301    HStore store = region.getStore(COLUMN_FAMILY_BYTES);
302    // Get some random bytes.
303    byte [] value = Bytes.toBytes(method);
304    // Make a random put against our cf.
305    Put put = new Put(value);
306    put.addColumn(COLUMN_FAMILY_BYTES, null, value);
307    // First put something in current memstore, which will be in snapshot after flusher.prepare()
308    region.put(put);
309    StoreFlushContext storeFlushCtx = store.createFlushContext(12345, FlushLifeCycleTracker.DUMMY);
310    storeFlushCtx.prepare();
311    // Second put something in current memstore
312    put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("abc"), value);
313    region.put(put);
314    // Close with something in memstore and something in the snapshot.  Make sure all is cleared.
315    region.close();
316    assertEquals(0, region.getMemStoreDataSize());
317    HBaseTestingUtility.closeRegionAndWAL(region);
318  }
319
320  /*
321   * This test is for verifying memstore snapshot size is correctly updated in case of rollback
322   * See HBASE-10845
323   */
324  @Test
325  public void testMemstoreSnapshotSize() throws IOException {
326    class MyFaultyFSLog extends FaultyFSLog {
327      StoreFlushContext storeFlushCtx;
328      public MyFaultyFSLog(FileSystem fs, Path rootDir, String logName, Configuration conf)
329          throws IOException {
330        super(fs, rootDir, logName, conf);
331      }
332
333      void setStoreFlushCtx(StoreFlushContext storeFlushCtx) {
334        this.storeFlushCtx = storeFlushCtx;
335      }
336
337      @Override
338      public void sync(long txid) throws IOException {
339        storeFlushCtx.prepare();
340        super.sync(txid);
341      }
342    }
343
344    FileSystem fs = FileSystem.get(CONF);
345    Path rootDir = new Path(dir + "testMemstoreSnapshotSize");
346    MyFaultyFSLog faultyLog = new MyFaultyFSLog(fs, rootDir, "testMemstoreSnapshotSize", CONF);
347    HRegion region = initHRegion(tableName, null, null, false, Durability.SYNC_WAL, faultyLog,
348        COLUMN_FAMILY_BYTES);
349
350    HStore store = region.getStore(COLUMN_FAMILY_BYTES);
351    // Get some random bytes.
352    byte [] value = Bytes.toBytes(method);
353    faultyLog.setStoreFlushCtx(store.createFlushContext(12345, FlushLifeCycleTracker.DUMMY));
354
355    Put put = new Put(value);
356    put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("abc"), value);
357    faultyLog.setFailureType(FaultyFSLog.FailureType.SYNC);
358
359    boolean threwIOE = false;
360    try {
361      region.put(put);
362    } catch (IOException ioe) {
363      threwIOE = true;
364    } finally {
365      assertTrue("The regionserver should have thrown an exception", threwIOE);
366    }
367    MemStoreSize mss = store.getFlushableSize();
368    assertTrue("flushable size should be zero, but it is " + mss,
369        mss.getDataSize() == 0);
370    HBaseTestingUtility.closeRegionAndWAL(region);
371  }
372
373  /**
374   * Create a WAL outside of the usual helper in
375   * {@link HBaseTestingUtility#createWal(Configuration, Path, RegionInfo)} because that method
376   * doesn't play nicely with FaultyFileSystem. Call this method before overriding
377   * {@code fs.file.impl}.
378   * @param callingMethod a unique component for the path, probably the name of the test method.
379   */
380  private static WAL createWALCompatibleWithFaultyFileSystem(String callingMethod,
381      Configuration conf, TableName tableName) throws IOException {
382    final Path logDir = TEST_UTIL.getDataTestDirOnTestFS(callingMethod + ".log");
383    final Configuration walConf = new Configuration(conf);
384    FSUtils.setRootDir(walConf, logDir);
385    return new WALFactory(walConf, callingMethod)
386        .getWAL(RegionInfoBuilder.newBuilder(tableName).build());
387  }
388
389  @Test
390  public void testMemstoreSizeAccountingWithFailedPostBatchMutate() throws IOException {
391    String testName = "testMemstoreSizeAccountingWithFailedPostBatchMutate";
392    FileSystem fs = FileSystem.get(CONF);
393    Path rootDir = new Path(dir + testName);
394    FSHLog hLog = new FSHLog(fs, rootDir, testName, CONF);
395    HRegion region = initHRegion(tableName, null, null, false, Durability.SYNC_WAL, hLog,
396        COLUMN_FAMILY_BYTES);
397    HStore store = region.getStore(COLUMN_FAMILY_BYTES);
398    assertEquals(0, region.getMemStoreDataSize());
399
400    // Put one value
401    byte [] value = Bytes.toBytes(method);
402    Put put = new Put(value);
403    put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("abc"), value);
404    region.put(put);
405    long onePutSize = region.getMemStoreDataSize();
406    assertTrue(onePutSize > 0);
407
408    RegionCoprocessorHost mockedCPHost = Mockito.mock(RegionCoprocessorHost.class);
409    doThrow(new IOException())
410       .when(mockedCPHost).postBatchMutate(Mockito.<MiniBatchOperationInProgress<Mutation>>any());
411    region.setCoprocessorHost(mockedCPHost);
412
413    put = new Put(value);
414    put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("dfg"), value);
415    try {
416      region.put(put);
417      fail("Should have failed with IOException");
418    } catch (IOException expected) {
419    }
420    long expectedSize = onePutSize * 2;
421    assertEquals("memstoreSize should be incremented",
422        expectedSize, region.getMemStoreDataSize());
423    assertEquals("flushable size should be incremented",
424        expectedSize, store.getFlushableSize().getDataSize());
425
426    region.setCoprocessorHost(null);
427    HBaseTestingUtility.closeRegionAndWAL(region);
428  }
429
430  /**
431   * A test case of HBASE-21041
432   * @throws Exception Exception
433   */
434  @Test
435  public void testFlushAndMemstoreSizeCounting() throws Exception {
436    byte[] family = Bytes.toBytes("family");
437    this.region = initHRegion(tableName, method, CONF, family);
438    final WALFactory wals = new WALFactory(CONF, method);
439    try {
440      for (byte[] row : HBaseTestingUtility.ROWS) {
441        Put put = new Put(row);
442        put.addColumn(family, family, row);
443        region.put(put);
444      }
445      region.flush(true);
446      // After flush, data size should be zero
447      assertEquals(0, region.getMemStoreDataSize());
448      // After flush, a new active mutable segment is created, so the heap size
449      // should equal to MutableSegment.DEEP_OVERHEAD
450      assertEquals(MutableSegment.DEEP_OVERHEAD, region.getMemStoreHeapSize());
451      // After flush, offheap should be zero
452      assertEquals(0, region.getMemStoreOffHeapSize());
453    } finally {
454      HBaseTestingUtility.closeRegionAndWAL(this.region);
455      this.region = null;
456      wals.close();
457    }
458  }
459
460  /**
461   * Test we do not lose data if we fail a flush and then close.
462   * Part of HBase-10466.  Tests the following from the issue description:
463   * "Bug 1: Wrong calculation of HRegion.memstoreSize: When a flush fails, data to be flushed is
464   * kept in each MemStore's snapshot and wait for next flush attempt to continue on it. But when
465   * the next flush succeeds, the counter of total memstore size in HRegion is always deduced by
466   * the sum of current memstore sizes instead of snapshots left from previous failed flush. This
467   * calculation is problematic that almost every time there is failed flush, HRegion.memstoreSize
468   * gets reduced by a wrong value. If region flush could not proceed for a couple cycles, the size
469   * in current memstore could be much larger than the snapshot. It's likely to drift memstoreSize
470   * much smaller than expected. In extreme case, if the error accumulates to even bigger than
471   * HRegion's memstore size limit, any further flush is skipped because flush does not do anything
472   * if memstoreSize is not larger than 0."
473   * @throws Exception
474   */
475  @Test
476  public void testFlushSizeAccounting() throws Exception {
477    final Configuration conf = HBaseConfiguration.create(CONF);
478    final WAL wal = createWALCompatibleWithFaultyFileSystem(method, conf, tableName);
479    // Only retry once.
480    conf.setInt("hbase.hstore.flush.retries.number", 1);
481    final User user =
482      User.createUserForTesting(conf, method, new String[]{"foo"});
483    // Inject our faulty LocalFileSystem
484    conf.setClass("fs.file.impl", FaultyFileSystem.class, FileSystem.class);
485    user.runAs(new PrivilegedExceptionAction<Object>() {
486      @Override
487      public Object run() throws Exception {
488        // Make sure it worked (above is sensitive to caching details in hadoop core)
489        FileSystem fs = FileSystem.get(conf);
490        Assert.assertEquals(FaultyFileSystem.class, fs.getClass());
491        FaultyFileSystem ffs = (FaultyFileSystem)fs;
492        HRegion region = null;
493        try {
494          // Initialize region
495          region = initHRegion(tableName, null, null, false, Durability.SYNC_WAL, wal,
496              COLUMN_FAMILY_BYTES);
497          long size = region.getMemStoreDataSize();
498          Assert.assertEquals(0, size);
499          // Put one item into memstore.  Measure the size of one item in memstore.
500          Put p1 = new Put(row);
501          p1.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual1, 1, (byte[]) null));
502          region.put(p1);
503          final long sizeOfOnePut = region.getMemStoreDataSize();
504          // Fail a flush which means the current memstore will hang out as memstore 'snapshot'.
505          try {
506            LOG.info("Flushing");
507            region.flush(true);
508            Assert.fail("Didn't bubble up IOE!");
509          } catch (DroppedSnapshotException dse) {
510            // What we are expecting
511            region.closing.set(false); // this is needed for the rest of the test to work
512          }
513          // Make it so all writes succeed from here on out
514          ffs.fault.set(false);
515          // Check sizes.  Should still be the one entry.
516          Assert.assertEquals(sizeOfOnePut, region.getMemStoreDataSize());
517          // Now add two entries so that on this next flush that fails, we can see if we
518          // subtract the right amount, the snapshot size only.
519          Put p2 = new Put(row);
520          p2.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual2, 2, (byte[])null));
521          p2.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual3, 3, (byte[])null));
522          region.put(p2);
523          long expectedSize = sizeOfOnePut * 3;
524          Assert.assertEquals(expectedSize, region.getMemStoreDataSize());
525          // Do a successful flush.  It will clear the snapshot only.  Thats how flushes work.
526          // If already a snapshot, we clear it else we move the memstore to be snapshot and flush
527          // it
528          region.flush(true);
529          // Make sure our memory accounting is right.
530          Assert.assertEquals(sizeOfOnePut * 2, region.getMemStoreDataSize());
531        } finally {
532          HBaseTestingUtility.closeRegionAndWAL(region);
533        }
534        return null;
535      }
536    });
537    FileSystem.closeAllForUGI(user.getUGI());
538  }
539
540  @Test
541  public void testCloseWithFailingFlush() throws Exception {
542    final Configuration conf = HBaseConfiguration.create(CONF);
543    final WAL wal = createWALCompatibleWithFaultyFileSystem(method, conf, tableName);
544    // Only retry once.
545    conf.setInt("hbase.hstore.flush.retries.number", 1);
546    final User user =
547      User.createUserForTesting(conf, this.method, new String[]{"foo"});
548    // Inject our faulty LocalFileSystem
549    conf.setClass("fs.file.impl", FaultyFileSystem.class, FileSystem.class);
550    user.runAs(new PrivilegedExceptionAction<Object>() {
551      @Override
552      public Object run() throws Exception {
553        // Make sure it worked (above is sensitive to caching details in hadoop core)
554        FileSystem fs = FileSystem.get(conf);
555        Assert.assertEquals(FaultyFileSystem.class, fs.getClass());
556        FaultyFileSystem ffs = (FaultyFileSystem)fs;
557        HRegion region = null;
558        try {
559          // Initialize region
560          region = initHRegion(tableName, null, null, false,
561              Durability.SYNC_WAL, wal, COLUMN_FAMILY_BYTES);
562          long size = region.getMemStoreDataSize();
563          Assert.assertEquals(0, size);
564          // Put one item into memstore.  Measure the size of one item in memstore.
565          Put p1 = new Put(row);
566          p1.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual1, 1, (byte[])null));
567          region.put(p1);
568          // Manufacture an outstanding snapshot -- fake a failed flush by doing prepare step only.
569          HStore store = region.getStore(COLUMN_FAMILY_BYTES);
570          StoreFlushContext storeFlushCtx =
571              store.createFlushContext(12345, FlushLifeCycleTracker.DUMMY);
572          storeFlushCtx.prepare();
573          // Now add two entries to the foreground memstore.
574          Put p2 = new Put(row);
575          p2.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual2, 2, (byte[])null));
576          p2.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual3, 3, (byte[])null));
577          region.put(p2);
578          // Now try close on top of a failing flush.
579          region.close();
580          fail();
581        } catch (DroppedSnapshotException dse) {
582          // Expected
583          LOG.info("Expected DroppedSnapshotException");
584        } finally {
585          // Make it so all writes succeed from here on out so can close clean
586          ffs.fault.set(false);
587          HBaseTestingUtility.closeRegionAndWAL(region);
588        }
589        return null;
590      }
591    });
592    FileSystem.closeAllForUGI(user.getUGI());
593  }
594
595  @Test
596  public void testCompactionAffectedByScanners() throws Exception {
597    byte[] family = Bytes.toBytes("family");
598    this.region = initHRegion(tableName, method, CONF, family);
599
600    Put put = new Put(Bytes.toBytes("r1"));
601    put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1"));
602    region.put(put);
603    region.flush(true);
604
605    Scan scan = new Scan();
606    scan.setMaxVersions(3);
607    // open the first scanner
608    RegionScanner scanner1 = region.getScanner(scan);
609
610    Delete delete = new Delete(Bytes.toBytes("r1"));
611    region.delete(delete);
612    region.flush(true);
613
614    // open the second scanner
615    RegionScanner scanner2 = region.getScanner(scan);
616
617    List<Cell> results = new ArrayList<>();
618
619    System.out.println("Smallest read point:" + region.getSmallestReadPoint());
620
621    // make a major compaction
622    region.compact(true);
623
624    // open the third scanner
625    RegionScanner scanner3 = region.getScanner(scan);
626
627    // get data from scanner 1, 2, 3 after major compaction
628    scanner1.next(results);
629    System.out.println(results);
630    assertEquals(1, results.size());
631
632    results.clear();
633    scanner2.next(results);
634    System.out.println(results);
635    assertEquals(0, results.size());
636
637    results.clear();
638    scanner3.next(results);
639    System.out.println(results);
640    assertEquals(0, results.size());
641  }
642
643  @Test
644  public void testToShowNPEOnRegionScannerReseek() throws Exception {
645    byte[] family = Bytes.toBytes("family");
646    this.region = initHRegion(tableName, method, CONF, family);
647
648    Put put = new Put(Bytes.toBytes("r1"));
649    put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1"));
650    region.put(put);
651    put = new Put(Bytes.toBytes("r2"));
652    put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1"));
653    region.put(put);
654    region.flush(true);
655
656    Scan scan = new Scan();
657    scan.setMaxVersions(3);
658    // open the first scanner
659    RegionScanner scanner1 = region.getScanner(scan);
660
661    System.out.println("Smallest read point:" + region.getSmallestReadPoint());
662
663    region.compact(true);
664
665    scanner1.reseek(Bytes.toBytes("r2"));
666    List<Cell> results = new ArrayList<>();
667    scanner1.next(results);
668    Cell keyValue = results.get(0);
669    Assert.assertTrue(Bytes.compareTo(CellUtil.cloneRow(keyValue), Bytes.toBytes("r2")) == 0);
670    scanner1.close();
671  }
672
673  @Test
674  public void testSkipRecoveredEditsReplay() throws Exception {
675    byte[] family = Bytes.toBytes("family");
676    this.region = initHRegion(tableName, method, CONF, family);
677    final WALFactory wals = new WALFactory(CONF, method);
678    try {
679      Path regiondir = region.getRegionFileSystem().getRegionDir();
680      FileSystem fs = region.getRegionFileSystem().getFileSystem();
681      byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes();
682
683      Path recoveredEditsDir = WALSplitter.getRegionDirRecoveredEditsDir(regiondir);
684
685      long maxSeqId = 1050;
686      long minSeqId = 1000;
687
688      for (long i = minSeqId; i <= maxSeqId; i += 10) {
689        Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i));
690        fs.create(recoveredEdits);
691        WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits);
692
693        long time = System.nanoTime();
694        WALEdit edit = new WALEdit();
695        edit.add(new KeyValue(row, family, Bytes.toBytes(i), time, KeyValue.Type.Put, Bytes
696            .toBytes(i)));
697        writer.append(new WAL.Entry(new WALKeyImpl(regionName, tableName, i, time,
698            HConstants.DEFAULT_CLUSTER_ID), edit));
699
700        writer.close();
701      }
702      MonitoredTask status = TaskMonitor.get().createStatus(method);
703      Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR);
704      for (HStore store : region.getStores()) {
705        maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), minSeqId - 1);
706      }
707      long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, status);
708      assertEquals(maxSeqId, seqId);
709      region.getMVCC().advanceTo(seqId);
710      Get get = new Get(row);
711      Result result = region.get(get);
712      for (long i = minSeqId; i <= maxSeqId; i += 10) {
713        List<Cell> kvs = result.getColumnCells(family, Bytes.toBytes(i));
714        assertEquals(1, kvs.size());
715        assertArrayEquals(Bytes.toBytes(i), CellUtil.cloneValue(kvs.get(0)));
716      }
717    } finally {
718      HBaseTestingUtility.closeRegionAndWAL(this.region);
719      this.region = null;
720      wals.close();
721    }
722  }
723
724  @Test
725  public void testSkipRecoveredEditsReplaySomeIgnored() throws Exception {
726    byte[] family = Bytes.toBytes("family");
727    this.region = initHRegion(tableName, method, CONF, family);
728    final WALFactory wals = new WALFactory(CONF, method);
729    try {
730      Path regiondir = region.getRegionFileSystem().getRegionDir();
731      FileSystem fs = region.getRegionFileSystem().getFileSystem();
732      byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes();
733
734      Path recoveredEditsDir = WALSplitter.getRegionDirRecoveredEditsDir(regiondir);
735
736      long maxSeqId = 1050;
737      long minSeqId = 1000;
738
739      for (long i = minSeqId; i <= maxSeqId; i += 10) {
740        Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i));
741        fs.create(recoveredEdits);
742        WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits);
743
744        long time = System.nanoTime();
745        WALEdit edit = new WALEdit();
746        edit.add(new KeyValue(row, family, Bytes.toBytes(i), time, KeyValue.Type.Put, Bytes
747            .toBytes(i)));
748        writer.append(new WAL.Entry(new WALKeyImpl(regionName, tableName, i, time,
749            HConstants.DEFAULT_CLUSTER_ID), edit));
750
751        writer.close();
752      }
753      long recoverSeqId = 1030;
754      MonitoredTask status = TaskMonitor.get().createStatus(method);
755      Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR);
756      for (HStore store : region.getStores()) {
757        maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), recoverSeqId - 1);
758      }
759      long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, status);
760      assertEquals(maxSeqId, seqId);
761      region.getMVCC().advanceTo(seqId);
762      Get get = new Get(row);
763      Result result = region.get(get);
764      for (long i = minSeqId; i <= maxSeqId; i += 10) {
765        List<Cell> kvs = result.getColumnCells(family, Bytes.toBytes(i));
766        if (i < recoverSeqId) {
767          assertEquals(0, kvs.size());
768        } else {
769          assertEquals(1, kvs.size());
770          assertArrayEquals(Bytes.toBytes(i), CellUtil.cloneValue(kvs.get(0)));
771        }
772      }
773    } finally {
774      HBaseTestingUtility.closeRegionAndWAL(this.region);
775      this.region = null;
776      wals.close();
777    }
778  }
779
780  @Test
781  public void testSkipRecoveredEditsReplayAllIgnored() throws Exception {
782    byte[] family = Bytes.toBytes("family");
783    this.region = initHRegion(tableName, method, CONF, family);
784    try {
785      Path regiondir = region.getRegionFileSystem().getRegionDir();
786      FileSystem fs = region.getRegionFileSystem().getFileSystem();
787
788      Path recoveredEditsDir = WALSplitter.getRegionDirRecoveredEditsDir(regiondir);
789      for (int i = 1000; i < 1050; i += 10) {
790        Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i));
791        FSDataOutputStream dos = fs.create(recoveredEdits);
792        dos.writeInt(i);
793        dos.close();
794      }
795      long minSeqId = 2000;
796      Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", minSeqId - 1));
797      FSDataOutputStream dos = fs.create(recoveredEdits);
798      dos.close();
799
800      Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR);
801      for (HStore store : region.getStores()) {
802        maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), minSeqId);
803      }
804      long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, null);
805      assertEquals(minSeqId, seqId);
806    } finally {
807      HBaseTestingUtility.closeRegionAndWAL(this.region);
808      this.region = null;
809    }
810  }
811
812  @Test
813  public void testSkipRecoveredEditsReplayTheLastFileIgnored() throws Exception {
814    byte[] family = Bytes.toBytes("family");
815    this.region = initHRegion(tableName, method, CONF, family);
816    final WALFactory wals = new WALFactory(CONF, method);
817    try {
818      Path regiondir = region.getRegionFileSystem().getRegionDir();
819      FileSystem fs = region.getRegionFileSystem().getFileSystem();
820      byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes();
821      byte[][] columns = region.getTableDescriptor().getColumnFamilyNames().toArray(new byte[0][]);
822
823      assertEquals(0, region.getStoreFileList(columns).size());
824
825      Path recoveredEditsDir = WALSplitter.getRegionDirRecoveredEditsDir(regiondir);
826
827      long maxSeqId = 1050;
828      long minSeqId = 1000;
829
830      for (long i = minSeqId; i <= maxSeqId; i += 10) {
831        Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i));
832        fs.create(recoveredEdits);
833        WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits);
834
835        long time = System.nanoTime();
836        WALEdit edit = null;
837        if (i == maxSeqId) {
838          edit = WALEdit.createCompaction(region.getRegionInfo(),
839          CompactionDescriptor.newBuilder()
840          .setTableName(ByteString.copyFrom(tableName.getName()))
841          .setFamilyName(ByteString.copyFrom(regionName))
842          .setEncodedRegionName(ByteString.copyFrom(regionName))
843          .setStoreHomeDirBytes(ByteString.copyFrom(Bytes.toBytes(regiondir.toString())))
844          .setRegionName(ByteString.copyFrom(region.getRegionInfo().getRegionName()))
845          .build());
846        } else {
847          edit = new WALEdit();
848          edit.add(new KeyValue(row, family, Bytes.toBytes(i), time, KeyValue.Type.Put, Bytes
849            .toBytes(i)));
850        }
851        writer.append(new WAL.Entry(new WALKeyImpl(regionName, tableName, i, time,
852            HConstants.DEFAULT_CLUSTER_ID), edit));
853        writer.close();
854      }
855
856      long recoverSeqId = 1030;
857      Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR);
858      MonitoredTask status = TaskMonitor.get().createStatus(method);
859      for (HStore store : region.getStores()) {
860        maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), recoverSeqId - 1);
861      }
862      long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, status);
863      assertEquals(maxSeqId, seqId);
864
865      // assert that the files are flushed
866      assertEquals(1, region.getStoreFileList(columns).size());
867
868    } finally {
869      HBaseTestingUtility.closeRegionAndWAL(this.region);
870      this.region = null;
871      wals.close();
872    }
873  }
874
875  @Test
876  public void testRecoveredEditsReplayCompaction() throws Exception {
877    testRecoveredEditsReplayCompaction(false);
878    testRecoveredEditsReplayCompaction(true);
879  }
880
881  public void testRecoveredEditsReplayCompaction(boolean mismatchedRegionName) throws Exception {
882    CONF.setClass(HConstants.REGION_IMPL, HRegionForTesting.class, Region.class);
883    byte[] family = Bytes.toBytes("family");
884    this.region = initHRegion(tableName, method, CONF, family);
885    final WALFactory wals = new WALFactory(CONF, method);
886    try {
887      Path regiondir = region.getRegionFileSystem().getRegionDir();
888      FileSystem fs = region.getRegionFileSystem().getFileSystem();
889      byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes();
890
891      long maxSeqId = 3;
892      long minSeqId = 0;
893
894      for (long i = minSeqId; i < maxSeqId; i++) {
895        Put put = new Put(Bytes.toBytes(i));
896        put.addColumn(family, Bytes.toBytes(i), Bytes.toBytes(i));
897        region.put(put);
898        region.flush(true);
899      }
900
901      // this will create a region with 3 files
902      assertEquals(3, region.getStore(family).getStorefilesCount());
903      List<Path> storeFiles = new ArrayList<>(3);
904      for (HStoreFile sf : region.getStore(family).getStorefiles()) {
905        storeFiles.add(sf.getPath());
906      }
907
908      // disable compaction completion
909      CONF.setBoolean("hbase.hstore.compaction.complete", false);
910      region.compactStores();
911
912      // ensure that nothing changed
913      assertEquals(3, region.getStore(family).getStorefilesCount());
914
915      // now find the compacted file, and manually add it to the recovered edits
916      Path tmpDir = new Path(region.getRegionFileSystem().getTempDir(), Bytes.toString(family));
917      FileStatus[] files = FSUtils.listStatus(fs, tmpDir);
918      String errorMsg = "Expected to find 1 file in the region temp directory "
919          + "from the compaction, could not find any";
920      assertNotNull(errorMsg, files);
921      assertEquals(errorMsg, 1, files.length);
922      // move the file inside region dir
923      Path newFile = region.getRegionFileSystem().commitStoreFile(Bytes.toString(family),
924          files[0].getPath());
925
926      byte[] encodedNameAsBytes = this.region.getRegionInfo().getEncodedNameAsBytes();
927      byte[] fakeEncodedNameAsBytes = new byte [encodedNameAsBytes.length];
928      for (int i=0; i < encodedNameAsBytes.length; i++) {
929        // Mix the byte array to have a new encodedName
930        fakeEncodedNameAsBytes[i] = (byte) (encodedNameAsBytes[i] + 1);
931      }
932
933      CompactionDescriptor compactionDescriptor = ProtobufUtil.toCompactionDescriptor(this.region
934        .getRegionInfo(), mismatchedRegionName ? fakeEncodedNameAsBytes : null, family,
935            storeFiles, Lists.newArrayList(newFile),
936            region.getRegionFileSystem().getStoreDir(Bytes.toString(family)));
937
938      WALUtil.writeCompactionMarker(region.getWAL(), this.region.getReplicationScope(),
939          this.region.getRegionInfo(), compactionDescriptor, region.getMVCC());
940
941      Path recoveredEditsDir = WALSplitter.getRegionDirRecoveredEditsDir(regiondir);
942
943      Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", 1000));
944      fs.create(recoveredEdits);
945      WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits);
946
947      long time = System.nanoTime();
948
949      writer.append(new WAL.Entry(new WALKeyImpl(regionName, tableName, 10, time,
950          HConstants.DEFAULT_CLUSTER_ID), WALEdit.createCompaction(region.getRegionInfo(),
951          compactionDescriptor)));
952      writer.close();
953
954      // close the region now, and reopen again
955      region.getTableDescriptor();
956      region.getRegionInfo();
957      region.close();
958      try {
959        region = HRegion.openHRegion(region, null);
960      } catch (WrongRegionException wre) {
961        fail("Matching encoded region name should not have produced WrongRegionException");
962      }
963
964      // now check whether we have only one store file, the compacted one
965      Collection<HStoreFile> sfs = region.getStore(family).getStorefiles();
966      for (HStoreFile sf : sfs) {
967        LOG.info(Objects.toString(sf.getPath()));
968      }
969      if (!mismatchedRegionName) {
970        assertEquals(1, region.getStore(family).getStorefilesCount());
971      }
972      files = FSUtils.listStatus(fs, tmpDir);
973      assertTrue("Expected to find 0 files inside " + tmpDir, files == null || files.length == 0);
974
975      for (long i = minSeqId; i < maxSeqId; i++) {
976        Get get = new Get(Bytes.toBytes(i));
977        Result result = region.get(get);
978        byte[] value = result.getValue(family, Bytes.toBytes(i));
979        assertArrayEquals(Bytes.toBytes(i), value);
980      }
981    } finally {
982      HBaseTestingUtility.closeRegionAndWAL(this.region);
983      this.region = null;
984      wals.close();
985      CONF.setClass(HConstants.REGION_IMPL, HRegion.class, Region.class);
986    }
987  }
988
989  @Test
990  public void testFlushMarkers() throws Exception {
991    // tests that flush markers are written to WAL and handled at recovered edits
992    byte[] family = Bytes.toBytes("family");
993    Path logDir = TEST_UTIL.getDataTestDirOnTestFS(method + ".log");
994    final Configuration walConf = new Configuration(TEST_UTIL.getConfiguration());
995    FSUtils.setRootDir(walConf, logDir);
996    final WALFactory wals = new WALFactory(walConf, method);
997    final WAL wal = wals.getWAL(RegionInfoBuilder.newBuilder(tableName).build());
998
999    this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW,
1000      HConstants.EMPTY_END_ROW, false, Durability.USE_DEFAULT, wal, family);
1001    try {
1002      Path regiondir = region.getRegionFileSystem().getRegionDir();
1003      FileSystem fs = region.getRegionFileSystem().getFileSystem();
1004      byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes();
1005
1006      long maxSeqId = 3;
1007      long minSeqId = 0;
1008
1009      for (long i = minSeqId; i < maxSeqId; i++) {
1010        Put put = new Put(Bytes.toBytes(i));
1011        put.addColumn(family, Bytes.toBytes(i), Bytes.toBytes(i));
1012        region.put(put);
1013        region.flush(true);
1014      }
1015
1016      // this will create a region with 3 files from flush
1017      assertEquals(3, region.getStore(family).getStorefilesCount());
1018      List<String> storeFiles = new ArrayList<>(3);
1019      for (HStoreFile sf : region.getStore(family).getStorefiles()) {
1020        storeFiles.add(sf.getPath().getName());
1021      }
1022
1023      // now verify that the flush markers are written
1024      wal.shutdown();
1025      WAL.Reader reader = WALFactory.createReader(fs, AbstractFSWALProvider.getCurrentFileName(wal),
1026        TEST_UTIL.getConfiguration());
1027      try {
1028        List<WAL.Entry> flushDescriptors = new ArrayList<>();
1029        long lastFlushSeqId = -1;
1030        while (true) {
1031          WAL.Entry entry = reader.next();
1032          if (entry == null) {
1033            break;
1034          }
1035          Cell cell = entry.getEdit().getCells().get(0);
1036          if (WALEdit.isMetaEditFamily(cell)) {
1037            FlushDescriptor flushDesc = WALEdit.getFlushDescriptor(cell);
1038            assertNotNull(flushDesc);
1039            assertArrayEquals(tableName.getName(), flushDesc.getTableName().toByteArray());
1040            if (flushDesc.getAction() == FlushAction.START_FLUSH) {
1041              assertTrue(flushDesc.getFlushSequenceNumber() > lastFlushSeqId);
1042            } else if (flushDesc.getAction() == FlushAction.COMMIT_FLUSH) {
1043              assertTrue(flushDesc.getFlushSequenceNumber() == lastFlushSeqId);
1044            }
1045            lastFlushSeqId = flushDesc.getFlushSequenceNumber();
1046            assertArrayEquals(regionName, flushDesc.getEncodedRegionName().toByteArray());
1047            assertEquals(1, flushDesc.getStoreFlushesCount()); //only one store
1048            StoreFlushDescriptor storeFlushDesc = flushDesc.getStoreFlushes(0);
1049            assertArrayEquals(family, storeFlushDesc.getFamilyName().toByteArray());
1050            assertEquals("family", storeFlushDesc.getStoreHomeDir());
1051            if (flushDesc.getAction() == FlushAction.START_FLUSH) {
1052              assertEquals(0, storeFlushDesc.getFlushOutputCount());
1053            } else {
1054              assertEquals(1, storeFlushDesc.getFlushOutputCount()); //only one file from flush
1055              assertTrue(storeFiles.contains(storeFlushDesc.getFlushOutput(0)));
1056            }
1057
1058            flushDescriptors.add(entry);
1059          }
1060        }
1061
1062        assertEquals(3 * 2, flushDescriptors.size()); // START_FLUSH and COMMIT_FLUSH per flush
1063
1064        // now write those markers to the recovered edits again.
1065
1066        Path recoveredEditsDir = WALSplitter.getRegionDirRecoveredEditsDir(regiondir);
1067
1068        Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", 1000));
1069        fs.create(recoveredEdits);
1070        WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits);
1071
1072        for (WAL.Entry entry : flushDescriptors) {
1073          writer.append(entry);
1074        }
1075        writer.close();
1076      } finally {
1077        if (null != reader) {
1078          try {
1079            reader.close();
1080          } catch (IOException exception) {
1081            LOG.warn("Problem closing wal: " + exception.getMessage());
1082            LOG.debug("exception details", exception);
1083          }
1084        }
1085      }
1086
1087
1088      // close the region now, and reopen again
1089      region.close();
1090      region = HRegion.openHRegion(region, null);
1091
1092      // now check whether we have can read back the data from region
1093      for (long i = minSeqId; i < maxSeqId; i++) {
1094        Get get = new Get(Bytes.toBytes(i));
1095        Result result = region.get(get);
1096        byte[] value = result.getValue(family, Bytes.toBytes(i));
1097        assertArrayEquals(Bytes.toBytes(i), value);
1098      }
1099    } finally {
1100      HBaseTestingUtility.closeRegionAndWAL(this.region);
1101      this.region = null;
1102      wals.close();
1103    }
1104  }
1105
1106  static class IsFlushWALMarker implements ArgumentMatcher<WALEdit> {
1107    volatile FlushAction[] actions;
1108    public IsFlushWALMarker(FlushAction... actions) {
1109      this.actions = actions;
1110    }
1111    @Override
1112    public boolean matches(WALEdit edit) {
1113      List<Cell> cells = edit.getCells();
1114      if (cells.isEmpty()) {
1115        return false;
1116      }
1117      if (WALEdit.isMetaEditFamily(cells.get(0))) {
1118        FlushDescriptor desc;
1119        try {
1120          desc = WALEdit.getFlushDescriptor(cells.get(0));
1121        } catch (IOException e) {
1122          LOG.warn(e.toString(), e);
1123          return false;
1124        }
1125        if (desc != null) {
1126          for (FlushAction action : actions) {
1127            if (desc.getAction() == action) {
1128              return true;
1129            }
1130          }
1131        }
1132      }
1133      return false;
1134    }
1135    public IsFlushWALMarker set(FlushAction... actions) {
1136      this.actions = actions;
1137      return this;
1138    }
1139  }
1140
1141  @Test
1142  public void testFlushMarkersWALFail() throws Exception {
1143    // test the cases where the WAL append for flush markers fail.
1144    byte[] family = Bytes.toBytes("family");
1145
1146    // spy an actual WAL implementation to throw exception (was not able to mock)
1147    Path logDir = TEST_UTIL.getDataTestDirOnTestFS(method + "log");
1148
1149    final Configuration walConf = new Configuration(TEST_UTIL.getConfiguration());
1150    FSUtils.setRootDir(walConf, logDir);
1151    // Make up a WAL that we can manipulate at append time.
1152    class FailAppendFlushMarkerWAL extends FSHLog {
1153      volatile FlushAction [] flushActions = null;
1154
1155      public FailAppendFlushMarkerWAL(FileSystem fs, Path root, String logDir, Configuration conf)
1156      throws IOException {
1157        super(fs, root, logDir, conf);
1158      }
1159
1160      @Override
1161      protected Writer createWriterInstance(Path path) throws IOException {
1162        final Writer w = super.createWriterInstance(path);
1163        return new Writer() {
1164          @Override
1165          public void close() throws IOException {
1166            w.close();
1167          }
1168
1169          @Override
1170          public void sync() throws IOException {
1171            w.sync();
1172          }
1173
1174          @Override
1175          public void append(Entry entry) throws IOException {
1176            List<Cell> cells = entry.getEdit().getCells();
1177            if (WALEdit.isMetaEditFamily(cells.get(0))) {
1178              FlushDescriptor desc = WALEdit.getFlushDescriptor(cells.get(0));
1179              if (desc != null) {
1180                for (FlushAction flushAction: flushActions) {
1181                  if (desc.getAction().equals(flushAction)) {
1182                    throw new IOException("Failed to append flush marker! " + flushAction);
1183                  }
1184                }
1185              }
1186            }
1187            w.append(entry);
1188          }
1189
1190          @Override
1191          public long getLength() {
1192            return w.getLength();
1193          }
1194        };
1195      }
1196    }
1197    FailAppendFlushMarkerWAL wal =
1198      new FailAppendFlushMarkerWAL(FileSystem.get(walConf), FSUtils.getRootDir(walConf),
1199        method, walConf);
1200    this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW,
1201      HConstants.EMPTY_END_ROW, false, Durability.USE_DEFAULT, wal, family);
1202    try {
1203      int i = 0;
1204      Put put = new Put(Bytes.toBytes(i));
1205      put.setDurability(Durability.SKIP_WAL); // have to skip mocked wal
1206      put.addColumn(family, Bytes.toBytes(i), Bytes.toBytes(i));
1207      region.put(put);
1208
1209      // 1. Test case where START_FLUSH throws exception
1210      wal.flushActions = new FlushAction [] {FlushAction.START_FLUSH};
1211
1212      // start cache flush will throw exception
1213      try {
1214        region.flush(true);
1215        fail("This should have thrown exception");
1216      } catch (DroppedSnapshotException unexpected) {
1217        // this should not be a dropped snapshot exception. Meaning that RS will not abort
1218        throw unexpected;
1219      } catch (IOException expected) {
1220        // expected
1221      }
1222      // The WAL is hosed now. It has two edits appended. We cannot roll the log without it
1223      // throwing a DroppedSnapshotException to force an abort. Just clean up the mess.
1224      region.close(true);
1225      wal.close();
1226
1227      // 2. Test case where START_FLUSH succeeds but COMMIT_FLUSH will throw exception
1228      wal.flushActions = new FlushAction [] {FlushAction.COMMIT_FLUSH};
1229      wal = new FailAppendFlushMarkerWAL(FileSystem.get(walConf), FSUtils.getRootDir(walConf),
1230            method, walConf);
1231
1232      this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW,
1233        HConstants.EMPTY_END_ROW, false, Durability.USE_DEFAULT, wal, family);
1234      region.put(put);
1235
1236      // 3. Test case where ABORT_FLUSH will throw exception.
1237      // Even if ABORT_FLUSH throws exception, we should not fail with IOE, but continue with
1238      // DroppedSnapshotException. Below COMMMIT_FLUSH will cause flush to abort
1239      wal.flushActions = new FlushAction [] {FlushAction.COMMIT_FLUSH, FlushAction.ABORT_FLUSH};
1240
1241      try {
1242        region.flush(true);
1243        fail("This should have thrown exception");
1244      } catch (DroppedSnapshotException expected) {
1245        // we expect this exception, since we were able to write the snapshot, but failed to
1246        // write the flush marker to WAL
1247      } catch (IOException unexpected) {
1248        throw unexpected;
1249      }
1250
1251    } finally {
1252      HBaseTestingUtility.closeRegionAndWAL(this.region);
1253      this.region = null;
1254    }
1255  }
1256
1257  @Test
1258  public void testGetWhileRegionClose() throws IOException {
1259    Configuration hc = initSplit();
1260    int numRows = 100;
1261    byte[][] families = { fam1, fam2, fam3 };
1262
1263    // Setting up region
1264    this.region = initHRegion(tableName, method, hc, families);
1265    try {
1266      // Put data in region
1267      final int startRow = 100;
1268      putData(startRow, numRows, qual1, families);
1269      putData(startRow, numRows, qual2, families);
1270      putData(startRow, numRows, qual3, families);
1271      final AtomicBoolean done = new AtomicBoolean(false);
1272      final AtomicInteger gets = new AtomicInteger(0);
1273      GetTillDoneOrException[] threads = new GetTillDoneOrException[10];
1274      try {
1275        // Set ten threads running concurrently getting from the region.
1276        for (int i = 0; i < threads.length / 2; i++) {
1277          threads[i] = new GetTillDoneOrException(i, Bytes.toBytes("" + startRow), done, gets);
1278          threads[i].setDaemon(true);
1279          threads[i].start();
1280        }
1281        // Artificially make the condition by setting closing flag explicitly.
1282        // I can't make the issue happen with a call to region.close().
1283        this.region.closing.set(true);
1284        for (int i = threads.length / 2; i < threads.length; i++) {
1285          threads[i] = new GetTillDoneOrException(i, Bytes.toBytes("" + startRow), done, gets);
1286          threads[i].setDaemon(true);
1287          threads[i].start();
1288        }
1289      } finally {
1290        if (this.region != null) {
1291          HBaseTestingUtility.closeRegionAndWAL(this.region);
1292        }
1293      }
1294      done.set(true);
1295      for (GetTillDoneOrException t : threads) {
1296        try {
1297          t.join();
1298        } catch (InterruptedException e) {
1299          e.printStackTrace();
1300        }
1301        if (t.e != null) {
1302          LOG.info("Exception=" + t.e);
1303          assertFalse("Found a NPE in " + t.getName(), t.e instanceof NullPointerException);
1304        }
1305      }
1306    } finally {
1307      HBaseTestingUtility.closeRegionAndWAL(this.region);
1308      this.region = null;
1309    }
1310  }
1311
1312  /*
1313   * Thread that does get on single row until 'done' flag is flipped. If an
1314   * exception causes us to fail, it records it.
1315   */
1316  class GetTillDoneOrException extends Thread {
1317    private final Get g;
1318    private final AtomicBoolean done;
1319    private final AtomicInteger count;
1320    private Exception e;
1321
1322    GetTillDoneOrException(final int i, final byte[] r, final AtomicBoolean d,
1323        final AtomicInteger c) {
1324      super("getter." + i);
1325      this.g = new Get(r);
1326      this.done = d;
1327      this.count = c;
1328    }
1329
1330    @Override
1331    public void run() {
1332      while (!this.done.get()) {
1333        try {
1334          assertTrue(region.get(g).size() > 0);
1335          this.count.incrementAndGet();
1336        } catch (Exception e) {
1337          this.e = e;
1338          break;
1339        }
1340      }
1341    }
1342  }
1343
1344  /*
1345   * An involved filter test. Has multiple column families and deletes in mix.
1346   */
1347  @Test
1348  public void testWeirdCacheBehaviour() throws Exception {
1349    final TableName tableName = TableName.valueOf(name.getMethodName());
1350    byte[][] FAMILIES = new byte[][] { Bytes.toBytes("trans-blob"), Bytes.toBytes("trans-type"),
1351        Bytes.toBytes("trans-date"), Bytes.toBytes("trans-tags"), Bytes.toBytes("trans-group") };
1352    this.region = initHRegion(tableName, method, CONF, FAMILIES);
1353    try {
1354      String value = "this is the value";
1355      String value2 = "this is some other value";
1356      String keyPrefix1 = "prefix1";
1357      String keyPrefix2 = "prefix2";
1358      String keyPrefix3 = "prefix3";
1359      putRows(this.region, 3, value, keyPrefix1);
1360      putRows(this.region, 3, value, keyPrefix2);
1361      putRows(this.region, 3, value, keyPrefix3);
1362      putRows(this.region, 3, value2, keyPrefix1);
1363      putRows(this.region, 3, value2, keyPrefix2);
1364      putRows(this.region, 3, value2, keyPrefix3);
1365      System.out.println("Checking values for key: " + keyPrefix1);
1366      assertEquals("Got back incorrect number of rows from scan", 3,
1367          getNumberOfRows(keyPrefix1, value2, this.region));
1368      System.out.println("Checking values for key: " + keyPrefix2);
1369      assertEquals("Got back incorrect number of rows from scan", 3,
1370          getNumberOfRows(keyPrefix2, value2, this.region));
1371      System.out.println("Checking values for key: " + keyPrefix3);
1372      assertEquals("Got back incorrect number of rows from scan", 3,
1373          getNumberOfRows(keyPrefix3, value2, this.region));
1374      deleteColumns(this.region, value2, keyPrefix1);
1375      deleteColumns(this.region, value2, keyPrefix2);
1376      deleteColumns(this.region, value2, keyPrefix3);
1377      System.out.println("Starting important checks.....");
1378      assertEquals("Got back incorrect number of rows from scan: " + keyPrefix1, 0,
1379          getNumberOfRows(keyPrefix1, value2, this.region));
1380      assertEquals("Got back incorrect number of rows from scan: " + keyPrefix2, 0,
1381          getNumberOfRows(keyPrefix2, value2, this.region));
1382      assertEquals("Got back incorrect number of rows from scan: " + keyPrefix3, 0,
1383          getNumberOfRows(keyPrefix3, value2, this.region));
1384    } finally {
1385      HBaseTestingUtility.closeRegionAndWAL(this.region);
1386      this.region = null;
1387    }
1388  }
1389
1390  @Test
1391  public void testAppendWithReadOnlyTable() throws Exception {
1392    final TableName tableName = TableName.valueOf(name.getMethodName());
1393    this.region = initHRegion(tableName, method, CONF, true, Bytes.toBytes("somefamily"));
1394    boolean exceptionCaught = false;
1395    Append append = new Append(Bytes.toBytes("somerow"));
1396    append.setDurability(Durability.SKIP_WAL);
1397    append.addColumn(Bytes.toBytes("somefamily"), Bytes.toBytes("somequalifier"),
1398        Bytes.toBytes("somevalue"));
1399    try {
1400      region.append(append);
1401    } catch (IOException e) {
1402      exceptionCaught = true;
1403    } finally {
1404      HBaseTestingUtility.closeRegionAndWAL(this.region);
1405      this.region = null;
1406    }
1407    assertTrue(exceptionCaught == true);
1408  }
1409
1410  @Test
1411  public void testIncrWithReadOnlyTable() throws Exception {
1412    final TableName tableName = TableName.valueOf(name.getMethodName());
1413    this.region = initHRegion(tableName, method, CONF, true, Bytes.toBytes("somefamily"));
1414    boolean exceptionCaught = false;
1415    Increment inc = new Increment(Bytes.toBytes("somerow"));
1416    inc.setDurability(Durability.SKIP_WAL);
1417    inc.addColumn(Bytes.toBytes("somefamily"), Bytes.toBytes("somequalifier"), 1L);
1418    try {
1419      region.increment(inc);
1420    } catch (IOException e) {
1421      exceptionCaught = true;
1422    } finally {
1423      HBaseTestingUtility.closeRegionAndWAL(this.region);
1424      this.region = null;
1425    }
1426    assertTrue(exceptionCaught == true);
1427  }
1428
1429  private void deleteColumns(HRegion r, String value, String keyPrefix) throws IOException {
1430    InternalScanner scanner = buildScanner(keyPrefix, value, r);
1431    int count = 0;
1432    boolean more = false;
1433    List<Cell> results = new ArrayList<>();
1434    do {
1435      more = scanner.next(results);
1436      if (results != null && !results.isEmpty())
1437        count++;
1438      else
1439        break;
1440      Delete delete = new Delete(CellUtil.cloneRow(results.get(0)));
1441      delete.addColumn(Bytes.toBytes("trans-tags"), Bytes.toBytes("qual2"));
1442      r.delete(delete);
1443      results.clear();
1444    } while (more);
1445    assertEquals("Did not perform correct number of deletes", 3, count);
1446  }
1447
1448  private int getNumberOfRows(String keyPrefix, String value, HRegion r) throws Exception {
1449    InternalScanner resultScanner = buildScanner(keyPrefix, value, r);
1450    int numberOfResults = 0;
1451    List<Cell> results = new ArrayList<>();
1452    boolean more = false;
1453    do {
1454      more = resultScanner.next(results);
1455      if (results != null && !results.isEmpty())
1456        numberOfResults++;
1457      else
1458        break;
1459      for (Cell kv : results) {
1460        System.out.println("kv=" + kv.toString() + ", " + Bytes.toString(CellUtil.cloneValue(kv)));
1461      }
1462      results.clear();
1463    } while (more);
1464    return numberOfResults;
1465  }
1466
1467  private InternalScanner buildScanner(String keyPrefix, String value, HRegion r)
1468      throws IOException {
1469    // Defaults FilterList.Operator.MUST_PASS_ALL.
1470    FilterList allFilters = new FilterList();
1471    allFilters.addFilter(new PrefixFilter(Bytes.toBytes(keyPrefix)));
1472    // Only return rows where this column value exists in the row.
1473    SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("trans-tags"),
1474        Bytes.toBytes("qual2"), CompareOp.EQUAL, Bytes.toBytes(value));
1475    filter.setFilterIfMissing(true);
1476    allFilters.addFilter(filter);
1477    Scan scan = new Scan();
1478    scan.addFamily(Bytes.toBytes("trans-blob"));
1479    scan.addFamily(Bytes.toBytes("trans-type"));
1480    scan.addFamily(Bytes.toBytes("trans-date"));
1481    scan.addFamily(Bytes.toBytes("trans-tags"));
1482    scan.addFamily(Bytes.toBytes("trans-group"));
1483    scan.setFilter(allFilters);
1484    return r.getScanner(scan);
1485  }
1486
1487  private void putRows(HRegion r, int numRows, String value, String key) throws IOException {
1488    for (int i = 0; i < numRows; i++) {
1489      String row = key + "_" + i/* UUID.randomUUID().toString() */;
1490      System.out.println(String.format("Saving row: %s, with value %s", row, value));
1491      Put put = new Put(Bytes.toBytes(row));
1492      put.setDurability(Durability.SKIP_WAL);
1493      put.addColumn(Bytes.toBytes("trans-blob"), null, Bytes.toBytes("value for blob"));
1494      put.addColumn(Bytes.toBytes("trans-type"), null, Bytes.toBytes("statement"));
1495      put.addColumn(Bytes.toBytes("trans-date"), null, Bytes.toBytes("20090921010101999"));
1496      put.addColumn(Bytes.toBytes("trans-tags"), Bytes.toBytes("qual2"), Bytes.toBytes(value));
1497      put.addColumn(Bytes.toBytes("trans-group"), null, Bytes.toBytes("adhocTransactionGroupId"));
1498      r.put(put);
1499    }
1500  }
1501
1502  @Test
1503  public void testFamilyWithAndWithoutColon() throws Exception {
1504    byte[] cf = Bytes.toBytes(COLUMN_FAMILY);
1505    this.region = initHRegion(tableName, method, CONF, cf);
1506    try {
1507      Put p = new Put(tableName.toBytes());
1508      byte[] cfwithcolon = Bytes.toBytes(COLUMN_FAMILY + ":");
1509      p.addColumn(cfwithcolon, cfwithcolon, cfwithcolon);
1510      boolean exception = false;
1511      try {
1512        this.region.put(p);
1513      } catch (NoSuchColumnFamilyException e) {
1514        exception = true;
1515      }
1516      assertTrue(exception);
1517    } finally {
1518      HBaseTestingUtility.closeRegionAndWAL(this.region);
1519      this.region = null;
1520    }
1521  }
1522
1523  @Test
1524  public void testBatchPut_whileNoRowLocksHeld() throws IOException {
1525    final Put[] puts = new Put[10];
1526    MetricsWALSource source = CompatibilitySingletonFactory.getInstance(MetricsWALSource.class);
1527    try {
1528      long syncs = prepareRegionForBachPut(puts, source, false);
1529
1530      OperationStatus[] codes = this.region.batchMutate(puts);
1531      assertEquals(10, codes.length);
1532      for (int i = 0; i < 10; i++) {
1533        assertEquals(OperationStatusCode.SUCCESS, codes[i].getOperationStatusCode());
1534      }
1535      metricsAssertHelper.assertCounter("syncTimeNumOps", syncs + 1, source);
1536
1537      LOG.info("Next a batch put with one invalid family");
1538      puts[5].addColumn(Bytes.toBytes("BAD_CF"), qual, value);
1539      codes = this.region.batchMutate(puts);
1540      assertEquals(10, codes.length);
1541      for (int i = 0; i < 10; i++) {
1542        assertEquals((i == 5) ? OperationStatusCode.BAD_FAMILY : OperationStatusCode.SUCCESS,
1543            codes[i].getOperationStatusCode());
1544      }
1545
1546      metricsAssertHelper.assertCounter("syncTimeNumOps", syncs + 2, source);
1547    } finally {
1548      HBaseTestingUtility.closeRegionAndWAL(this.region);
1549      this.region = null;
1550    }
1551  }
1552
1553  @Test
1554  public void testBatchPut_whileMultipleRowLocksHeld() throws Exception {
1555    final Put[] puts = new Put[10];
1556    MetricsWALSource source = CompatibilitySingletonFactory.getInstance(MetricsWALSource.class);
1557    try {
1558      long syncs = prepareRegionForBachPut(puts, source, false);
1559
1560      puts[5].addColumn(Bytes.toBytes("BAD_CF"), qual, value);
1561
1562      LOG.info("batchPut will have to break into four batches to avoid row locks");
1563      RowLock rowLock1 = region.getRowLock(Bytes.toBytes("row_2"));
1564      RowLock rowLock2 = region.getRowLock(Bytes.toBytes("row_1"));
1565      RowLock rowLock3 = region.getRowLock(Bytes.toBytes("row_3"));
1566      RowLock rowLock4 = region.getRowLock(Bytes.toBytes("row_3"), true);
1567
1568      MultithreadedTestUtil.TestContext ctx = new MultithreadedTestUtil.TestContext(CONF);
1569      final AtomicReference<OperationStatus[]> retFromThread = new AtomicReference<>();
1570      final CountDownLatch startingPuts = new CountDownLatch(1);
1571      final CountDownLatch startingClose = new CountDownLatch(1);
1572      TestThread putter = new TestThread(ctx) {
1573        @Override
1574        public void doWork() throws IOException {
1575          startingPuts.countDown();
1576          retFromThread.set(region.batchMutate(puts));
1577        }
1578      };
1579      LOG.info("...starting put thread while holding locks");
1580      ctx.addThread(putter);
1581      ctx.startThreads();
1582
1583      // Now attempt to close the region from another thread.  Prior to HBASE-12565
1584      // this would cause the in-progress batchMutate operation to to fail with
1585      // exception because it use to release and re-acquire the close-guard lock
1586      // between batches.  Caller then didn't get status indicating which writes succeeded.
1587      // We now expect this thread to block until the batchMutate call finishes.
1588      Thread regionCloseThread = new TestThread(ctx) {
1589        @Override
1590        public void doWork() {
1591          try {
1592            startingPuts.await();
1593            // Give some time for the batch mutate to get in.
1594            // We don't want to race with the mutate
1595            Thread.sleep(10);
1596            startingClose.countDown();
1597            HBaseTestingUtility.closeRegionAndWAL(region);
1598          } catch (IOException e) {
1599            throw new RuntimeException(e);
1600          } catch (InterruptedException e) {
1601            throw new RuntimeException(e);
1602          }
1603        }
1604      };
1605      regionCloseThread.start();
1606
1607      startingClose.await();
1608      startingPuts.await();
1609      Thread.sleep(100);
1610      LOG.info("...releasing row lock 1, which should let put thread continue");
1611      rowLock1.release();
1612      rowLock2.release();
1613      rowLock3.release();
1614      waitForCounter(source, "syncTimeNumOps", syncs + 1);
1615
1616      LOG.info("...joining on put thread");
1617      ctx.stop();
1618      regionCloseThread.join();
1619
1620      OperationStatus[] codes = retFromThread.get();
1621      for (int i = 0; i < codes.length; i++) {
1622        assertEquals((i == 5) ? OperationStatusCode.BAD_FAMILY : OperationStatusCode.SUCCESS,
1623            codes[i].getOperationStatusCode());
1624      }
1625      rowLock4.release();
1626    } finally {
1627      HBaseTestingUtility.closeRegionAndWAL(this.region);
1628      this.region = null;
1629    }
1630  }
1631
1632  private void waitForCounter(MetricsWALSource source, String metricName, long expectedCount)
1633      throws InterruptedException {
1634    long startWait = System.currentTimeMillis();
1635    long currentCount;
1636    while ((currentCount = metricsAssertHelper.getCounter(metricName, source)) < expectedCount) {
1637      Thread.sleep(100);
1638      if (System.currentTimeMillis() - startWait > 10000) {
1639        fail(String.format("Timed out waiting for '%s' >= '%s', currentCount=%s", metricName,
1640            expectedCount, currentCount));
1641      }
1642    }
1643  }
1644
1645  @Test
1646  public void testAtomicBatchPut() throws IOException {
1647    final Put[] puts = new Put[10];
1648    MetricsWALSource source = CompatibilitySingletonFactory.getInstance(MetricsWALSource.class);
1649    try {
1650      long syncs = prepareRegionForBachPut(puts, source, false);
1651
1652      // 1. Straight forward case, should succeed
1653      MutationBatchOperation batchOp = new MutationBatchOperation(region, puts, true,
1654          HConstants.NO_NONCE, HConstants.NO_NONCE);
1655      OperationStatus[] codes = this.region.batchMutate(batchOp);
1656      assertEquals(10, codes.length);
1657      for (int i = 0; i < 10; i++) {
1658        assertEquals(OperationStatusCode.SUCCESS, codes[i].getOperationStatusCode());
1659      }
1660      metricsAssertHelper.assertCounter("syncTimeNumOps", syncs + 1, source);
1661
1662      // 2. Failed to get lock
1663      RowLock lock = region.getRowLock(Bytes.toBytes("row_" + 3));
1664      // Method {@link HRegion#getRowLock(byte[])} is reentrant. As 'row_3' is locked in this
1665      // thread, need to run {@link HRegion#batchMutate(HRegion.BatchOperation)} in different thread
1666      MultithreadedTestUtil.TestContext ctx = new MultithreadedTestUtil.TestContext(CONF);
1667      final AtomicReference<IOException> retFromThread = new AtomicReference<>();
1668      final CountDownLatch finishedPuts = new CountDownLatch(1);
1669      final MutationBatchOperation finalBatchOp = new MutationBatchOperation(region, puts, true,
1670          HConstants
1671          .NO_NONCE,
1672          HConstants.NO_NONCE);
1673      TestThread putter = new TestThread(ctx) {
1674        @Override
1675        public void doWork() throws IOException {
1676          try {
1677            region.batchMutate(finalBatchOp);
1678          } catch (IOException ioe) {
1679            LOG.error("test failed!", ioe);
1680            retFromThread.set(ioe);
1681          }
1682          finishedPuts.countDown();
1683        }
1684      };
1685      LOG.info("...starting put thread while holding locks");
1686      ctx.addThread(putter);
1687      ctx.startThreads();
1688      LOG.info("...waiting for batch puts while holding locks");
1689      try {
1690        finishedPuts.await();
1691      } catch (InterruptedException e) {
1692        LOG.error("Interrupted!", e);
1693      } finally {
1694        if (lock != null) {
1695          lock.release();
1696        }
1697      }
1698      assertNotNull(retFromThread.get());
1699      metricsAssertHelper.assertCounter("syncTimeNumOps", syncs + 1, source);
1700
1701      // 3. Exception thrown in validation
1702      LOG.info("Next a batch put with one invalid family");
1703      puts[5].addColumn(Bytes.toBytes("BAD_CF"), qual, value);
1704      batchOp = new MutationBatchOperation(region, puts, true, HConstants.NO_NONCE,
1705          HConstants.NO_NONCE);
1706      thrown.expect(NoSuchColumnFamilyException.class);
1707      this.region.batchMutate(batchOp);
1708    } finally {
1709      HBaseTestingUtility.closeRegionAndWAL(this.region);
1710      this.region = null;
1711    }
1712  }
1713
1714  @Test
1715  public void testBatchPutWithTsSlop() throws Exception {
1716    // add data with a timestamp that is too recent for range. Ensure assert
1717    CONF.setInt("hbase.hregion.keyvalue.timestamp.slop.millisecs", 1000);
1718    final Put[] puts = new Put[10];
1719    MetricsWALSource source = CompatibilitySingletonFactory.getInstance(MetricsWALSource.class);
1720
1721    try {
1722      long syncs = prepareRegionForBachPut(puts, source, true);
1723
1724      OperationStatus[] codes = this.region.batchMutate(puts);
1725      assertEquals(10, codes.length);
1726      for (int i = 0; i < 10; i++) {
1727        assertEquals(OperationStatusCode.SANITY_CHECK_FAILURE, codes[i].getOperationStatusCode());
1728      }
1729      metricsAssertHelper.assertCounter("syncTimeNumOps", syncs, source);
1730    } finally {
1731      HBaseTestingUtility.closeRegionAndWAL(this.region);
1732      this.region = null;
1733    }
1734  }
1735
1736  /**
1737   * @return syncs initial syncTimeNumOps
1738   */
1739  private long prepareRegionForBachPut(final Put[] puts, final MetricsWALSource source,
1740      boolean slop) throws IOException {
1741    this.region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES);
1742
1743    LOG.info("First a batch put with all valid puts");
1744    for (int i = 0; i < puts.length; i++) {
1745      puts[i] = slop ? new Put(Bytes.toBytes("row_" + i), Long.MAX_VALUE - 100) :
1746          new Put(Bytes.toBytes("row_" + i));
1747      puts[i].addColumn(COLUMN_FAMILY_BYTES, qual, value);
1748    }
1749
1750    long syncs = metricsAssertHelper.getCounter("syncTimeNumOps", source);
1751    metricsAssertHelper.assertCounter("syncTimeNumOps", syncs, source);
1752    return syncs;
1753  }
1754
1755  // ////////////////////////////////////////////////////////////////////////////
1756  // checkAndMutate tests
1757  // ////////////////////////////////////////////////////////////////////////////
1758  @Test
1759  public void testCheckAndMutate_WithEmptyRowValue() throws IOException {
1760    byte[] row1 = Bytes.toBytes("row1");
1761    byte[] fam1 = Bytes.toBytes("fam1");
1762    byte[] qf1 = Bytes.toBytes("qualifier");
1763    byte[] emptyVal = new byte[] {};
1764    byte[] val1 = Bytes.toBytes("value1");
1765    byte[] val2 = Bytes.toBytes("value2");
1766
1767    // Setting up region
1768    this.region = initHRegion(tableName, method, CONF, fam1);
1769    try {
1770      // Putting empty data in key
1771      Put put = new Put(row1);
1772      put.addColumn(fam1, qf1, emptyVal);
1773
1774      // checkAndPut with empty value
1775      boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(
1776          emptyVal), put);
1777      assertTrue(res);
1778
1779      // Putting data in key
1780      put = new Put(row1);
1781      put.addColumn(fam1, qf1, val1);
1782
1783      // checkAndPut with correct value
1784      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(emptyVal),
1785          put);
1786      assertTrue(res);
1787
1788      // not empty anymore
1789      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(emptyVal),
1790          put);
1791      assertFalse(res);
1792
1793      Delete delete = new Delete(row1);
1794      delete.addColumn(fam1, qf1);
1795      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(emptyVal),
1796          delete);
1797      assertFalse(res);
1798
1799      put = new Put(row1);
1800      put.addColumn(fam1, qf1, val2);
1801      // checkAndPut with correct value
1802      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val1),
1803          put);
1804      assertTrue(res);
1805
1806      // checkAndDelete with correct value
1807      delete = new Delete(row1);
1808      delete.addColumn(fam1, qf1);
1809      delete.addColumn(fam1, qf1);
1810      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val2),
1811          delete);
1812      assertTrue(res);
1813
1814      delete = new Delete(row1);
1815      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(emptyVal),
1816          delete);
1817      assertTrue(res);
1818
1819      // checkAndPut looking for a null value
1820      put = new Put(row1);
1821      put.addColumn(fam1, qf1, val1);
1822
1823      res = region
1824          .checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new NullComparator(), put);
1825      assertTrue(res);
1826    } finally {
1827      HBaseTestingUtility.closeRegionAndWAL(this.region);
1828      this.region = null;
1829    }
1830  }
1831
1832  @Test
1833  public void testCheckAndMutate_WithWrongValue() throws IOException {
1834    byte[] row1 = Bytes.toBytes("row1");
1835    byte[] fam1 = Bytes.toBytes("fam1");
1836    byte[] qf1 = Bytes.toBytes("qualifier");
1837    byte[] val1 = Bytes.toBytes("value1");
1838    byte[] val2 = Bytes.toBytes("value2");
1839    BigDecimal bd1 = new BigDecimal(Double.MAX_VALUE);
1840    BigDecimal bd2 = new BigDecimal(Double.MIN_VALUE);
1841
1842    // Setting up region
1843    this.region = initHRegion(tableName, method, CONF, fam1);
1844    try {
1845      // Putting data in key
1846      Put put = new Put(row1);
1847      put.addColumn(fam1, qf1, val1);
1848      region.put(put);
1849
1850      // checkAndPut with wrong value
1851      boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(
1852          val2), put);
1853      assertEquals(false, res);
1854
1855      // checkAndDelete with wrong value
1856      Delete delete = new Delete(row1);
1857      delete.addFamily(fam1);
1858      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val2),
1859          put);
1860      assertEquals(false, res);
1861
1862      // Putting data in key
1863      put = new Put(row1);
1864      put.addColumn(fam1, qf1, Bytes.toBytes(bd1));
1865      region.put(put);
1866
1867      // checkAndPut with wrong value
1868      res =
1869          region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BigDecimalComparator(
1870              bd2), put);
1871      assertEquals(false, res);
1872
1873      // checkAndDelete with wrong value
1874      delete = new Delete(row1);
1875      delete.addFamily(fam1);
1876      res =
1877          region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BigDecimalComparator(
1878              bd2), put);
1879      assertEquals(false, res);
1880    } finally {
1881      HBaseTestingUtility.closeRegionAndWAL(this.region);
1882      this.region = null;
1883    }
1884  }
1885
1886  @Test
1887  public void testCheckAndMutate_WithCorrectValue() throws IOException {
1888    byte[] row1 = Bytes.toBytes("row1");
1889    byte[] fam1 = Bytes.toBytes("fam1");
1890    byte[] qf1 = Bytes.toBytes("qualifier");
1891    byte[] val1 = Bytes.toBytes("value1");
1892    BigDecimal bd1 = new BigDecimal(Double.MIN_VALUE);
1893
1894    // Setting up region
1895    this.region = initHRegion(tableName, method, CONF, fam1);
1896    try {
1897      // Putting data in key
1898      Put put = new Put(row1);
1899      put.addColumn(fam1, qf1, val1);
1900      region.put(put);
1901
1902      // checkAndPut with correct value
1903      boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(
1904          val1), put);
1905      assertEquals(true, res);
1906
1907      // checkAndDelete with correct value
1908      Delete delete = new Delete(row1);
1909      delete.addColumn(fam1, qf1);
1910      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val1),
1911          delete);
1912      assertEquals(true, res);
1913
1914      // Putting data in key
1915      put = new Put(row1);
1916      put.addColumn(fam1, qf1, Bytes.toBytes(bd1));
1917      region.put(put);
1918
1919      // checkAndPut with correct value
1920      res =
1921          region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BigDecimalComparator(
1922              bd1), put);
1923      assertEquals(true, res);
1924
1925      // checkAndDelete with correct value
1926      delete = new Delete(row1);
1927      delete.addColumn(fam1, qf1);
1928      res =
1929          region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BigDecimalComparator(
1930              bd1), delete);
1931      assertEquals(true, res);
1932    } finally {
1933      HBaseTestingUtility.closeRegionAndWAL(this.region);
1934      this.region = null;
1935    }
1936  }
1937
1938  @Test
1939  public void testCheckAndMutate_WithNonEqualCompareOp() throws IOException {
1940    byte[] row1 = Bytes.toBytes("row1");
1941    byte[] fam1 = Bytes.toBytes("fam1");
1942    byte[] qf1 = Bytes.toBytes("qualifier");
1943    byte[] val1 = Bytes.toBytes("value1");
1944    byte[] val2 = Bytes.toBytes("value2");
1945    byte[] val3 = Bytes.toBytes("value3");
1946    byte[] val4 = Bytes.toBytes("value4");
1947
1948    // Setting up region
1949    this.region = initHRegion(tableName, method, CONF, fam1);
1950    try {
1951      // Putting val3 in key
1952      Put put = new Put(row1);
1953      put.addColumn(fam1, qf1, val3);
1954      region.put(put);
1955
1956      // Test CompareOp.LESS: original = val3, compare with val3, fail
1957      boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS,
1958          new BinaryComparator(val3), put);
1959      assertEquals(false, res);
1960
1961      // Test CompareOp.LESS: original = val3, compare with val4, fail
1962      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS,
1963          new BinaryComparator(val4), put);
1964      assertEquals(false, res);
1965
1966      // Test CompareOp.LESS: original = val3, compare with val2,
1967      // succeed (now value = val2)
1968      put = new Put(row1);
1969      put.addColumn(fam1, qf1, val2);
1970      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS,
1971          new BinaryComparator(val2), put);
1972      assertEquals(true, res);
1973
1974      // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val3, fail
1975      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS_OR_EQUAL,
1976          new BinaryComparator(val3), put);
1977      assertEquals(false, res);
1978
1979      // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val2,
1980      // succeed (value still = val2)
1981      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS_OR_EQUAL,
1982          new BinaryComparator(val2), put);
1983      assertEquals(true, res);
1984
1985      // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val1,
1986      // succeed (now value = val3)
1987      put = new Put(row1);
1988      put.addColumn(fam1, qf1, val3);
1989      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS_OR_EQUAL,
1990          new BinaryComparator(val1), put);
1991      assertEquals(true, res);
1992
1993      // Test CompareOp.GREATER: original = val3, compare with val3, fail
1994      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER,
1995          new BinaryComparator(val3), put);
1996      assertEquals(false, res);
1997
1998      // Test CompareOp.GREATER: original = val3, compare with val2, fail
1999      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER,
2000          new BinaryComparator(val2), put);
2001      assertEquals(false, res);
2002
2003      // Test CompareOp.GREATER: original = val3, compare with val4,
2004      // succeed (now value = val2)
2005      put = new Put(row1);
2006      put.addColumn(fam1, qf1, val2);
2007      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER,
2008          new BinaryComparator(val4), put);
2009      assertEquals(true, res);
2010
2011      // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val1, fail
2012      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER_OR_EQUAL,
2013          new BinaryComparator(val1), put);
2014      assertEquals(false, res);
2015
2016      // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val2,
2017      // succeed (value still = val2)
2018      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER_OR_EQUAL,
2019          new BinaryComparator(val2), put);
2020      assertEquals(true, res);
2021
2022      // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val3, succeed
2023      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER_OR_EQUAL,
2024          new BinaryComparator(val3), put);
2025      assertEquals(true, res);
2026    } finally {
2027      HBaseTestingUtility.closeRegionAndWAL(this.region);
2028      this.region = null;
2029    }
2030  }
2031
2032  @Test
2033  public void testCheckAndPut_ThatPutWasWritten() throws IOException {
2034    byte[] row1 = Bytes.toBytes("row1");
2035    byte[] fam1 = Bytes.toBytes("fam1");
2036    byte[] fam2 = Bytes.toBytes("fam2");
2037    byte[] qf1 = Bytes.toBytes("qualifier");
2038    byte[] val1 = Bytes.toBytes("value1");
2039    byte[] val2 = Bytes.toBytes("value2");
2040
2041    byte[][] families = { fam1, fam2 };
2042
2043    // Setting up region
2044    this.region = initHRegion(tableName, method, CONF, families);
2045    try {
2046      // Putting data in the key to check
2047      Put put = new Put(row1);
2048      put.addColumn(fam1, qf1, val1);
2049      region.put(put);
2050
2051      // Creating put to add
2052      long ts = System.currentTimeMillis();
2053      KeyValue kv = new KeyValue(row1, fam2, qf1, ts, KeyValue.Type.Put, val2);
2054      put = new Put(row1);
2055      put.add(kv);
2056
2057      // checkAndPut with wrong value
2058      boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(
2059          val1), put);
2060      assertEquals(true, res);
2061
2062      Get get = new Get(row1);
2063      get.addColumn(fam2, qf1);
2064      Cell[] actual = region.get(get).rawCells();
2065
2066      Cell[] expected = { kv };
2067
2068      assertEquals(expected.length, actual.length);
2069      for (int i = 0; i < actual.length; i++) {
2070        assertEquals(expected[i], actual[i]);
2071      }
2072    } finally {
2073      HBaseTestingUtility.closeRegionAndWAL(this.region);
2074      this.region = null;
2075    }
2076  }
2077
2078  @Test
2079  public void testCheckAndPut_wrongRowInPut() throws IOException {
2080    this.region = initHRegion(tableName, method, CONF, COLUMNS);
2081    try {
2082      Put put = new Put(row2);
2083      put.addColumn(fam1, qual1, value1);
2084      try {
2085        region.checkAndMutate(row, fam1, qual1, CompareOperator.EQUAL,
2086            new BinaryComparator(value2), put);
2087        fail();
2088      } catch (org.apache.hadoop.hbase.DoNotRetryIOException expected) {
2089        // expected exception.
2090      }
2091    } finally {
2092      HBaseTestingUtility.closeRegionAndWAL(this.region);
2093      this.region = null;
2094    }
2095  }
2096
2097  @Test
2098  public void testCheckAndDelete_ThatDeleteWasWritten() throws IOException {
2099    byte[] row1 = Bytes.toBytes("row1");
2100    byte[] fam1 = Bytes.toBytes("fam1");
2101    byte[] fam2 = Bytes.toBytes("fam2");
2102    byte[] qf1 = Bytes.toBytes("qualifier1");
2103    byte[] qf2 = Bytes.toBytes("qualifier2");
2104    byte[] qf3 = Bytes.toBytes("qualifier3");
2105    byte[] val1 = Bytes.toBytes("value1");
2106    byte[] val2 = Bytes.toBytes("value2");
2107    byte[] val3 = Bytes.toBytes("value3");
2108    byte[] emptyVal = new byte[] {};
2109
2110    byte[][] families = { fam1, fam2 };
2111
2112    // Setting up region
2113    this.region = initHRegion(tableName, method, CONF, families);
2114    try {
2115      // Put content
2116      Put put = new Put(row1);
2117      put.addColumn(fam1, qf1, val1);
2118      region.put(put);
2119      Threads.sleep(2);
2120
2121      put = new Put(row1);
2122      put.addColumn(fam1, qf1, val2);
2123      put.addColumn(fam2, qf1, val3);
2124      put.addColumn(fam2, qf2, val2);
2125      put.addColumn(fam2, qf3, val1);
2126      put.addColumn(fam1, qf3, val1);
2127      region.put(put);
2128
2129      // Multi-column delete
2130      Delete delete = new Delete(row1);
2131      delete.addColumn(fam1, qf1);
2132      delete.addColumn(fam2, qf1);
2133      delete.addColumn(fam1, qf3);
2134      boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(
2135          val2), delete);
2136      assertEquals(true, res);
2137
2138      Get get = new Get(row1);
2139      get.addColumn(fam1, qf1);
2140      get.addColumn(fam1, qf3);
2141      get.addColumn(fam2, qf2);
2142      Result r = region.get(get);
2143      assertEquals(2, r.size());
2144      assertArrayEquals(val1, r.getValue(fam1, qf1));
2145      assertArrayEquals(val2, r.getValue(fam2, qf2));
2146
2147      // Family delete
2148      delete = new Delete(row1);
2149      delete.addFamily(fam2);
2150      res = region.checkAndMutate(row1, fam2, qf1, CompareOperator.EQUAL, new BinaryComparator(emptyVal),
2151          delete);
2152      assertEquals(true, res);
2153
2154      get = new Get(row1);
2155      r = region.get(get);
2156      assertEquals(1, r.size());
2157      assertArrayEquals(val1, r.getValue(fam1, qf1));
2158
2159      // Row delete
2160      delete = new Delete(row1);
2161      res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val1),
2162          delete);
2163      assertEquals(true, res);
2164      get = new Get(row1);
2165      r = region.get(get);
2166      assertEquals(0, r.size());
2167    } finally {
2168      HBaseTestingUtility.closeRegionAndWAL(this.region);
2169      this.region = null;
2170    }
2171  }
2172
2173  // ////////////////////////////////////////////////////////////////////////////
2174  // Delete tests
2175  // ////////////////////////////////////////////////////////////////////////////
2176  @Test
2177  public void testDelete_multiDeleteColumn() throws IOException {
2178    byte[] row1 = Bytes.toBytes("row1");
2179    byte[] fam1 = Bytes.toBytes("fam1");
2180    byte[] qual = Bytes.toBytes("qualifier");
2181    byte[] value = Bytes.toBytes("value");
2182
2183    Put put = new Put(row1);
2184    put.addColumn(fam1, qual, 1, value);
2185    put.addColumn(fam1, qual, 2, value);
2186
2187    this.region = initHRegion(tableName, method, CONF, fam1);
2188    try {
2189      region.put(put);
2190
2191      // We do support deleting more than 1 'latest' version
2192      Delete delete = new Delete(row1);
2193      delete.addColumn(fam1, qual);
2194      delete.addColumn(fam1, qual);
2195      region.delete(delete);
2196
2197      Get get = new Get(row1);
2198      get.addFamily(fam1);
2199      Result r = region.get(get);
2200      assertEquals(0, r.size());
2201    } finally {
2202      HBaseTestingUtility.closeRegionAndWAL(this.region);
2203      this.region = null;
2204    }
2205  }
2206
2207  @Test
2208  public void testDelete_CheckFamily() throws IOException {
2209    byte[] row1 = Bytes.toBytes("row1");
2210    byte[] fam1 = Bytes.toBytes("fam1");
2211    byte[] fam2 = Bytes.toBytes("fam2");
2212    byte[] fam3 = Bytes.toBytes("fam3");
2213    byte[] fam4 = Bytes.toBytes("fam4");
2214
2215    // Setting up region
2216    this.region = initHRegion(tableName, method, CONF, fam1, fam2, fam3);
2217    try {
2218      List<Cell> kvs = new ArrayList<>();
2219      kvs.add(new KeyValue(row1, fam4, null, null));
2220
2221      // testing existing family
2222      byte[] family = fam2;
2223      try {
2224        NavigableMap<byte[], List<Cell>> deleteMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
2225        deleteMap.put(family, kvs);
2226        region.delete(deleteMap, Durability.SYNC_WAL);
2227      } catch (Exception e) {
2228        fail("Family " + new String(family, StandardCharsets.UTF_8) + " does not exist");
2229      }
2230
2231      // testing non existing family
2232      boolean ok = false;
2233      family = fam4;
2234      try {
2235        NavigableMap<byte[], List<Cell>> deleteMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
2236        deleteMap.put(family, kvs);
2237        region.delete(deleteMap, Durability.SYNC_WAL);
2238      } catch (Exception e) {
2239        ok = true;
2240      }
2241      assertTrue("Family " + new String(family, StandardCharsets.UTF_8) + " does exist", ok);
2242    } finally {
2243      HBaseTestingUtility.closeRegionAndWAL(this.region);
2244      this.region = null;
2245    }
2246  }
2247
2248  @Test
2249  public void testDelete_mixed() throws IOException, InterruptedException {
2250    byte[] fam = Bytes.toBytes("info");
2251    byte[][] families = { fam };
2252    this.region = initHRegion(tableName, method, CONF, families);
2253    try {
2254      EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge());
2255
2256      byte[] row = Bytes.toBytes("table_name");
2257      // column names
2258      byte[] serverinfo = Bytes.toBytes("serverinfo");
2259      byte[] splitA = Bytes.toBytes("splitA");
2260      byte[] splitB = Bytes.toBytes("splitB");
2261
2262      // add some data:
2263      Put put = new Put(row);
2264      put.addColumn(fam, splitA, Bytes.toBytes("reference_A"));
2265      region.put(put);
2266
2267      put = new Put(row);
2268      put.addColumn(fam, splitB, Bytes.toBytes("reference_B"));
2269      region.put(put);
2270
2271      put = new Put(row);
2272      put.addColumn(fam, serverinfo, Bytes.toBytes("ip_address"));
2273      region.put(put);
2274
2275      // ok now delete a split:
2276      Delete delete = new Delete(row);
2277      delete.addColumns(fam, splitA);
2278      region.delete(delete);
2279
2280      // assert some things:
2281      Get get = new Get(row).addColumn(fam, serverinfo);
2282      Result result = region.get(get);
2283      assertEquals(1, result.size());
2284
2285      get = new Get(row).addColumn(fam, splitA);
2286      result = region.get(get);
2287      assertEquals(0, result.size());
2288
2289      get = new Get(row).addColumn(fam, splitB);
2290      result = region.get(get);
2291      assertEquals(1, result.size());
2292
2293      // Assert that after a delete, I can put.
2294      put = new Put(row);
2295      put.addColumn(fam, splitA, Bytes.toBytes("reference_A"));
2296      region.put(put);
2297      get = new Get(row);
2298      result = region.get(get);
2299      assertEquals(3, result.size());
2300
2301      // Now delete all... then test I can add stuff back
2302      delete = new Delete(row);
2303      region.delete(delete);
2304      assertEquals(0, region.get(get).size());
2305
2306      region.put(new Put(row).addColumn(fam, splitA, Bytes.toBytes("reference_A")));
2307      result = region.get(get);
2308      assertEquals(1, result.size());
2309    } finally {
2310      HBaseTestingUtility.closeRegionAndWAL(this.region);
2311      this.region = null;
2312    }
2313  }
2314
2315  @Test
2316  public void testDeleteRowWithFutureTs() throws IOException {
2317    byte[] fam = Bytes.toBytes("info");
2318    byte[][] families = { fam };
2319    this.region = initHRegion(tableName, method, CONF, families);
2320    try {
2321      byte[] row = Bytes.toBytes("table_name");
2322      // column names
2323      byte[] serverinfo = Bytes.toBytes("serverinfo");
2324
2325      // add data in the far future
2326      Put put = new Put(row);
2327      put.addColumn(fam, serverinfo, HConstants.LATEST_TIMESTAMP - 5, Bytes.toBytes("value"));
2328      region.put(put);
2329
2330      // now delete something in the present
2331      Delete delete = new Delete(row);
2332      region.delete(delete);
2333
2334      // make sure we still see our data
2335      Get get = new Get(row).addColumn(fam, serverinfo);
2336      Result result = region.get(get);
2337      assertEquals(1, result.size());
2338
2339      // delete the future row
2340      delete = new Delete(row, HConstants.LATEST_TIMESTAMP - 3);
2341      region.delete(delete);
2342
2343      // make sure it is gone
2344      get = new Get(row).addColumn(fam, serverinfo);
2345      result = region.get(get);
2346      assertEquals(0, result.size());
2347    } finally {
2348      HBaseTestingUtility.closeRegionAndWAL(this.region);
2349      this.region = null;
2350    }
2351  }
2352
2353  /**
2354   * Tests that the special LATEST_TIMESTAMP option for puts gets replaced by
2355   * the actual timestamp
2356   */
2357  @Test
2358  public void testPutWithLatestTS() throws IOException {
2359    byte[] fam = Bytes.toBytes("info");
2360    byte[][] families = { fam };
2361    this.region = initHRegion(tableName, method, CONF, families);
2362    try {
2363      byte[] row = Bytes.toBytes("row1");
2364      // column names
2365      byte[] qual = Bytes.toBytes("qual");
2366
2367      // add data with LATEST_TIMESTAMP, put without WAL
2368      Put put = new Put(row);
2369      put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, Bytes.toBytes("value"));
2370      region.put(put);
2371
2372      // Make sure it shows up with an actual timestamp
2373      Get get = new Get(row).addColumn(fam, qual);
2374      Result result = region.get(get);
2375      assertEquals(1, result.size());
2376      Cell kv = result.rawCells()[0];
2377      LOG.info("Got: " + kv);
2378      assertTrue("LATEST_TIMESTAMP was not replaced with real timestamp",
2379          kv.getTimestamp() != HConstants.LATEST_TIMESTAMP);
2380
2381      // Check same with WAL enabled (historically these took different
2382      // code paths, so check both)
2383      row = Bytes.toBytes("row2");
2384      put = new Put(row);
2385      put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, Bytes.toBytes("value"));
2386      region.put(put);
2387
2388      // Make sure it shows up with an actual timestamp
2389      get = new Get(row).addColumn(fam, qual);
2390      result = region.get(get);
2391      assertEquals(1, result.size());
2392      kv = result.rawCells()[0];
2393      LOG.info("Got: " + kv);
2394      assertTrue("LATEST_TIMESTAMP was not replaced with real timestamp",
2395          kv.getTimestamp() != HConstants.LATEST_TIMESTAMP);
2396    } finally {
2397      HBaseTestingUtility.closeRegionAndWAL(this.region);
2398      this.region = null;
2399    }
2400
2401  }
2402
2403  /**
2404   * Tests that there is server-side filtering for invalid timestamp upper
2405   * bound. Note that the timestamp lower bound is automatically handled for us
2406   * by the TTL field.
2407   */
2408  @Test
2409  public void testPutWithTsSlop() throws IOException {
2410    byte[] fam = Bytes.toBytes("info");
2411    byte[][] families = { fam };
2412
2413    // add data with a timestamp that is too recent for range. Ensure assert
2414    CONF.setInt("hbase.hregion.keyvalue.timestamp.slop.millisecs", 1000);
2415    this.region = initHRegion(tableName, method, CONF, families);
2416    boolean caughtExcep = false;
2417    try {
2418      try {
2419        // no TS specified == use latest. should not error
2420        region.put(new Put(row).addColumn(fam, Bytes.toBytes("qual"), Bytes.toBytes("value")));
2421        // TS out of range. should error
2422        region.put(new Put(row).addColumn(fam, Bytes.toBytes("qual"),
2423            System.currentTimeMillis() + 2000, Bytes.toBytes("value")));
2424        fail("Expected IOE for TS out of configured timerange");
2425      } catch (FailedSanityCheckException ioe) {
2426        LOG.debug("Received expected exception", ioe);
2427        caughtExcep = true;
2428      }
2429      assertTrue("Should catch FailedSanityCheckException", caughtExcep);
2430    } finally {
2431      HBaseTestingUtility.closeRegionAndWAL(this.region);
2432      this.region = null;
2433    }
2434  }
2435
2436  @Test
2437  public void testScanner_DeleteOneFamilyNotAnother() throws IOException {
2438    byte[] fam1 = Bytes.toBytes("columnA");
2439    byte[] fam2 = Bytes.toBytes("columnB");
2440    this.region = initHRegion(tableName, method, CONF, fam1, fam2);
2441    try {
2442      byte[] rowA = Bytes.toBytes("rowA");
2443      byte[] rowB = Bytes.toBytes("rowB");
2444
2445      byte[] value = Bytes.toBytes("value");
2446
2447      Delete delete = new Delete(rowA);
2448      delete.addFamily(fam1);
2449
2450      region.delete(delete);
2451
2452      // now create data.
2453      Put put = new Put(rowA);
2454      put.addColumn(fam2, null, value);
2455      region.put(put);
2456
2457      put = new Put(rowB);
2458      put.addColumn(fam1, null, value);
2459      put.addColumn(fam2, null, value);
2460      region.put(put);
2461
2462      Scan scan = new Scan();
2463      scan.addFamily(fam1).addFamily(fam2);
2464      InternalScanner s = region.getScanner(scan);
2465      List<Cell> results = new ArrayList<>();
2466      s.next(results);
2467      assertTrue(CellUtil.matchingRows(results.get(0), rowA));
2468
2469      results.clear();
2470      s.next(results);
2471      assertTrue(CellUtil.matchingRows(results.get(0), rowB));
2472    } finally {
2473      HBaseTestingUtility.closeRegionAndWAL(this.region);
2474      this.region = null;
2475    }
2476  }
2477
2478  @Test
2479  public void testDataInMemoryWithoutWAL() throws IOException {
2480    FileSystem fs = FileSystem.get(CONF);
2481    Path rootDir = new Path(dir + "testDataInMemoryWithoutWAL");
2482    FSHLog hLog = new FSHLog(fs, rootDir, "testDataInMemoryWithoutWAL", CONF);
2483    // This chunk creation is done throughout the code base. Do we want to move it into core?
2484    // It is missing from this test. W/o it we NPE.
2485    ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null);
2486    HRegion region = initHRegion(tableName, null, null, false, Durability.SYNC_WAL, hLog,
2487        COLUMN_FAMILY_BYTES);
2488
2489    Cell originalCell = CellUtil.createCell(row, COLUMN_FAMILY_BYTES, qual1,
2490      System.currentTimeMillis(), KeyValue.Type.Put.getCode(), value1);
2491    final long originalSize = KeyValueUtil.length(originalCell);
2492
2493    Cell addCell = CellUtil.createCell(row, COLUMN_FAMILY_BYTES, qual1,
2494      System.currentTimeMillis(), KeyValue.Type.Put.getCode(), Bytes.toBytes("xxxxxxxxxx"));
2495    final long addSize = KeyValueUtil.length(addCell);
2496
2497    LOG.info("originalSize:" + originalSize
2498      + ", addSize:" + addSize);
2499    // start test. We expect that the addPut's durability will be replaced
2500    // by originalPut's durability.
2501
2502    // case 1:
2503    testDataInMemoryWithoutWAL(region,
2504            new Put(row).add(originalCell).setDurability(Durability.SKIP_WAL),
2505            new Put(row).add(addCell).setDurability(Durability.SKIP_WAL),
2506            originalSize + addSize);
2507
2508    // case 2:
2509    testDataInMemoryWithoutWAL(region,
2510            new Put(row).add(originalCell).setDurability(Durability.SKIP_WAL),
2511            new Put(row).add(addCell).setDurability(Durability.SYNC_WAL),
2512            originalSize + addSize);
2513
2514    // case 3:
2515    testDataInMemoryWithoutWAL(region,
2516            new Put(row).add(originalCell).setDurability(Durability.SYNC_WAL),
2517            new Put(row).add(addCell).setDurability(Durability.SKIP_WAL),
2518            0);
2519
2520    // case 4:
2521    testDataInMemoryWithoutWAL(region,
2522            new Put(row).add(originalCell).setDurability(Durability.SYNC_WAL),
2523            new Put(row).add(addCell).setDurability(Durability.SYNC_WAL),
2524            0);
2525  }
2526
2527  private static void testDataInMemoryWithoutWAL(HRegion region, Put originalPut,
2528          final Put addPut, long delta) throws IOException {
2529    final long initSize = region.getDataInMemoryWithoutWAL();
2530    // save normalCPHost and replaced by mockedCPHost
2531    RegionCoprocessorHost normalCPHost = region.getCoprocessorHost();
2532    RegionCoprocessorHost mockedCPHost = Mockito.mock(RegionCoprocessorHost.class);
2533    // Because the preBatchMutate returns void, we can't do usual Mockito when...then form. Must
2534    // do below format (from Mockito doc).
2535    Mockito.doAnswer(new Answer() {
2536      @Override
2537      public Object answer(InvocationOnMock invocation) throws Throwable {
2538        MiniBatchOperationInProgress<Mutation> mb = invocation.getArgument(0);
2539        mb.addOperationsFromCP(0, new Mutation[]{addPut});
2540        return null;
2541      }
2542    }).when(mockedCPHost).preBatchMutate(Mockito.isA(MiniBatchOperationInProgress.class));
2543    region.setCoprocessorHost(mockedCPHost);
2544    region.put(originalPut);
2545    region.setCoprocessorHost(normalCPHost);
2546    final long finalSize = region.getDataInMemoryWithoutWAL();
2547    assertEquals("finalSize:" + finalSize + ", initSize:"
2548      + initSize + ", delta:" + delta,finalSize, initSize + delta);
2549  }
2550
2551  @Test
2552  public void testDeleteColumns_PostInsert() throws IOException, InterruptedException {
2553    Delete delete = new Delete(row);
2554    delete.addColumns(fam1, qual1);
2555    doTestDelete_AndPostInsert(delete);
2556  }
2557
2558  @Test
2559  public void testaddFamily_PostInsert() throws IOException, InterruptedException {
2560    Delete delete = new Delete(row);
2561    delete.addFamily(fam1);
2562    doTestDelete_AndPostInsert(delete);
2563  }
2564
2565  public void doTestDelete_AndPostInsert(Delete delete) throws IOException, InterruptedException {
2566    this.region = initHRegion(tableName, method, CONF, fam1);
2567    try {
2568      EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge());
2569      Put put = new Put(row);
2570      put.addColumn(fam1, qual1, value1);
2571      region.put(put);
2572
2573      // now delete the value:
2574      region.delete(delete);
2575
2576      // ok put data:
2577      put = new Put(row);
2578      put.addColumn(fam1, qual1, value2);
2579      region.put(put);
2580
2581      // ok get:
2582      Get get = new Get(row);
2583      get.addColumn(fam1, qual1);
2584
2585      Result r = region.get(get);
2586      assertEquals(1, r.size());
2587      assertArrayEquals(value2, r.getValue(fam1, qual1));
2588
2589      // next:
2590      Scan scan = new Scan(row);
2591      scan.addColumn(fam1, qual1);
2592      InternalScanner s = region.getScanner(scan);
2593
2594      List<Cell> results = new ArrayList<>();
2595      assertEquals(false, s.next(results));
2596      assertEquals(1, results.size());
2597      Cell kv = results.get(0);
2598
2599      assertArrayEquals(value2, CellUtil.cloneValue(kv));
2600      assertArrayEquals(fam1, CellUtil.cloneFamily(kv));
2601      assertArrayEquals(qual1, CellUtil.cloneQualifier(kv));
2602      assertArrayEquals(row, CellUtil.cloneRow(kv));
2603    } finally {
2604      HBaseTestingUtility.closeRegionAndWAL(this.region);
2605      this.region = null;
2606    }
2607  }
2608
2609  @Test
2610  public void testDelete_CheckTimestampUpdated() throws IOException {
2611    byte[] row1 = Bytes.toBytes("row1");
2612    byte[] col1 = Bytes.toBytes("col1");
2613    byte[] col2 = Bytes.toBytes("col2");
2614    byte[] col3 = Bytes.toBytes("col3");
2615
2616    // Setting up region
2617    this.region = initHRegion(tableName, method, CONF, fam1);
2618    try {
2619      // Building checkerList
2620      List<Cell> kvs = new ArrayList<>();
2621      kvs.add(new KeyValue(row1, fam1, col1, null));
2622      kvs.add(new KeyValue(row1, fam1, col2, null));
2623      kvs.add(new KeyValue(row1, fam1, col3, null));
2624
2625      NavigableMap<byte[], List<Cell>> deleteMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
2626      deleteMap.put(fam1, kvs);
2627      region.delete(deleteMap, Durability.SYNC_WAL);
2628
2629      // extract the key values out the memstore:
2630      // This is kinda hacky, but better than nothing...
2631      long now = System.currentTimeMillis();
2632      AbstractMemStore memstore = (AbstractMemStore)region.getStore(fam1).memstore;
2633      Cell firstCell = memstore.getActive().first();
2634      assertTrue(firstCell.getTimestamp() <= now);
2635      now = firstCell.getTimestamp();
2636      for (Cell cell : memstore.getActive().getCellSet()) {
2637        assertTrue(cell.getTimestamp() <= now);
2638        now = cell.getTimestamp();
2639      }
2640    } finally {
2641      HBaseTestingUtility.closeRegionAndWAL(this.region);
2642      this.region = null;
2643    }
2644  }
2645
2646  // ////////////////////////////////////////////////////////////////////////////
2647  // Get tests
2648  // ////////////////////////////////////////////////////////////////////////////
2649  @Test
2650  public void testGet_FamilyChecker() throws IOException {
2651    byte[] row1 = Bytes.toBytes("row1");
2652    byte[] fam1 = Bytes.toBytes("fam1");
2653    byte[] fam2 = Bytes.toBytes("False");
2654    byte[] col1 = Bytes.toBytes("col1");
2655
2656    // Setting up region
2657    this.region = initHRegion(tableName, method, CONF, fam1);
2658    try {
2659      Get get = new Get(row1);
2660      get.addColumn(fam2, col1);
2661
2662      // Test
2663      try {
2664        region.get(get);
2665      } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
2666        assertFalse(false);
2667        return;
2668      }
2669      assertFalse(true);
2670    } finally {
2671      HBaseTestingUtility.closeRegionAndWAL(this.region);
2672      this.region = null;
2673    }
2674  }
2675
2676  @Test
2677  public void testGet_Basic() throws IOException {
2678    byte[] row1 = Bytes.toBytes("row1");
2679    byte[] fam1 = Bytes.toBytes("fam1");
2680    byte[] col1 = Bytes.toBytes("col1");
2681    byte[] col2 = Bytes.toBytes("col2");
2682    byte[] col3 = Bytes.toBytes("col3");
2683    byte[] col4 = Bytes.toBytes("col4");
2684    byte[] col5 = Bytes.toBytes("col5");
2685
2686    // Setting up region
2687    this.region = initHRegion(tableName, method, CONF, fam1);
2688    try {
2689      // Add to memstore
2690      Put put = new Put(row1);
2691      put.addColumn(fam1, col1, null);
2692      put.addColumn(fam1, col2, null);
2693      put.addColumn(fam1, col3, null);
2694      put.addColumn(fam1, col4, null);
2695      put.addColumn(fam1, col5, null);
2696      region.put(put);
2697
2698      Get get = new Get(row1);
2699      get.addColumn(fam1, col2);
2700      get.addColumn(fam1, col4);
2701      // Expected result
2702      KeyValue kv1 = new KeyValue(row1, fam1, col2);
2703      KeyValue kv2 = new KeyValue(row1, fam1, col4);
2704      KeyValue[] expected = { kv1, kv2 };
2705
2706      // Test
2707      Result res = region.get(get);
2708      assertEquals(expected.length, res.size());
2709      for (int i = 0; i < res.size(); i++) {
2710        assertTrue(CellUtil.matchingRows(expected[i], res.rawCells()[i]));
2711        assertTrue(CellUtil.matchingFamily(expected[i], res.rawCells()[i]));
2712        assertTrue(CellUtil.matchingQualifier(expected[i], res.rawCells()[i]));
2713      }
2714
2715      // Test using a filter on a Get
2716      Get g = new Get(row1);
2717      final int count = 2;
2718      g.setFilter(new ColumnCountGetFilter(count));
2719      res = region.get(g);
2720      assertEquals(count, res.size());
2721    } finally {
2722      HBaseTestingUtility.closeRegionAndWAL(this.region);
2723      this.region = null;
2724    }
2725  }
2726
2727  @Test
2728  public void testGet_Empty() throws IOException {
2729    byte[] row = Bytes.toBytes("row");
2730    byte[] fam = Bytes.toBytes("fam");
2731
2732    this.region = initHRegion(tableName, method, CONF, fam);
2733    try {
2734      Get get = new Get(row);
2735      get.addFamily(fam);
2736      Result r = region.get(get);
2737
2738      assertTrue(r.isEmpty());
2739    } finally {
2740      HBaseTestingUtility.closeRegionAndWAL(this.region);
2741      this.region = null;
2742    }
2743  }
2744
2745  @Test
2746  public void testGetWithFilter() throws IOException, InterruptedException {
2747    byte[] row1 = Bytes.toBytes("row1");
2748    byte[] fam1 = Bytes.toBytes("fam1");
2749    byte[] col1 = Bytes.toBytes("col1");
2750    byte[] value1 = Bytes.toBytes("value1");
2751    byte[] value2 = Bytes.toBytes("value2");
2752
2753    final int maxVersions = 3;
2754    HColumnDescriptor hcd = new HColumnDescriptor(fam1);
2755    hcd.setMaxVersions(maxVersions);
2756    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("testFilterAndColumnTracker"));
2757    htd.addFamily(hcd);
2758    ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null);
2759    HRegionInfo info = new HRegionInfo(htd.getTableName(), null, null, false);
2760    Path logDir = TEST_UTIL.getDataTestDirOnTestFS(method + ".log");
2761    final WAL wal = HBaseTestingUtility.createWal(TEST_UTIL.getConfiguration(), logDir, info);
2762    this.region = TEST_UTIL.createLocalHRegion(info, htd, wal);
2763
2764    try {
2765      // Put 4 version to memstore
2766      long ts = 0;
2767      Put put = new Put(row1, ts);
2768      put.addColumn(fam1, col1, value1);
2769      region.put(put);
2770      put = new Put(row1, ts + 1);
2771      put.addColumn(fam1, col1, Bytes.toBytes("filter1"));
2772      region.put(put);
2773      put = new Put(row1, ts + 2);
2774      put.addColumn(fam1, col1, Bytes.toBytes("filter2"));
2775      region.put(put);
2776      put = new Put(row1, ts + 3);
2777      put.addColumn(fam1, col1, value2);
2778      region.put(put);
2779
2780      Get get = new Get(row1);
2781      get.setMaxVersions();
2782      Result res = region.get(get);
2783      // Get 3 versions, the oldest version has gone from user view
2784      assertEquals(maxVersions, res.size());
2785
2786      get.setFilter(new ValueFilter(CompareOp.EQUAL, new SubstringComparator("value")));
2787      res = region.get(get);
2788      // When use value filter, the oldest version should still gone from user view and it
2789      // should only return one key vaule
2790      assertEquals(1, res.size());
2791      assertTrue(CellUtil.matchingValue(new KeyValue(row1, fam1, col1, value2), res.rawCells()[0]));
2792      assertEquals(ts + 3, res.rawCells()[0].getTimestamp());
2793
2794      region.flush(true);
2795      region.compact(true);
2796      Thread.sleep(1000);
2797      res = region.get(get);
2798      // After flush and compact, the result should be consistent with previous result
2799      assertEquals(1, res.size());
2800      assertTrue(CellUtil.matchingValue(new KeyValue(row1, fam1, col1, value2), res.rawCells()[0]));
2801    } finally {
2802      HBaseTestingUtility.closeRegionAndWAL(this.region);
2803      this.region = null;
2804    }
2805  }
2806
2807  // ////////////////////////////////////////////////////////////////////////////
2808  // Scanner tests
2809  // ////////////////////////////////////////////////////////////////////////////
2810  @Test
2811  public void testGetScanner_WithOkFamilies() throws IOException {
2812    byte[] fam1 = Bytes.toBytes("fam1");
2813    byte[] fam2 = Bytes.toBytes("fam2");
2814
2815    byte[][] families = { fam1, fam2 };
2816
2817    // Setting up region
2818    this.region = initHRegion(tableName, method, CONF, families);
2819    try {
2820      Scan scan = new Scan();
2821      scan.addFamily(fam1);
2822      scan.addFamily(fam2);
2823      try {
2824        region.getScanner(scan);
2825      } catch (Exception e) {
2826        assertTrue("Families could not be found in Region", false);
2827      }
2828    } finally {
2829      HBaseTestingUtility.closeRegionAndWAL(this.region);
2830      this.region = null;
2831    }
2832  }
2833
2834  @Test
2835  public void testGetScanner_WithNotOkFamilies() throws IOException {
2836    byte[] fam1 = Bytes.toBytes("fam1");
2837    byte[] fam2 = Bytes.toBytes("fam2");
2838
2839    byte[][] families = { fam1 };
2840
2841    // Setting up region
2842    this.region = initHRegion(tableName, method, CONF, families);
2843    try {
2844      Scan scan = new Scan();
2845      scan.addFamily(fam2);
2846      boolean ok = false;
2847      try {
2848        region.getScanner(scan);
2849      } catch (Exception e) {
2850        ok = true;
2851      }
2852      assertTrue("Families could not be found in Region", ok);
2853    } finally {
2854      HBaseTestingUtility.closeRegionAndWAL(this.region);
2855      this.region = null;
2856    }
2857  }
2858
2859  @Test
2860  public void testGetScanner_WithNoFamilies() throws IOException {
2861    byte[] row1 = Bytes.toBytes("row1");
2862    byte[] fam1 = Bytes.toBytes("fam1");
2863    byte[] fam2 = Bytes.toBytes("fam2");
2864    byte[] fam3 = Bytes.toBytes("fam3");
2865    byte[] fam4 = Bytes.toBytes("fam4");
2866
2867    byte[][] families = { fam1, fam2, fam3, fam4 };
2868
2869    // Setting up region
2870    this.region = initHRegion(tableName, method, CONF, families);
2871    try {
2872
2873      // Putting data in Region
2874      Put put = new Put(row1);
2875      put.addColumn(fam1, null, null);
2876      put.addColumn(fam2, null, null);
2877      put.addColumn(fam3, null, null);
2878      put.addColumn(fam4, null, null);
2879      region.put(put);
2880
2881      Scan scan = null;
2882      HRegion.RegionScannerImpl is = null;
2883
2884      // Testing to see how many scanners that is produced by getScanner,
2885      // starting
2886      // with known number, 2 - current = 1
2887      scan = new Scan();
2888      scan.addFamily(fam2);
2889      scan.addFamily(fam4);
2890      is = region.getScanner(scan);
2891      assertEquals(1, is.storeHeap.getHeap().size());
2892
2893      scan = new Scan();
2894      is = region.getScanner(scan);
2895      assertEquals(families.length - 1, is.storeHeap.getHeap().size());
2896    } finally {
2897      HBaseTestingUtility.closeRegionAndWAL(this.region);
2898      this.region = null;
2899    }
2900  }
2901
2902  /**
2903   * This method tests https://issues.apache.org/jira/browse/HBASE-2516.
2904   *
2905   * @throws IOException
2906   */
2907  @Test
2908  public void testGetScanner_WithRegionClosed() throws IOException {
2909    byte[] fam1 = Bytes.toBytes("fam1");
2910    byte[] fam2 = Bytes.toBytes("fam2");
2911
2912    byte[][] families = { fam1, fam2 };
2913
2914    // Setting up region
2915    try {
2916      this.region = initHRegion(tableName, method, CONF, families);
2917    } catch (IOException e) {
2918      e.printStackTrace();
2919      fail("Got IOException during initHRegion, " + e.getMessage());
2920    }
2921    try {
2922      region.closed.set(true);
2923      try {
2924        region.getScanner(null);
2925        fail("Expected to get an exception during getScanner on a region that is closed");
2926      } catch (NotServingRegionException e) {
2927        // this is the correct exception that is expected
2928      } catch (IOException e) {
2929        fail("Got wrong type of exception - should be a NotServingRegionException, " +
2930            "but was an IOException: "
2931            + e.getMessage());
2932      }
2933    } finally {
2934      HBaseTestingUtility.closeRegionAndWAL(this.region);
2935      this.region = null;
2936    }
2937  }
2938
2939  @Test
2940  public void testRegionScanner_Next() throws IOException {
2941    byte[] row1 = Bytes.toBytes("row1");
2942    byte[] row2 = Bytes.toBytes("row2");
2943    byte[] fam1 = Bytes.toBytes("fam1");
2944    byte[] fam2 = Bytes.toBytes("fam2");
2945    byte[] fam3 = Bytes.toBytes("fam3");
2946    byte[] fam4 = Bytes.toBytes("fam4");
2947
2948    byte[][] families = { fam1, fam2, fam3, fam4 };
2949    long ts = System.currentTimeMillis();
2950
2951    // Setting up region
2952    this.region = initHRegion(tableName, method, CONF, families);
2953    try {
2954      // Putting data in Region
2955      Put put = null;
2956      put = new Put(row1);
2957      put.addColumn(fam1, (byte[]) null, ts, null);
2958      put.addColumn(fam2, (byte[]) null, ts, null);
2959      put.addColumn(fam3, (byte[]) null, ts, null);
2960      put.addColumn(fam4, (byte[]) null, ts, null);
2961      region.put(put);
2962
2963      put = new Put(row2);
2964      put.addColumn(fam1, (byte[]) null, ts, null);
2965      put.addColumn(fam2, (byte[]) null, ts, null);
2966      put.addColumn(fam3, (byte[]) null, ts, null);
2967      put.addColumn(fam4, (byte[]) null, ts, null);
2968      region.put(put);
2969
2970      Scan scan = new Scan();
2971      scan.addFamily(fam2);
2972      scan.addFamily(fam4);
2973      InternalScanner is = region.getScanner(scan);
2974
2975      List<Cell> res = null;
2976
2977      // Result 1
2978      List<Cell> expected1 = new ArrayList<>();
2979      expected1.add(new KeyValue(row1, fam2, null, ts, KeyValue.Type.Put, null));
2980      expected1.add(new KeyValue(row1, fam4, null, ts, KeyValue.Type.Put, null));
2981
2982      res = new ArrayList<>();
2983      is.next(res);
2984      for (int i = 0; i < res.size(); i++) {
2985        assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected1.get(i), res.get(i)));
2986      }
2987
2988      // Result 2
2989      List<Cell> expected2 = new ArrayList<>();
2990      expected2.add(new KeyValue(row2, fam2, null, ts, KeyValue.Type.Put, null));
2991      expected2.add(new KeyValue(row2, fam4, null, ts, KeyValue.Type.Put, null));
2992
2993      res = new ArrayList<>();
2994      is.next(res);
2995      for (int i = 0; i < res.size(); i++) {
2996        assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected2.get(i), res.get(i)));
2997      }
2998    } finally {
2999      HBaseTestingUtility.closeRegionAndWAL(this.region);
3000      this.region = null;
3001    }
3002  }
3003
3004  @Test
3005  public void testScanner_ExplicitColumns_FromMemStore_EnforceVersions() throws IOException {
3006    byte[] row1 = Bytes.toBytes("row1");
3007    byte[] qf1 = Bytes.toBytes("qualifier1");
3008    byte[] qf2 = Bytes.toBytes("qualifier2");
3009    byte[] fam1 = Bytes.toBytes("fam1");
3010    byte[][] families = { fam1 };
3011
3012    long ts1 = System.currentTimeMillis();
3013    long ts2 = ts1 + 1;
3014    long ts3 = ts1 + 2;
3015
3016    // Setting up region
3017    this.region = initHRegion(tableName, method, CONF, families);
3018    try {
3019      // Putting data in Region
3020      Put put = null;
3021      KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null);
3022      KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null);
3023      KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null);
3024
3025      KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null);
3026      KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null);
3027      KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null);
3028
3029      put = new Put(row1);
3030      put.add(kv13);
3031      put.add(kv12);
3032      put.add(kv11);
3033      put.add(kv23);
3034      put.add(kv22);
3035      put.add(kv21);
3036      region.put(put);
3037
3038      // Expected
3039      List<Cell> expected = new ArrayList<>();
3040      expected.add(kv13);
3041      expected.add(kv12);
3042
3043      Scan scan = new Scan(row1);
3044      scan.addColumn(fam1, qf1);
3045      scan.setMaxVersions(MAX_VERSIONS);
3046      List<Cell> actual = new ArrayList<>();
3047      InternalScanner scanner = region.getScanner(scan);
3048
3049      boolean hasNext = scanner.next(actual);
3050      assertEquals(false, hasNext);
3051
3052      // Verify result
3053      for (int i = 0; i < expected.size(); i++) {
3054        assertEquals(expected.get(i), actual.get(i));
3055      }
3056    } finally {
3057      HBaseTestingUtility.closeRegionAndWAL(this.region);
3058      this.region = null;
3059    }
3060  }
3061
3062  @Test
3063  public void testScanner_ExplicitColumns_FromFilesOnly_EnforceVersions() throws IOException {
3064    byte[] row1 = Bytes.toBytes("row1");
3065    byte[] qf1 = Bytes.toBytes("qualifier1");
3066    byte[] qf2 = Bytes.toBytes("qualifier2");
3067    byte[] fam1 = Bytes.toBytes("fam1");
3068    byte[][] families = { fam1 };
3069
3070    long ts1 = 1; // System.currentTimeMillis();
3071    long ts2 = ts1 + 1;
3072    long ts3 = ts1 + 2;
3073
3074    // Setting up region
3075    this.region = initHRegion(tableName, method, CONF, families);
3076    try {
3077      // Putting data in Region
3078      Put put = null;
3079      KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null);
3080      KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null);
3081      KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null);
3082
3083      KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null);
3084      KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null);
3085      KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null);
3086
3087      put = new Put(row1);
3088      put.add(kv13);
3089      put.add(kv12);
3090      put.add(kv11);
3091      put.add(kv23);
3092      put.add(kv22);
3093      put.add(kv21);
3094      region.put(put);
3095      region.flush(true);
3096
3097      // Expected
3098      List<Cell> expected = new ArrayList<>();
3099      expected.add(kv13);
3100      expected.add(kv12);
3101      expected.add(kv23);
3102      expected.add(kv22);
3103
3104      Scan scan = new Scan(row1);
3105      scan.addColumn(fam1, qf1);
3106      scan.addColumn(fam1, qf2);
3107      scan.setMaxVersions(MAX_VERSIONS);
3108      List<Cell> actual = new ArrayList<>();
3109      InternalScanner scanner = region.getScanner(scan);
3110
3111      boolean hasNext = scanner.next(actual);
3112      assertEquals(false, hasNext);
3113
3114      // Verify result
3115      for (int i = 0; i < expected.size(); i++) {
3116        assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i)));
3117      }
3118    } finally {
3119      HBaseTestingUtility.closeRegionAndWAL(this.region);
3120      this.region = null;
3121    }
3122  }
3123
3124  @Test
3125  public void testScanner_ExplicitColumns_FromMemStoreAndFiles_EnforceVersions() throws
3126      IOException {
3127    byte[] row1 = Bytes.toBytes("row1");
3128    byte[] fam1 = Bytes.toBytes("fam1");
3129    byte[][] families = { fam1 };
3130    byte[] qf1 = Bytes.toBytes("qualifier1");
3131    byte[] qf2 = Bytes.toBytes("qualifier2");
3132
3133    long ts1 = 1;
3134    long ts2 = ts1 + 1;
3135    long ts3 = ts1 + 2;
3136    long ts4 = ts1 + 3;
3137
3138    // Setting up region
3139    this.region = initHRegion(tableName, method, CONF, families);
3140    try {
3141      // Putting data in Region
3142      KeyValue kv14 = new KeyValue(row1, fam1, qf1, ts4, KeyValue.Type.Put, null);
3143      KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null);
3144      KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null);
3145      KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null);
3146
3147      KeyValue kv24 = new KeyValue(row1, fam1, qf2, ts4, KeyValue.Type.Put, null);
3148      KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null);
3149      KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null);
3150      KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null);
3151
3152      Put put = null;
3153      put = new Put(row1);
3154      put.add(kv14);
3155      put.add(kv24);
3156      region.put(put);
3157      region.flush(true);
3158
3159      put = new Put(row1);
3160      put.add(kv23);
3161      put.add(kv13);
3162      region.put(put);
3163      region.flush(true);
3164
3165      put = new Put(row1);
3166      put.add(kv22);
3167      put.add(kv12);
3168      region.put(put);
3169      region.flush(true);
3170
3171      put = new Put(row1);
3172      put.add(kv21);
3173      put.add(kv11);
3174      region.put(put);
3175
3176      // Expected
3177      List<Cell> expected = new ArrayList<>();
3178      expected.add(kv14);
3179      expected.add(kv13);
3180      expected.add(kv12);
3181      expected.add(kv24);
3182      expected.add(kv23);
3183      expected.add(kv22);
3184
3185      Scan scan = new Scan(row1);
3186      scan.addColumn(fam1, qf1);
3187      scan.addColumn(fam1, qf2);
3188      int versions = 3;
3189      scan.setMaxVersions(versions);
3190      List<Cell> actual = new ArrayList<>();
3191      InternalScanner scanner = region.getScanner(scan);
3192
3193      boolean hasNext = scanner.next(actual);
3194      assertEquals(false, hasNext);
3195
3196      // Verify result
3197      for (int i = 0; i < expected.size(); i++) {
3198        assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i)));
3199      }
3200    } finally {
3201      HBaseTestingUtility.closeRegionAndWAL(this.region);
3202      this.region = null;
3203    }
3204  }
3205
3206  @Test
3207  public void testScanner_Wildcard_FromMemStore_EnforceVersions() throws IOException {
3208    byte[] row1 = Bytes.toBytes("row1");
3209    byte[] qf1 = Bytes.toBytes("qualifier1");
3210    byte[] qf2 = Bytes.toBytes("qualifier2");
3211    byte[] fam1 = Bytes.toBytes("fam1");
3212    byte[][] families = { fam1 };
3213
3214    long ts1 = System.currentTimeMillis();
3215    long ts2 = ts1 + 1;
3216    long ts3 = ts1 + 2;
3217
3218    // Setting up region
3219    this.region = initHRegion(tableName, method, CONF, families);
3220    try {
3221      // Putting data in Region
3222      Put put = null;
3223      KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null);
3224      KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null);
3225      KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null);
3226
3227      KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null);
3228      KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null);
3229      KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null);
3230
3231      put = new Put(row1);
3232      put.add(kv13);
3233      put.add(kv12);
3234      put.add(kv11);
3235      put.add(kv23);
3236      put.add(kv22);
3237      put.add(kv21);
3238      region.put(put);
3239
3240      // Expected
3241      List<Cell> expected = new ArrayList<>();
3242      expected.add(kv13);
3243      expected.add(kv12);
3244      expected.add(kv23);
3245      expected.add(kv22);
3246
3247      Scan scan = new Scan(row1);
3248      scan.addFamily(fam1);
3249      scan.setMaxVersions(MAX_VERSIONS);
3250      List<Cell> actual = new ArrayList<>();
3251      InternalScanner scanner = region.getScanner(scan);
3252
3253      boolean hasNext = scanner.next(actual);
3254      assertEquals(false, hasNext);
3255
3256      // Verify result
3257      for (int i = 0; i < expected.size(); i++) {
3258        assertEquals(expected.get(i), actual.get(i));
3259      }
3260    } finally {
3261      HBaseTestingUtility.closeRegionAndWAL(this.region);
3262      this.region = null;
3263    }
3264  }
3265
3266  @Test
3267  public void testScanner_Wildcard_FromFilesOnly_EnforceVersions() throws IOException {
3268    byte[] row1 = Bytes.toBytes("row1");
3269    byte[] qf1 = Bytes.toBytes("qualifier1");
3270    byte[] qf2 = Bytes.toBytes("qualifier2");
3271    byte[] fam1 = Bytes.toBytes("fam1");
3272
3273    long ts1 = 1; // System.currentTimeMillis();
3274    long ts2 = ts1 + 1;
3275    long ts3 = ts1 + 2;
3276
3277    // Setting up region
3278    this.region = initHRegion(tableName, method, CONF, fam1);
3279    try {
3280      // Putting data in Region
3281      Put put = null;
3282      KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null);
3283      KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null);
3284      KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null);
3285
3286      KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null);
3287      KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null);
3288      KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null);
3289
3290      put = new Put(row1);
3291      put.add(kv13);
3292      put.add(kv12);
3293      put.add(kv11);
3294      put.add(kv23);
3295      put.add(kv22);
3296      put.add(kv21);
3297      region.put(put);
3298      region.flush(true);
3299
3300      // Expected
3301      List<Cell> expected = new ArrayList<>();
3302      expected.add(kv13);
3303      expected.add(kv12);
3304      expected.add(kv23);
3305      expected.add(kv22);
3306
3307      Scan scan = new Scan(row1);
3308      scan.addFamily(fam1);
3309      scan.setMaxVersions(MAX_VERSIONS);
3310      List<Cell> actual = new ArrayList<>();
3311      InternalScanner scanner = region.getScanner(scan);
3312
3313      boolean hasNext = scanner.next(actual);
3314      assertEquals(false, hasNext);
3315
3316      // Verify result
3317      for (int i = 0; i < expected.size(); i++) {
3318        assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i)));
3319      }
3320    } finally {
3321      HBaseTestingUtility.closeRegionAndWAL(this.region);
3322      this.region = null;
3323    }
3324  }
3325
3326  @Test
3327  public void testScanner_StopRow1542() throws IOException {
3328    byte[] family = Bytes.toBytes("testFamily");
3329    this.region = initHRegion(tableName, method, CONF, family);
3330    try {
3331      byte[] row1 = Bytes.toBytes("row111");
3332      byte[] row2 = Bytes.toBytes("row222");
3333      byte[] row3 = Bytes.toBytes("row333");
3334      byte[] row4 = Bytes.toBytes("row444");
3335      byte[] row5 = Bytes.toBytes("row555");
3336
3337      byte[] col1 = Bytes.toBytes("Pub111");
3338      byte[] col2 = Bytes.toBytes("Pub222");
3339
3340      Put put = new Put(row1);
3341      put.addColumn(family, col1, Bytes.toBytes(10L));
3342      region.put(put);
3343
3344      put = new Put(row2);
3345      put.addColumn(family, col1, Bytes.toBytes(15L));
3346      region.put(put);
3347
3348      put = new Put(row3);
3349      put.addColumn(family, col2, Bytes.toBytes(20L));
3350      region.put(put);
3351
3352      put = new Put(row4);
3353      put.addColumn(family, col2, Bytes.toBytes(30L));
3354      region.put(put);
3355
3356      put = new Put(row5);
3357      put.addColumn(family, col1, Bytes.toBytes(40L));
3358      region.put(put);
3359
3360      Scan scan = new Scan(row3, row4);
3361      scan.setMaxVersions();
3362      scan.addColumn(family, col1);
3363      InternalScanner s = region.getScanner(scan);
3364
3365      List<Cell> results = new ArrayList<>();
3366      assertEquals(false, s.next(results));
3367      assertEquals(0, results.size());
3368    } finally {
3369      HBaseTestingUtility.closeRegionAndWAL(this.region);
3370      this.region = null;
3371    }
3372  }
3373
3374  @Test
3375  public void testScanner_Wildcard_FromMemStoreAndFiles_EnforceVersions() throws IOException {
3376    byte[] row1 = Bytes.toBytes("row1");
3377    byte[] fam1 = Bytes.toBytes("fam1");
3378    byte[] qf1 = Bytes.toBytes("qualifier1");
3379    byte[] qf2 = Bytes.toBytes("quateslifier2");
3380
3381    long ts1 = 1;
3382    long ts2 = ts1 + 1;
3383    long ts3 = ts1 + 2;
3384    long ts4 = ts1 + 3;
3385
3386    // Setting up region
3387    this.region = initHRegion(tableName, method, CONF, fam1);
3388    try {
3389      // Putting data in Region
3390      KeyValue kv14 = new KeyValue(row1, fam1, qf1, ts4, KeyValue.Type.Put, null);
3391      KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null);
3392      KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null);
3393      KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null);
3394
3395      KeyValue kv24 = new KeyValue(row1, fam1, qf2, ts4, KeyValue.Type.Put, null);
3396      KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null);
3397      KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null);
3398      KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null);
3399
3400      Put put = null;
3401      put = new Put(row1);
3402      put.add(kv14);
3403      put.add(kv24);
3404      region.put(put);
3405      region.flush(true);
3406
3407      put = new Put(row1);
3408      put.add(kv23);
3409      put.add(kv13);
3410      region.put(put);
3411      region.flush(true);
3412
3413      put = new Put(row1);
3414      put.add(kv22);
3415      put.add(kv12);
3416      region.put(put);
3417      region.flush(true);
3418
3419      put = new Put(row1);
3420      put.add(kv21);
3421      put.add(kv11);
3422      region.put(put);
3423
3424      // Expected
3425      List<KeyValue> expected = new ArrayList<>();
3426      expected.add(kv14);
3427      expected.add(kv13);
3428      expected.add(kv12);
3429      expected.add(kv24);
3430      expected.add(kv23);
3431      expected.add(kv22);
3432
3433      Scan scan = new Scan(row1);
3434      int versions = 3;
3435      scan.setMaxVersions(versions);
3436      List<Cell> actual = new ArrayList<>();
3437      InternalScanner scanner = region.getScanner(scan);
3438
3439      boolean hasNext = scanner.next(actual);
3440      assertEquals(false, hasNext);
3441
3442      // Verify result
3443      for (int i = 0; i < expected.size(); i++) {
3444        assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i)));
3445      }
3446    } finally {
3447      HBaseTestingUtility.closeRegionAndWAL(this.region);
3448      this.region = null;
3449    }
3450  }
3451
3452  /**
3453   * Added for HBASE-5416
3454   *
3455   * Here we test scan optimization when only subset of CFs are used in filter
3456   * conditions.
3457   */
3458  @Test
3459  public void testScanner_JoinedScanners() throws IOException {
3460    byte[] cf_essential = Bytes.toBytes("essential");
3461    byte[] cf_joined = Bytes.toBytes("joined");
3462    byte[] cf_alpha = Bytes.toBytes("alpha");
3463    this.region = initHRegion(tableName, method, CONF, cf_essential, cf_joined, cf_alpha);
3464    try {
3465      byte[] row1 = Bytes.toBytes("row1");
3466      byte[] row2 = Bytes.toBytes("row2");
3467      byte[] row3 = Bytes.toBytes("row3");
3468
3469      byte[] col_normal = Bytes.toBytes("d");
3470      byte[] col_alpha = Bytes.toBytes("a");
3471
3472      byte[] filtered_val = Bytes.toBytes(3);
3473
3474      Put put = new Put(row1);
3475      put.addColumn(cf_essential, col_normal, Bytes.toBytes(1));
3476      put.addColumn(cf_joined, col_alpha, Bytes.toBytes(1));
3477      region.put(put);
3478
3479      put = new Put(row2);
3480      put.addColumn(cf_essential, col_alpha, Bytes.toBytes(2));
3481      put.addColumn(cf_joined, col_normal, Bytes.toBytes(2));
3482      put.addColumn(cf_alpha, col_alpha, Bytes.toBytes(2));
3483      region.put(put);
3484
3485      put = new Put(row3);
3486      put.addColumn(cf_essential, col_normal, filtered_val);
3487      put.addColumn(cf_joined, col_normal, filtered_val);
3488      region.put(put);
3489
3490      // Check two things:
3491      // 1. result list contains expected values
3492      // 2. result list is sorted properly
3493
3494      Scan scan = new Scan();
3495      Filter filter = new SingleColumnValueExcludeFilter(cf_essential, col_normal,
3496          CompareOp.NOT_EQUAL, filtered_val);
3497      scan.setFilter(filter);
3498      scan.setLoadColumnFamiliesOnDemand(true);
3499      InternalScanner s = region.getScanner(scan);
3500
3501      List<Cell> results = new ArrayList<>();
3502      assertTrue(s.next(results));
3503      assertEquals(1, results.size());
3504      results.clear();
3505
3506      assertTrue(s.next(results));
3507      assertEquals(3, results.size());
3508      assertTrue("orderCheck", CellUtil.matchingFamily(results.get(0), cf_alpha));
3509      assertTrue("orderCheck", CellUtil.matchingFamily(results.get(1), cf_essential));
3510      assertTrue("orderCheck", CellUtil.matchingFamily(results.get(2), cf_joined));
3511      results.clear();
3512
3513      assertFalse(s.next(results));
3514      assertEquals(0, results.size());
3515    } finally {
3516      HBaseTestingUtility.closeRegionAndWAL(this.region);
3517      this.region = null;
3518    }
3519  }
3520
3521  /**
3522   * HBASE-5416
3523   *
3524   * Test case when scan limits amount of KVs returned on each next() call.
3525   */
3526  @Test
3527  public void testScanner_JoinedScannersWithLimits() throws IOException {
3528    final byte[] cf_first = Bytes.toBytes("first");
3529    final byte[] cf_second = Bytes.toBytes("second");
3530
3531    this.region = initHRegion(tableName, method, CONF, cf_first, cf_second);
3532    try {
3533      final byte[] col_a = Bytes.toBytes("a");
3534      final byte[] col_b = Bytes.toBytes("b");
3535
3536      Put put;
3537
3538      for (int i = 0; i < 10; i++) {
3539        put = new Put(Bytes.toBytes("r" + Integer.toString(i)));
3540        put.addColumn(cf_first, col_a, Bytes.toBytes(i));
3541        if (i < 5) {
3542          put.addColumn(cf_first, col_b, Bytes.toBytes(i));
3543          put.addColumn(cf_second, col_a, Bytes.toBytes(i));
3544          put.addColumn(cf_second, col_b, Bytes.toBytes(i));
3545        }
3546        region.put(put);
3547      }
3548
3549      Scan scan = new Scan();
3550      scan.setLoadColumnFamiliesOnDemand(true);
3551      Filter bogusFilter = new FilterBase() {
3552        @Override
3553        public ReturnCode filterCell(final Cell ignored) throws IOException {
3554          return ReturnCode.INCLUDE;
3555        }
3556        @Override
3557        public boolean isFamilyEssential(byte[] name) {
3558          return Bytes.equals(name, cf_first);
3559        }
3560      };
3561
3562      scan.setFilter(bogusFilter);
3563      InternalScanner s = region.getScanner(scan);
3564
3565      // Our data looks like this:
3566      // r0: first:a, first:b, second:a, second:b
3567      // r1: first:a, first:b, second:a, second:b
3568      // r2: first:a, first:b, second:a, second:b
3569      // r3: first:a, first:b, second:a, second:b
3570      // r4: first:a, first:b, second:a, second:b
3571      // r5: first:a
3572      // r6: first:a
3573      // r7: first:a
3574      // r8: first:a
3575      // r9: first:a
3576
3577      // But due to next's limit set to 3, we should get this:
3578      // r0: first:a, first:b, second:a
3579      // r0: second:b
3580      // r1: first:a, first:b, second:a
3581      // r1: second:b
3582      // r2: first:a, first:b, second:a
3583      // r2: second:b
3584      // r3: first:a, first:b, second:a
3585      // r3: second:b
3586      // r4: first:a, first:b, second:a
3587      // r4: second:b
3588      // r5: first:a
3589      // r6: first:a
3590      // r7: first:a
3591      // r8: first:a
3592      // r9: first:a
3593
3594      List<Cell> results = new ArrayList<>();
3595      int index = 0;
3596      ScannerContext scannerContext = ScannerContext.newBuilder().setBatchLimit(3).build();
3597      while (true) {
3598        boolean more = s.next(results, scannerContext);
3599        if ((index >> 1) < 5) {
3600          if (index % 2 == 0) {
3601            assertEquals(3, results.size());
3602          } else {
3603            assertEquals(1, results.size());
3604          }
3605        } else {
3606          assertEquals(1, results.size());
3607        }
3608        results.clear();
3609        index++;
3610        if (!more) {
3611          break;
3612        }
3613      }
3614    } finally {
3615      HBaseTestingUtility.closeRegionAndWAL(this.region);
3616      this.region = null;
3617    }
3618  }
3619
3620  /**
3621   * Write an HFile block full with Cells whose qualifier that are identical between
3622   * 0 and Short.MAX_VALUE. See HBASE-13329.
3623   * @throws Exception
3624   */
3625  @Test
3626  public void testLongQualifier() throws Exception {
3627    byte[] family = Bytes.toBytes("family");
3628    this.region = initHRegion(tableName, method, CONF, family);
3629    byte[] q = new byte[Short.MAX_VALUE+2];
3630    Arrays.fill(q, 0, q.length-1, (byte)42);
3631    for (byte i=0; i<10; i++) {
3632      Put p = new Put(Bytes.toBytes("row"));
3633      // qualifiers that differ past Short.MAX_VALUE
3634      q[q.length-1]=i;
3635      p.addColumn(family, q, q);
3636      region.put(p);
3637    }
3638    region.flush(false);
3639    HBaseTestingUtility.closeRegionAndWAL(this.region);
3640    this.region = null;
3641  }
3642
3643  /**
3644   * Flushes the cache in a thread while scanning. The tests verify that the
3645   * scan is coherent - e.g. the returned results are always of the same or
3646   * later update as the previous results.
3647   *
3648   * @throws IOException
3649   *           scan / compact
3650   * @throws InterruptedException
3651   *           thread join
3652   */
3653  @Test
3654  public void testFlushCacheWhileScanning() throws IOException, InterruptedException {
3655    byte[] family = Bytes.toBytes("family");
3656    int numRows = 1000;
3657    int flushAndScanInterval = 10;
3658    int compactInterval = 10 * flushAndScanInterval;
3659
3660    this.region = initHRegion(tableName, method, CONF, family);
3661    FlushThread flushThread = new FlushThread();
3662    try {
3663      flushThread.start();
3664
3665      Scan scan = new Scan();
3666      scan.addFamily(family);
3667      scan.setFilter(new SingleColumnValueFilter(family, qual1, CompareOp.EQUAL,
3668          new BinaryComparator(Bytes.toBytes(5L))));
3669
3670      int expectedCount = 0;
3671      List<Cell> res = new ArrayList<>();
3672
3673      boolean toggle = true;
3674      for (long i = 0; i < numRows; i++) {
3675        Put put = new Put(Bytes.toBytes(i));
3676        put.setDurability(Durability.SKIP_WAL);
3677        put.addColumn(family, qual1, Bytes.toBytes(i % 10));
3678        region.put(put);
3679
3680        if (i != 0 && i % compactInterval == 0) {
3681          LOG.debug("iteration = " + i+ " ts="+System.currentTimeMillis());
3682          region.compact(true);
3683        }
3684
3685        if (i % 10 == 5L) {
3686          expectedCount++;
3687        }
3688
3689        if (i != 0 && i % flushAndScanInterval == 0) {
3690          res.clear();
3691          InternalScanner scanner = region.getScanner(scan);
3692          if (toggle) {
3693            flushThread.flush();
3694          }
3695          while (scanner.next(res))
3696            ;
3697          if (!toggle) {
3698            flushThread.flush();
3699          }
3700          assertEquals("toggle="+toggle+"i=" + i + " ts="+System.currentTimeMillis(),
3701              expectedCount, res.size());
3702          toggle = !toggle;
3703        }
3704      }
3705
3706    } finally {
3707      try {
3708        flushThread.done();
3709        flushThread.join();
3710        flushThread.checkNoError();
3711      } catch (InterruptedException ie) {
3712        LOG.warn("Caught exception when joining with flushThread", ie);
3713      }
3714      HBaseTestingUtility.closeRegionAndWAL(this.region);
3715      this.region = null;
3716    }
3717  }
3718
3719  protected class FlushThread extends Thread {
3720    private volatile boolean done;
3721    private Throwable error = null;
3722
3723    FlushThread() {
3724      super("FlushThread");
3725    }
3726
3727    public void done() {
3728      done = true;
3729      synchronized (this) {
3730        interrupt();
3731      }
3732    }
3733
3734    public void checkNoError() {
3735      if (error != null) {
3736        assertNull(error);
3737      }
3738    }
3739
3740    @Override
3741    public void run() {
3742      done = false;
3743      while (!done) {
3744        synchronized (this) {
3745          try {
3746            wait();
3747          } catch (InterruptedException ignored) {
3748            if (done) {
3749              break;
3750            }
3751          }
3752        }
3753        try {
3754          region.flush(true);
3755        } catch (IOException e) {
3756          if (!done) {
3757            LOG.error("Error while flushing cache", e);
3758            error = e;
3759          }
3760          break;
3761        } catch (Throwable t) {
3762          LOG.error("Uncaught exception", t);
3763          throw t;
3764        }
3765      }
3766    }
3767
3768    public void flush() {
3769      synchronized (this) {
3770        notify();
3771      }
3772    }
3773  }
3774
3775  /**
3776   * Writes very wide records and scans for the latest every time.. Flushes and
3777   * compacts the region every now and then to keep things realistic.
3778   *
3779   * @throws IOException
3780   *           by flush / scan / compaction
3781   * @throws InterruptedException
3782   *           when joining threads
3783   */
3784  @Test
3785  public void testWritesWhileScanning() throws IOException, InterruptedException {
3786    int testCount = 100;
3787    int numRows = 1;
3788    int numFamilies = 10;
3789    int numQualifiers = 100;
3790    int flushInterval = 7;
3791    int compactInterval = 5 * flushInterval;
3792    byte[][] families = new byte[numFamilies][];
3793    for (int i = 0; i < numFamilies; i++) {
3794      families[i] = Bytes.toBytes("family" + i);
3795    }
3796    byte[][] qualifiers = new byte[numQualifiers][];
3797    for (int i = 0; i < numQualifiers; i++) {
3798      qualifiers[i] = Bytes.toBytes("qual" + i);
3799    }
3800
3801    this.region = initHRegion(tableName, method, CONF, families);
3802    FlushThread flushThread = new FlushThread();
3803    PutThread putThread = new PutThread(numRows, families, qualifiers);
3804    try {
3805      putThread.start();
3806      putThread.waitForFirstPut();
3807
3808      flushThread.start();
3809
3810      Scan scan = new Scan(Bytes.toBytes("row0"), Bytes.toBytes("row1"));
3811
3812      int expectedCount = numFamilies * numQualifiers;
3813      List<Cell> res = new ArrayList<>();
3814
3815      long prevTimestamp = 0L;
3816      for (int i = 0; i < testCount; i++) {
3817
3818        if (i != 0 && i % compactInterval == 0) {
3819          region.compact(true);
3820          for (HStore store : region.getStores()) {
3821            store.closeAndArchiveCompactedFiles();
3822          }
3823        }
3824
3825        if (i != 0 && i % flushInterval == 0) {
3826          flushThread.flush();
3827        }
3828
3829        boolean previousEmpty = res.isEmpty();
3830        res.clear();
3831        InternalScanner scanner = region.getScanner(scan);
3832        while (scanner.next(res))
3833          ;
3834        if (!res.isEmpty() || !previousEmpty || i > compactInterval) {
3835          assertEquals("i=" + i, expectedCount, res.size());
3836          long timestamp = res.get(0).getTimestamp();
3837          assertTrue("Timestamps were broke: " + timestamp + " prev: " + prevTimestamp,
3838              timestamp >= prevTimestamp);
3839          prevTimestamp = timestamp;
3840        }
3841      }
3842
3843      putThread.done();
3844
3845      region.flush(true);
3846
3847    } finally {
3848      try {
3849        flushThread.done();
3850        flushThread.join();
3851        flushThread.checkNoError();
3852
3853        putThread.join();
3854        putThread.checkNoError();
3855      } catch (InterruptedException ie) {
3856        LOG.warn("Caught exception when joining with flushThread", ie);
3857      }
3858
3859      try {
3860          HBaseTestingUtility.closeRegionAndWAL(this.region);
3861      } catch (DroppedSnapshotException dse) {
3862        // We could get this on way out because we interrupt the background flusher and it could
3863        // fail anywhere causing a DSE over in the background flusher... only it is not properly
3864        // dealt with so could still be memory hanging out when we get to here -- memory we can't
3865        // flush because the accounting is 'off' since original DSE.
3866      }
3867      this.region = null;
3868    }
3869  }
3870
3871  protected class PutThread extends Thread {
3872    private volatile boolean done;
3873    private volatile int numPutsFinished = 0;
3874
3875    private Throwable error = null;
3876    private int numRows;
3877    private byte[][] families;
3878    private byte[][] qualifiers;
3879
3880    private PutThread(int numRows, byte[][] families, byte[][] qualifiers) {
3881      super("PutThread");
3882      this.numRows = numRows;
3883      this.families = families;
3884      this.qualifiers = qualifiers;
3885    }
3886
3887    /**
3888     * Block calling thread until this instance of PutThread has put at least one row.
3889     */
3890    public void waitForFirstPut() throws InterruptedException {
3891      // wait until put thread actually puts some data
3892      while (isAlive() && numPutsFinished == 0) {
3893        checkNoError();
3894        Thread.sleep(50);
3895      }
3896    }
3897
3898    public void done() {
3899      done = true;
3900      synchronized (this) {
3901        interrupt();
3902      }
3903    }
3904
3905    public void checkNoError() {
3906      if (error != null) {
3907        assertNull(error);
3908      }
3909    }
3910
3911    @Override
3912    public void run() {
3913      done = false;
3914      while (!done) {
3915        try {
3916          for (int r = 0; r < numRows; r++) {
3917            byte[] row = Bytes.toBytes("row" + r);
3918            Put put = new Put(row);
3919            put.setDurability(Durability.SKIP_WAL);
3920            byte[] value = Bytes.toBytes(String.valueOf(numPutsFinished));
3921            for (byte[] family : families) {
3922              for (byte[] qualifier : qualifiers) {
3923                put.addColumn(family, qualifier, numPutsFinished, value);
3924              }
3925            }
3926            region.put(put);
3927            numPutsFinished++;
3928            if (numPutsFinished > 0 && numPutsFinished % 47 == 0) {
3929              System.out.println("put iteration = " + numPutsFinished);
3930              Delete delete = new Delete(row, (long) numPutsFinished - 30);
3931              region.delete(delete);
3932            }
3933            numPutsFinished++;
3934          }
3935        } catch (InterruptedIOException e) {
3936          // This is fine. It means we are done, or didn't get the lock on time
3937          LOG.info("Interrupted", e);
3938        } catch (IOException e) {
3939          LOG.error("Error while putting records", e);
3940          error = e;
3941          break;
3942        }
3943      }
3944
3945    }
3946
3947  }
3948
3949  /**
3950   * Writes very wide records and gets the latest row every time.. Flushes and
3951   * compacts the region aggressivly to catch issues.
3952   *
3953   * @throws IOException
3954   *           by flush / scan / compaction
3955   * @throws InterruptedException
3956   *           when joining threads
3957   */
3958  @Test
3959  public void testWritesWhileGetting() throws Exception {
3960    int testCount = 50;
3961    int numRows = 1;
3962    int numFamilies = 10;
3963    int numQualifiers = 100;
3964    int compactInterval = 100;
3965    byte[][] families = new byte[numFamilies][];
3966    for (int i = 0; i < numFamilies; i++) {
3967      families[i] = Bytes.toBytes("family" + i);
3968    }
3969    byte[][] qualifiers = new byte[numQualifiers][];
3970    for (int i = 0; i < numQualifiers; i++) {
3971      qualifiers[i] = Bytes.toBytes("qual" + i);
3972    }
3973
3974
3975    // This test flushes constantly and can cause many files to be created,
3976    // possibly
3977    // extending over the ulimit. Make sure compactions are aggressive in
3978    // reducing
3979    // the number of HFiles created.
3980    Configuration conf = HBaseConfiguration.create(CONF);
3981    conf.setInt("hbase.hstore.compaction.min", 1);
3982    conf.setInt("hbase.hstore.compaction.max", 1000);
3983    this.region = initHRegion(tableName, method, conf, families);
3984    PutThread putThread = null;
3985    MultithreadedTestUtil.TestContext ctx = new MultithreadedTestUtil.TestContext(conf);
3986    try {
3987      putThread = new PutThread(numRows, families, qualifiers);
3988      putThread.start();
3989      putThread.waitForFirstPut();
3990
3991      // Add a thread that flushes as fast as possible
3992      ctx.addThread(new RepeatingTestThread(ctx) {
3993
3994        @Override
3995        public void doAnAction() throws Exception {
3996          region.flush(true);
3997          // Compact regularly to avoid creating too many files and exceeding
3998          // the ulimit.
3999          region.compact(false);
4000          for (HStore store : region.getStores()) {
4001            store.closeAndArchiveCompactedFiles();
4002          }
4003        }
4004      });
4005      ctx.startThreads();
4006
4007      Get get = new Get(Bytes.toBytes("row0"));
4008      Result result = null;
4009
4010      int expectedCount = numFamilies * numQualifiers;
4011
4012      long prevTimestamp = 0L;
4013      for (int i = 0; i < testCount; i++) {
4014        LOG.info("testWritesWhileGetting verify turn " + i);
4015        boolean previousEmpty = result == null || result.isEmpty();
4016        result = region.get(get);
4017        if (!result.isEmpty() || !previousEmpty || i > compactInterval) {
4018          assertEquals("i=" + i, expectedCount, result.size());
4019          // TODO this was removed, now what dangit?!
4020          // search looking for the qualifier in question?
4021          long timestamp = 0;
4022          for (Cell kv : result.rawCells()) {
4023            if (CellUtil.matchingFamily(kv, families[0])
4024                && CellUtil.matchingQualifier(kv, qualifiers[0])) {
4025              timestamp = kv.getTimestamp();
4026            }
4027          }
4028          assertTrue(timestamp >= prevTimestamp);
4029          prevTimestamp = timestamp;
4030          Cell previousKV = null;
4031
4032          for (Cell kv : result.rawCells()) {
4033            byte[] thisValue = CellUtil.cloneValue(kv);
4034            if (previousKV != null) {
4035              if (Bytes.compareTo(CellUtil.cloneValue(previousKV), thisValue) != 0) {
4036                LOG.warn("These two KV should have the same value." + " Previous KV:" + previousKV
4037                    + "(memStoreTS:" + previousKV.getSequenceId() + ")" + ", New KV: " + kv
4038                    + "(memStoreTS:" + kv.getSequenceId() + ")");
4039                assertEquals(0, Bytes.compareTo(CellUtil.cloneValue(previousKV), thisValue));
4040              }
4041            }
4042            previousKV = kv;
4043          }
4044        }
4045      }
4046    } finally {
4047      if (putThread != null)
4048        putThread.done();
4049
4050      region.flush(true);
4051
4052      if (putThread != null) {
4053        putThread.join();
4054        putThread.checkNoError();
4055      }
4056
4057      ctx.stop();
4058      HBaseTestingUtility.closeRegionAndWAL(this.region);
4059      this.region = null;
4060    }
4061  }
4062
4063  @Test
4064  public void testHolesInMeta() throws Exception {
4065    byte[] family = Bytes.toBytes("family");
4066    this.region = initHRegion(tableName, Bytes.toBytes("x"), Bytes.toBytes("z"), method, CONF,
4067        false, family);
4068    try {
4069      byte[] rowNotServed = Bytes.toBytes("a");
4070      Get g = new Get(rowNotServed);
4071      try {
4072        region.get(g);
4073        fail();
4074      } catch (WrongRegionException x) {
4075        // OK
4076      }
4077      byte[] row = Bytes.toBytes("y");
4078      g = new Get(row);
4079      region.get(g);
4080    } finally {
4081      HBaseTestingUtility.closeRegionAndWAL(this.region);
4082      this.region = null;
4083    }
4084  }
4085
4086  @Test
4087  public void testIndexesScanWithOneDeletedRow() throws IOException {
4088    byte[] family = Bytes.toBytes("family");
4089
4090    // Setting up region
4091    this.region = initHRegion(tableName, method, CONF, family);
4092    try {
4093      Put put = new Put(Bytes.toBytes(1L));
4094      put.addColumn(family, qual1, 1L, Bytes.toBytes(1L));
4095      region.put(put);
4096
4097      region.flush(true);
4098
4099      Delete delete = new Delete(Bytes.toBytes(1L), 1L);
4100      region.delete(delete);
4101
4102      put = new Put(Bytes.toBytes(2L));
4103      put.addColumn(family, qual1, 2L, Bytes.toBytes(2L));
4104      region.put(put);
4105
4106      Scan idxScan = new Scan();
4107      idxScan.addFamily(family);
4108      idxScan.setFilter(new FilterList(FilterList.Operator.MUST_PASS_ALL, Arrays.<Filter> asList(
4109          new SingleColumnValueFilter(family, qual1, CompareOp.GREATER_OR_EQUAL,
4110              new BinaryComparator(Bytes.toBytes(0L))), new SingleColumnValueFilter(family, qual1,
4111              CompareOp.LESS_OR_EQUAL, new BinaryComparator(Bytes.toBytes(3L))))));
4112      InternalScanner scanner = region.getScanner(idxScan);
4113      List<Cell> res = new ArrayList<>();
4114
4115      while (scanner.next(res))
4116        ;
4117      assertEquals(1L, res.size());
4118    } finally {
4119      HBaseTestingUtility.closeRegionAndWAL(this.region);
4120      this.region = null;
4121    }
4122  }
4123
4124  // ////////////////////////////////////////////////////////////////////////////
4125  // Bloom filter test
4126  // ////////////////////////////////////////////////////////////////////////////
4127  @Test
4128  public void testBloomFilterSize() throws IOException {
4129    byte[] fam1 = Bytes.toBytes("fam1");
4130    byte[] qf1 = Bytes.toBytes("col");
4131    byte[] val1 = Bytes.toBytes("value1");
4132    // Create Table
4133    HColumnDescriptor hcd = new HColumnDescriptor(fam1).setMaxVersions(Integer.MAX_VALUE)
4134        .setBloomFilterType(BloomType.ROWCOL);
4135
4136    HTableDescriptor htd = new HTableDescriptor(tableName);
4137    htd.addFamily(hcd);
4138    HRegionInfo info = new HRegionInfo(htd.getTableName(), null, null, false);
4139    this.region = TEST_UTIL.createLocalHRegion(info, htd);
4140    try {
4141      int num_unique_rows = 10;
4142      int duplicate_multiplier = 2;
4143      int num_storefiles = 4;
4144
4145      int version = 0;
4146      for (int f = 0; f < num_storefiles; f++) {
4147        for (int i = 0; i < duplicate_multiplier; i++) {
4148          for (int j = 0; j < num_unique_rows; j++) {
4149            Put put = new Put(Bytes.toBytes("row" + j));
4150            put.setDurability(Durability.SKIP_WAL);
4151            long ts = version++;
4152            put.addColumn(fam1, qf1, ts, val1);
4153            region.put(put);
4154          }
4155        }
4156        region.flush(true);
4157      }
4158      // before compaction
4159      HStore store = region.getStore(fam1);
4160      Collection<HStoreFile> storeFiles = store.getStorefiles();
4161      for (HStoreFile storefile : storeFiles) {
4162        StoreFileReader reader = storefile.getReader();
4163        reader.loadFileInfo();
4164        reader.loadBloomfilter();
4165        assertEquals(num_unique_rows * duplicate_multiplier, reader.getEntries());
4166        assertEquals(num_unique_rows, reader.getFilterEntries());
4167      }
4168
4169      region.compact(true);
4170
4171      // after compaction
4172      storeFiles = store.getStorefiles();
4173      for (HStoreFile storefile : storeFiles) {
4174        StoreFileReader reader = storefile.getReader();
4175        reader.loadFileInfo();
4176        reader.loadBloomfilter();
4177        assertEquals(num_unique_rows * duplicate_multiplier * num_storefiles, reader.getEntries());
4178        assertEquals(num_unique_rows, reader.getFilterEntries());
4179      }
4180    } finally {
4181      HBaseTestingUtility.closeRegionAndWAL(this.region);
4182      this.region = null;
4183    }
4184  }
4185
4186  @Test
4187  public void testAllColumnsWithBloomFilter() throws IOException {
4188    byte[] TABLE = Bytes.toBytes(name.getMethodName());
4189    byte[] FAMILY = Bytes.toBytes("family");
4190
4191    // Create table
4192    HColumnDescriptor hcd = new HColumnDescriptor(FAMILY).setMaxVersions(Integer.MAX_VALUE)
4193        .setBloomFilterType(BloomType.ROWCOL);
4194    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TABLE));
4195    htd.addFamily(hcd);
4196    HRegionInfo info = new HRegionInfo(htd.getTableName(), null, null, false);
4197    this.region = TEST_UTIL.createLocalHRegion(info, htd);
4198    try {
4199      // For row:0, col:0: insert versions 1 through 5.
4200      byte row[] = Bytes.toBytes("row:" + 0);
4201      byte column[] = Bytes.toBytes("column:" + 0);
4202      Put put = new Put(row);
4203      put.setDurability(Durability.SKIP_WAL);
4204      for (long idx = 1; idx <= 4; idx++) {
4205        put.addColumn(FAMILY, column, idx, Bytes.toBytes("value-version-" + idx));
4206      }
4207      region.put(put);
4208
4209      // Flush
4210      region.flush(true);
4211
4212      // Get rows
4213      Get get = new Get(row);
4214      get.setMaxVersions();
4215      Cell[] kvs = region.get(get).rawCells();
4216
4217      // Check if rows are correct
4218      assertEquals(4, kvs.length);
4219      checkOneCell(kvs[0], FAMILY, 0, 0, 4);
4220      checkOneCell(kvs[1], FAMILY, 0, 0, 3);
4221      checkOneCell(kvs[2], FAMILY, 0, 0, 2);
4222      checkOneCell(kvs[3], FAMILY, 0, 0, 1);
4223    } finally {
4224      HBaseTestingUtility.closeRegionAndWAL(this.region);
4225      this.region = null;
4226    }
4227  }
4228
4229  /**
4230   * Testcase to cover bug-fix for HBASE-2823 Ensures correct delete when
4231   * issuing delete row on columns with bloom filter set to row+col
4232   * (BloomType.ROWCOL)
4233   */
4234  @Test
4235  public void testDeleteRowWithBloomFilter() throws IOException {
4236    byte[] familyName = Bytes.toBytes("familyName");
4237
4238    // Create Table
4239    HColumnDescriptor hcd = new HColumnDescriptor(familyName).setMaxVersions(Integer.MAX_VALUE)
4240        .setBloomFilterType(BloomType.ROWCOL);
4241
4242    HTableDescriptor htd = new HTableDescriptor(tableName);
4243    htd.addFamily(hcd);
4244    HRegionInfo info = new HRegionInfo(htd.getTableName(), null, null, false);
4245    this.region = TEST_UTIL.createLocalHRegion(info, htd);
4246    try {
4247      // Insert some data
4248      byte row[] = Bytes.toBytes("row1");
4249      byte col[] = Bytes.toBytes("col1");
4250
4251      Put put = new Put(row);
4252      put.addColumn(familyName, col, 1, Bytes.toBytes("SomeRandomValue"));
4253      region.put(put);
4254      region.flush(true);
4255
4256      Delete del = new Delete(row);
4257      region.delete(del);
4258      region.flush(true);
4259
4260      // Get remaining rows (should have none)
4261      Get get = new Get(row);
4262      get.addColumn(familyName, col);
4263
4264      Cell[] keyValues = region.get(get).rawCells();
4265      assertTrue(keyValues.length == 0);
4266    } finally {
4267      HBaseTestingUtility.closeRegionAndWAL(this.region);
4268      this.region = null;
4269    }
4270  }
4271
4272  @Test
4273  public void testgetHDFSBlocksDistribution() throws Exception {
4274    HBaseTestingUtility htu = new HBaseTestingUtility();
4275    // Why do we set the block size in this test?  If we set it smaller than the kvs, then we'll
4276    // break up the file in to more pieces that can be distributed across the three nodes and we
4277    // won't be able to have the condition this test asserts; that at least one node has
4278    // a copy of all replicas -- if small block size, then blocks are spread evenly across the
4279    // the three nodes.  hfilev3 with tags seems to put us over the block size.  St.Ack.
4280    // final int DEFAULT_BLOCK_SIZE = 1024;
4281    // htu.getConfiguration().setLong("dfs.blocksize", DEFAULT_BLOCK_SIZE);
4282    htu.getConfiguration().setInt("dfs.replication", 2);
4283
4284    // set up a cluster with 3 nodes
4285    MiniHBaseCluster cluster = null;
4286    String dataNodeHosts[] = new String[] { "host1", "host2", "host3" };
4287    int regionServersCount = 3;
4288
4289    try {
4290      cluster = htu.startMiniCluster(1, regionServersCount, dataNodeHosts);
4291      byte[][] families = { fam1, fam2 };
4292      Table ht = htu.createTable(tableName, families);
4293
4294      // Setting up region
4295      byte row[] = Bytes.toBytes("row1");
4296      byte col[] = Bytes.toBytes("col1");
4297
4298      Put put = new Put(row);
4299      put.addColumn(fam1, col, 1, Bytes.toBytes("test1"));
4300      put.addColumn(fam2, col, 1, Bytes.toBytes("test2"));
4301      ht.put(put);
4302
4303      HRegion firstRegion = htu.getHBaseCluster().getRegions(tableName).get(0);
4304      firstRegion.flush(true);
4305      HDFSBlocksDistribution blocksDistribution1 = firstRegion.getHDFSBlocksDistribution();
4306
4307      // Given the default replication factor is 2 and we have 2 HFiles,
4308      // we will have total of 4 replica of blocks on 3 datanodes; thus there
4309      // must be at least one host that have replica for 2 HFiles. That host's
4310      // weight will be equal to the unique block weight.
4311      long uniqueBlocksWeight1 = blocksDistribution1.getUniqueBlocksTotalWeight();
4312      StringBuilder sb = new StringBuilder();
4313      for (String host: blocksDistribution1.getTopHosts()) {
4314        if (sb.length() > 0) sb.append(", ");
4315        sb.append(host);
4316        sb.append("=");
4317        sb.append(blocksDistribution1.getWeight(host));
4318      }
4319
4320      String topHost = blocksDistribution1.getTopHosts().get(0);
4321      long topHostWeight = blocksDistribution1.getWeight(topHost);
4322      String msg = "uniqueBlocksWeight=" + uniqueBlocksWeight1 + ", topHostWeight=" +
4323        topHostWeight + ", topHost=" + topHost + "; " + sb.toString();
4324      LOG.info(msg);
4325      assertTrue(msg, uniqueBlocksWeight1 == topHostWeight);
4326
4327      // use the static method to compute the value, it should be the same.
4328      // static method is used by load balancer or other components
4329      HDFSBlocksDistribution blocksDistribution2 = HRegion.computeHDFSBlocksDistribution(
4330          htu.getConfiguration(), firstRegion.getTableDescriptor(), firstRegion.getRegionInfo());
4331      long uniqueBlocksWeight2 = blocksDistribution2.getUniqueBlocksTotalWeight();
4332
4333      assertTrue(uniqueBlocksWeight1 == uniqueBlocksWeight2);
4334
4335      ht.close();
4336    } finally {
4337      if (cluster != null) {
4338        htu.shutdownMiniCluster();
4339      }
4340    }
4341  }
4342
4343  /**
4344   * Testcase to check state of region initialization task set to ABORTED or not
4345   * if any exceptions during initialization
4346   *
4347   * @throws Exception
4348   */
4349  @Test
4350  public void testStatusSettingToAbortIfAnyExceptionDuringRegionInitilization() throws Exception {
4351    HRegionInfo info;
4352    try {
4353      FileSystem fs = Mockito.mock(FileSystem.class);
4354      Mockito.when(fs.exists((Path) Mockito.anyObject())).thenThrow(new IOException());
4355      HTableDescriptor htd = new HTableDescriptor(tableName);
4356      htd.addFamily(new HColumnDescriptor("cf"));
4357      info = new HRegionInfo(htd.getTableName(), HConstants.EMPTY_BYTE_ARRAY,
4358          HConstants.EMPTY_BYTE_ARRAY, false);
4359      Path path = new Path(dir + "testStatusSettingToAbortIfAnyExceptionDuringRegionInitilization");
4360      region = HRegion.newHRegion(path, null, fs, CONF, info, htd, null);
4361      // region initialization throws IOException and set task state to ABORTED.
4362      region.initialize();
4363      fail("Region initialization should fail due to IOException");
4364    } catch (IOException io) {
4365      List<MonitoredTask> tasks = TaskMonitor.get().getTasks();
4366      for (MonitoredTask monitoredTask : tasks) {
4367        if (!(monitoredTask instanceof MonitoredRPCHandler)
4368            && monitoredTask.getDescription().contains(region.toString())) {
4369          assertTrue("Region state should be ABORTED.",
4370              monitoredTask.getState().equals(MonitoredTask.State.ABORTED));
4371          break;
4372        }
4373      }
4374    } finally {
4375      HBaseTestingUtility.closeRegionAndWAL(region);
4376    }
4377  }
4378
4379  /**
4380   * Verifies that the .regioninfo file is written on region creation and that
4381   * is recreated if missing during region opening.
4382   */
4383  @Test
4384  public void testRegionInfoFileCreation() throws IOException {
4385    Path rootDir = new Path(dir + "testRegionInfoFileCreation");
4386
4387    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
4388    htd.addFamily(new HColumnDescriptor("cf"));
4389
4390    HRegionInfo hri = new HRegionInfo(htd.getTableName());
4391
4392    // Create a region and skip the initialization (like CreateTableHandler)
4393    HRegion region = HBaseTestingUtility.createRegionAndWAL(hri, rootDir, CONF, htd, false);
4394    Path regionDir = region.getRegionFileSystem().getRegionDir();
4395    FileSystem fs = region.getRegionFileSystem().getFileSystem();
4396    HBaseTestingUtility.closeRegionAndWAL(region);
4397
4398    Path regionInfoFile = new Path(regionDir, HRegionFileSystem.REGION_INFO_FILE);
4399
4400    // Verify that the .regioninfo file is present
4401    assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir",
4402        fs.exists(regionInfoFile));
4403
4404    // Try to open the region
4405    region = HRegion.openHRegion(rootDir, hri, htd, null, CONF);
4406    assertEquals(regionDir, region.getRegionFileSystem().getRegionDir());
4407    HBaseTestingUtility.closeRegionAndWAL(region);
4408
4409    // Verify that the .regioninfo file is still there
4410    assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir",
4411        fs.exists(regionInfoFile));
4412
4413    // Remove the .regioninfo file and verify is recreated on region open
4414    fs.delete(regionInfoFile, true);
4415    assertFalse(HRegionFileSystem.REGION_INFO_FILE + " should be removed from the region dir",
4416        fs.exists(regionInfoFile));
4417
4418    region = HRegion.openHRegion(rootDir, hri, htd, null, CONF);
4419//    region = TEST_UTIL.openHRegion(hri, htd);
4420    assertEquals(regionDir, region.getRegionFileSystem().getRegionDir());
4421    HBaseTestingUtility.closeRegionAndWAL(region);
4422
4423    // Verify that the .regioninfo file is still there
4424    assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir",
4425        fs.exists(new Path(regionDir, HRegionFileSystem.REGION_INFO_FILE)));
4426  }
4427
4428  /**
4429   * TestCase for increment
4430   */
4431  private static class Incrementer implements Runnable {
4432    private HRegion region;
4433    private final static byte[] incRow = Bytes.toBytes("incRow");
4434    private final static byte[] family = Bytes.toBytes("family");
4435    private final static byte[] qualifier = Bytes.toBytes("qualifier");
4436    private final static long ONE = 1L;
4437    private int incCounter;
4438
4439    public Incrementer(HRegion region, int incCounter) {
4440      this.region = region;
4441      this.incCounter = incCounter;
4442    }
4443
4444    @Override
4445    public void run() {
4446      int count = 0;
4447      while (count < incCounter) {
4448        Increment inc = new Increment(incRow);
4449        inc.addColumn(family, qualifier, ONE);
4450        count++;
4451        try {
4452          region.increment(inc);
4453        } catch (IOException e) {
4454          LOG.info("Count=" + count + ", " + e);
4455          break;
4456        }
4457      }
4458    }
4459  }
4460
4461  /**
4462   * Test case to check increment function with memstore flushing
4463   * @throws Exception
4464   */
4465  @Test
4466  public void testParallelIncrementWithMemStoreFlush() throws Exception {
4467    byte[] family = Incrementer.family;
4468    this.region = initHRegion(tableName, method, CONF, family);
4469    final HRegion region = this.region;
4470    final AtomicBoolean incrementDone = new AtomicBoolean(false);
4471    Runnable flusher = new Runnable() {
4472      @Override
4473      public void run() {
4474        while (!incrementDone.get()) {
4475          try {
4476            region.flush(true);
4477          } catch (Exception e) {
4478            e.printStackTrace();
4479          }
4480        }
4481      }
4482    };
4483
4484    // after all increment finished, the row will increment to 20*100 = 2000
4485    int threadNum = 20;
4486    int incCounter = 100;
4487    long expected = (long) threadNum * incCounter;
4488    Thread[] incrementers = new Thread[threadNum];
4489    Thread flushThread = new Thread(flusher);
4490    for (int i = 0; i < threadNum; i++) {
4491      incrementers[i] = new Thread(new Incrementer(this.region, incCounter));
4492      incrementers[i].start();
4493    }
4494    flushThread.start();
4495    for (int i = 0; i < threadNum; i++) {
4496      incrementers[i].join();
4497    }
4498
4499    incrementDone.set(true);
4500    flushThread.join();
4501
4502    Get get = new Get(Incrementer.incRow);
4503    get.addColumn(Incrementer.family, Incrementer.qualifier);
4504    get.setMaxVersions(1);
4505    Result res = this.region.get(get);
4506    List<Cell> kvs = res.getColumnCells(Incrementer.family, Incrementer.qualifier);
4507
4508    // we just got the latest version
4509    assertEquals(1, kvs.size());
4510    Cell kv = kvs.get(0);
4511    assertEquals(expected, Bytes.toLong(kv.getValueArray(), kv.getValueOffset()));
4512    this.region = null;
4513  }
4514
4515  /**
4516   * TestCase for append
4517   */
4518  private static class Appender implements Runnable {
4519    private HRegion region;
4520    private final static byte[] appendRow = Bytes.toBytes("appendRow");
4521    private final static byte[] family = Bytes.toBytes("family");
4522    private final static byte[] qualifier = Bytes.toBytes("qualifier");
4523    private final static byte[] CHAR = Bytes.toBytes("a");
4524    private int appendCounter;
4525
4526    public Appender(HRegion region, int appendCounter) {
4527      this.region = region;
4528      this.appendCounter = appendCounter;
4529    }
4530
4531    @Override
4532    public void run() {
4533      int count = 0;
4534      while (count < appendCounter) {
4535        Append app = new Append(appendRow);
4536        app.addColumn(family, qualifier, CHAR);
4537        count++;
4538        try {
4539          region.append(app);
4540        } catch (IOException e) {
4541          LOG.info("Count=" + count + ", max=" + appendCounter + ", " + e);
4542          break;
4543        }
4544      }
4545    }
4546  }
4547
4548  /**
4549   * Test case to check append function with memstore flushing
4550   * @throws Exception
4551   */
4552  @Test
4553  public void testParallelAppendWithMemStoreFlush() throws Exception {
4554    byte[] family = Appender.family;
4555    this.region = initHRegion(tableName, method, CONF, family);
4556    final HRegion region = this.region;
4557    final AtomicBoolean appendDone = new AtomicBoolean(false);
4558    Runnable flusher = new Runnable() {
4559      @Override
4560      public void run() {
4561        while (!appendDone.get()) {
4562          try {
4563            region.flush(true);
4564          } catch (Exception e) {
4565            e.printStackTrace();
4566          }
4567        }
4568      }
4569    };
4570
4571    // After all append finished, the value will append to threadNum *
4572    // appendCounter Appender.CHAR
4573    int threadNum = 20;
4574    int appendCounter = 100;
4575    byte[] expected = new byte[threadNum * appendCounter];
4576    for (int i = 0; i < threadNum * appendCounter; i++) {
4577      System.arraycopy(Appender.CHAR, 0, expected, i, 1);
4578    }
4579    Thread[] appenders = new Thread[threadNum];
4580    Thread flushThread = new Thread(flusher);
4581    for (int i = 0; i < threadNum; i++) {
4582      appenders[i] = new Thread(new Appender(this.region, appendCounter));
4583      appenders[i].start();
4584    }
4585    flushThread.start();
4586    for (int i = 0; i < threadNum; i++) {
4587      appenders[i].join();
4588    }
4589
4590    appendDone.set(true);
4591    flushThread.join();
4592
4593    Get get = new Get(Appender.appendRow);
4594    get.addColumn(Appender.family, Appender.qualifier);
4595    get.setMaxVersions(1);
4596    Result res = this.region.get(get);
4597    List<Cell> kvs = res.getColumnCells(Appender.family, Appender.qualifier);
4598
4599    // we just got the latest version
4600    assertEquals(1, kvs.size());
4601    Cell kv = kvs.get(0);
4602    byte[] appendResult = new byte[kv.getValueLength()];
4603    System.arraycopy(kv.getValueArray(), kv.getValueOffset(), appendResult, 0, kv.getValueLength());
4604    assertArrayEquals(expected, appendResult);
4605    this.region = null;
4606  }
4607
4608  /**
4609   * Test case to check put function with memstore flushing for same row, same ts
4610   * @throws Exception
4611   */
4612  @Test
4613  public void testPutWithMemStoreFlush() throws Exception {
4614    byte[] family = Bytes.toBytes("family");
4615    byte[] qualifier = Bytes.toBytes("qualifier");
4616    byte[] row = Bytes.toBytes("putRow");
4617    byte[] value = null;
4618    this.region = initHRegion(tableName, method, CONF, family);
4619    Put put = null;
4620    Get get = null;
4621    List<Cell> kvs = null;
4622    Result res = null;
4623
4624    put = new Put(row);
4625    value = Bytes.toBytes("value0");
4626    put.addColumn(family, qualifier, 1234567L, value);
4627    region.put(put);
4628    get = new Get(row);
4629    get.addColumn(family, qualifier);
4630    get.setMaxVersions();
4631    res = this.region.get(get);
4632    kvs = res.getColumnCells(family, qualifier);
4633    assertEquals(1, kvs.size());
4634    assertArrayEquals(Bytes.toBytes("value0"), CellUtil.cloneValue(kvs.get(0)));
4635
4636    region.flush(true);
4637    get = new Get(row);
4638    get.addColumn(family, qualifier);
4639    get.setMaxVersions();
4640    res = this.region.get(get);
4641    kvs = res.getColumnCells(family, qualifier);
4642    assertEquals(1, kvs.size());
4643    assertArrayEquals(Bytes.toBytes("value0"), CellUtil.cloneValue(kvs.get(0)));
4644
4645    put = new Put(row);
4646    value = Bytes.toBytes("value1");
4647    put.addColumn(family, qualifier, 1234567L, value);
4648    region.put(put);
4649    get = new Get(row);
4650    get.addColumn(family, qualifier);
4651    get.setMaxVersions();
4652    res = this.region.get(get);
4653    kvs = res.getColumnCells(family, qualifier);
4654    assertEquals(1, kvs.size());
4655    assertArrayEquals(Bytes.toBytes("value1"), CellUtil.cloneValue(kvs.get(0)));
4656
4657    region.flush(true);
4658    get = new Get(row);
4659    get.addColumn(family, qualifier);
4660    get.setMaxVersions();
4661    res = this.region.get(get);
4662    kvs = res.getColumnCells(family, qualifier);
4663    assertEquals(1, kvs.size());
4664    assertArrayEquals(Bytes.toBytes("value1"), CellUtil.cloneValue(kvs.get(0)));
4665  }
4666
4667  @Test
4668  public void testDurability() throws Exception {
4669    // there are 5 x 5 cases:
4670    // table durability(SYNC,FSYNC,ASYC,SKIP,USE_DEFAULT) x mutation
4671    // durability(SYNC,FSYNC,ASYC,SKIP,USE_DEFAULT)
4672
4673    // expected cases for append and sync wal
4674    durabilityTest(method, Durability.SYNC_WAL, Durability.SYNC_WAL, 0, true, true, false);
4675    durabilityTest(method, Durability.SYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false);
4676    durabilityTest(method, Durability.SYNC_WAL, Durability.USE_DEFAULT, 0, true, true, false);
4677
4678    durabilityTest(method, Durability.FSYNC_WAL, Durability.SYNC_WAL, 0, true, true, false);
4679    durabilityTest(method, Durability.FSYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false);
4680    durabilityTest(method, Durability.FSYNC_WAL, Durability.USE_DEFAULT, 0, true, true, false);
4681
4682    durabilityTest(method, Durability.ASYNC_WAL, Durability.SYNC_WAL, 0, true, true, false);
4683    durabilityTest(method, Durability.ASYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false);
4684
4685    durabilityTest(method, Durability.SKIP_WAL, Durability.SYNC_WAL, 0, true, true, false);
4686    durabilityTest(method, Durability.SKIP_WAL, Durability.FSYNC_WAL, 0, true, true, false);
4687
4688    durabilityTest(method, Durability.USE_DEFAULT, Durability.SYNC_WAL, 0, true, true, false);
4689    durabilityTest(method, Durability.USE_DEFAULT, Durability.FSYNC_WAL, 0, true, true, false);
4690    durabilityTest(method, Durability.USE_DEFAULT, Durability.USE_DEFAULT, 0, true, true, false);
4691
4692    // expected cases for async wal
4693    durabilityTest(method, Durability.SYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false);
4694    durabilityTest(method, Durability.FSYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false);
4695    durabilityTest(method, Durability.ASYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false);
4696    durabilityTest(method, Durability.SKIP_WAL, Durability.ASYNC_WAL, 0, true, false, false);
4697    durabilityTest(method, Durability.USE_DEFAULT, Durability.ASYNC_WAL, 0, true, false, false);
4698    durabilityTest(method, Durability.ASYNC_WAL, Durability.USE_DEFAULT, 0, true, false, false);
4699
4700    durabilityTest(method, Durability.SYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true);
4701    durabilityTest(method, Durability.FSYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true);
4702    durabilityTest(method, Durability.ASYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true);
4703    durabilityTest(method, Durability.SKIP_WAL, Durability.ASYNC_WAL, 5000, true, false, true);
4704    durabilityTest(method, Durability.USE_DEFAULT, Durability.ASYNC_WAL, 5000, true, false, true);
4705    durabilityTest(method, Durability.ASYNC_WAL, Durability.USE_DEFAULT, 5000, true, false, true);
4706
4707    // expect skip wal cases
4708    durabilityTest(method, Durability.SYNC_WAL, Durability.SKIP_WAL, 0, false, false, false);
4709    durabilityTest(method, Durability.FSYNC_WAL, Durability.SKIP_WAL, 0, false, false, false);
4710    durabilityTest(method, Durability.ASYNC_WAL, Durability.SKIP_WAL, 0, false, false, false);
4711    durabilityTest(method, Durability.SKIP_WAL, Durability.SKIP_WAL, 0, false, false, false);
4712    durabilityTest(method, Durability.USE_DEFAULT, Durability.SKIP_WAL, 0, false, false, false);
4713    durabilityTest(method, Durability.SKIP_WAL, Durability.USE_DEFAULT, 0, false, false, false);
4714
4715  }
4716
4717  private void durabilityTest(String method, Durability tableDurability,
4718      Durability mutationDurability, long timeout, boolean expectAppend, final boolean expectSync,
4719      final boolean expectSyncFromLogSyncer) throws Exception {
4720    Configuration conf = HBaseConfiguration.create(CONF);
4721    method = method + "_" + tableDurability.name() + "_" + mutationDurability.name();
4722    byte[] family = Bytes.toBytes("family");
4723    Path logDir = new Path(new Path(dir + method), "log");
4724    final Configuration walConf = new Configuration(conf);
4725    FSUtils.setRootDir(walConf, logDir);
4726    // XXX: The spied AsyncFSWAL can not work properly because of a Mockito defect that can not
4727    // deal with classes which have a field of an inner class. See discussions in HBASE-15536.
4728    walConf.set(WALFactory.WAL_PROVIDER, "filesystem");
4729    final WALFactory wals = new WALFactory(walConf, UUID.randomUUID().toString());
4730    final WAL wal = spy(wals.getWAL(RegionInfoBuilder.newBuilder(tableName).build()));
4731    this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW,
4732        HConstants.EMPTY_END_ROW, false, tableDurability, wal,
4733        new byte[][] { family });
4734
4735    Put put = new Put(Bytes.toBytes("r1"));
4736    put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1"));
4737    put.setDurability(mutationDurability);
4738    region.put(put);
4739
4740    //verify append called or not
4741    verify(wal, expectAppend ? times(1) : never())
4742      .append((HRegionInfo)any(), (WALKeyImpl)any(),
4743          (WALEdit)any(), Mockito.anyBoolean());
4744
4745    // verify sync called or not
4746    if (expectSync || expectSyncFromLogSyncer) {
4747      TEST_UTIL.waitFor(timeout, new Waiter.Predicate<Exception>() {
4748        @Override
4749        public boolean evaluate() throws Exception {
4750          try {
4751            if (expectSync) {
4752              verify(wal, times(1)).sync(anyLong()); // Hregion calls this one
4753            } else if (expectSyncFromLogSyncer) {
4754              verify(wal, times(1)).sync(); // wal syncer calls this one
4755            }
4756          } catch (Throwable ignore) {
4757          }
4758          return true;
4759        }
4760      });
4761    } else {
4762      //verify(wal, never()).sync(anyLong());
4763      verify(wal, never()).sync();
4764    }
4765
4766    HBaseTestingUtility.closeRegionAndWAL(this.region);
4767    wals.close();
4768    this.region = null;
4769  }
4770
4771  @Test
4772  public void testRegionReplicaSecondary() throws IOException {
4773    // create a primary region, load some data and flush
4774    // create a secondary region, and do a get against that
4775    Path rootDir = new Path(dir + name.getMethodName());
4776    FSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir);
4777
4778    byte[][] families = new byte[][] {
4779        Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3")
4780    };
4781    byte[] cq = Bytes.toBytes("cq");
4782    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
4783    for (byte[] family : families) {
4784      htd.addFamily(new HColumnDescriptor(family));
4785    }
4786
4787    long time = System.currentTimeMillis();
4788    HRegionInfo primaryHri = new HRegionInfo(htd.getTableName(),
4789      HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW,
4790      false, time, 0);
4791    HRegionInfo secondaryHri = new HRegionInfo(htd.getTableName(),
4792      HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW,
4793      false, time, 1);
4794
4795    HRegion primaryRegion = null, secondaryRegion = null;
4796
4797    try {
4798      primaryRegion = HBaseTestingUtility.createRegionAndWAL(primaryHri,
4799          rootDir, TEST_UTIL.getConfiguration(), htd);
4800
4801      // load some data
4802      putData(primaryRegion, 0, 1000, cq, families);
4803
4804      // flush region
4805      primaryRegion.flush(true);
4806
4807      // open secondary region
4808      secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, htd, null, CONF);
4809
4810      verifyData(secondaryRegion, 0, 1000, cq, families);
4811    } finally {
4812      if (primaryRegion != null) {
4813        HBaseTestingUtility.closeRegionAndWAL(primaryRegion);
4814      }
4815      if (secondaryRegion != null) {
4816        HBaseTestingUtility.closeRegionAndWAL(secondaryRegion);
4817      }
4818    }
4819  }
4820
4821  @Test
4822  public void testRegionReplicaSecondaryIsReadOnly() throws IOException {
4823    // create a primary region, load some data and flush
4824    // create a secondary region, and do a put against that
4825    Path rootDir = new Path(dir + name.getMethodName());
4826    FSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir);
4827
4828    byte[][] families = new byte[][] {
4829        Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3")
4830    };
4831    byte[] cq = Bytes.toBytes("cq");
4832    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
4833    for (byte[] family : families) {
4834      htd.addFamily(new HColumnDescriptor(family));
4835    }
4836
4837    long time = System.currentTimeMillis();
4838    HRegionInfo primaryHri = new HRegionInfo(htd.getTableName(),
4839      HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW,
4840      false, time, 0);
4841    HRegionInfo secondaryHri = new HRegionInfo(htd.getTableName(),
4842      HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW,
4843      false, time, 1);
4844
4845    HRegion primaryRegion = null, secondaryRegion = null;
4846
4847    try {
4848      primaryRegion = HBaseTestingUtility.createRegionAndWAL(primaryHri,
4849          rootDir, TEST_UTIL.getConfiguration(), htd);
4850
4851      // load some data
4852      putData(primaryRegion, 0, 1000, cq, families);
4853
4854      // flush region
4855      primaryRegion.flush(true);
4856
4857      // open secondary region
4858      secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, htd, null, CONF);
4859
4860      try {
4861        putData(secondaryRegion, 0, 1000, cq, families);
4862        fail("Should have thrown exception");
4863      } catch (IOException ex) {
4864        // expected
4865      }
4866    } finally {
4867      if (primaryRegion != null) {
4868        HBaseTestingUtility.closeRegionAndWAL(primaryRegion);
4869      }
4870      if (secondaryRegion != null) {
4871        HBaseTestingUtility.closeRegionAndWAL(secondaryRegion);
4872      }
4873    }
4874  }
4875
4876  static WALFactory createWALFactory(Configuration conf, Path rootDir) throws IOException {
4877    Configuration confForWAL = new Configuration(conf);
4878    confForWAL.set(HConstants.HBASE_DIR, rootDir.toString());
4879    return new WALFactory(confForWAL, "hregion-" + RandomStringUtils.randomNumeric(8));
4880  }
4881
4882  @Test
4883  public void testCompactionFromPrimary() throws IOException {
4884    Path rootDir = new Path(dir + name.getMethodName());
4885    FSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir);
4886
4887    byte[][] families = new byte[][] {
4888        Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3")
4889    };
4890    byte[] cq = Bytes.toBytes("cq");
4891    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
4892    for (byte[] family : families) {
4893      htd.addFamily(new HColumnDescriptor(family));
4894    }
4895
4896    long time = System.currentTimeMillis();
4897    HRegionInfo primaryHri = new HRegionInfo(htd.getTableName(),
4898      HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW,
4899      false, time, 0);
4900    HRegionInfo secondaryHri = new HRegionInfo(htd.getTableName(),
4901      HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW,
4902      false, time, 1);
4903
4904    HRegion primaryRegion = null, secondaryRegion = null;
4905
4906    try {
4907      primaryRegion = HBaseTestingUtility.createRegionAndWAL(primaryHri,
4908          rootDir, TEST_UTIL.getConfiguration(), htd);
4909
4910      // load some data
4911      putData(primaryRegion, 0, 1000, cq, families);
4912
4913      // flush region
4914      primaryRegion.flush(true);
4915
4916      // open secondary region
4917      secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, htd, null, CONF);
4918
4919      // move the file of the primary region to the archive, simulating a compaction
4920      Collection<HStoreFile> storeFiles = primaryRegion.getStore(families[0]).getStorefiles();
4921      primaryRegion.getRegionFileSystem().removeStoreFiles(Bytes.toString(families[0]), storeFiles);
4922      Collection<StoreFileInfo> storeFileInfos = primaryRegion.getRegionFileSystem()
4923          .getStoreFiles(families[0]);
4924      Assert.assertTrue(storeFileInfos == null || storeFileInfos.isEmpty());
4925
4926      verifyData(secondaryRegion, 0, 1000, cq, families);
4927    } finally {
4928      if (primaryRegion != null) {
4929        HBaseTestingUtility.closeRegionAndWAL(primaryRegion);
4930      }
4931      if (secondaryRegion != null) {
4932        HBaseTestingUtility.closeRegionAndWAL(secondaryRegion);
4933      }
4934    }
4935  }
4936
4937  private void putData(int startRow, int numRows, byte[] qf, byte[]... families) throws
4938      IOException {
4939    putData(this.region, startRow, numRows, qf, families);
4940  }
4941
4942  private void putData(HRegion region,
4943      int startRow, int numRows, byte[] qf, byte[]... families) throws IOException {
4944    putData(region, Durability.SKIP_WAL, startRow, numRows, qf, families);
4945  }
4946
4947  static void putData(HRegion region, Durability durability,
4948      int startRow, int numRows, byte[] qf, byte[]... families) throws IOException {
4949    for (int i = startRow; i < startRow + numRows; i++) {
4950      Put put = new Put(Bytes.toBytes("" + i));
4951      put.setDurability(durability);
4952      for (byte[] family : families) {
4953        put.addColumn(family, qf, null);
4954      }
4955      region.put(put);
4956      LOG.info(put.toString());
4957    }
4958  }
4959
4960  static void verifyData(HRegion newReg, int startRow, int numRows, byte[] qf, byte[]... families)
4961      throws IOException {
4962    for (int i = startRow; i < startRow + numRows; i++) {
4963      byte[] row = Bytes.toBytes("" + i);
4964      Get get = new Get(row);
4965      for (byte[] family : families) {
4966        get.addColumn(family, qf);
4967      }
4968      Result result = newReg.get(get);
4969      Cell[] raw = result.rawCells();
4970      assertEquals(families.length, result.size());
4971      for (int j = 0; j < families.length; j++) {
4972        assertTrue(CellUtil.matchingRows(raw[j], row));
4973        assertTrue(CellUtil.matchingFamily(raw[j], families[j]));
4974        assertTrue(CellUtil.matchingQualifier(raw[j], qf));
4975      }
4976    }
4977  }
4978
4979  static void assertGet(final HRegion r, final byte[] family, final byte[] k) throws IOException {
4980    // Now I have k, get values out and assert they are as expected.
4981    Get get = new Get(k).addFamily(family).setMaxVersions();
4982    Cell[] results = r.get(get).rawCells();
4983    for (int j = 0; j < results.length; j++) {
4984      byte[] tmp = CellUtil.cloneValue(results[j]);
4985      // Row should be equal to value every time.
4986      assertTrue(Bytes.equals(k, tmp));
4987    }
4988  }
4989
4990  /*
4991   * Assert first value in the passed region is <code>firstValue</code>.
4992   *
4993   * @param r
4994   *
4995   * @param fs
4996   *
4997   * @param firstValue
4998   *
4999   * @throws IOException
5000   */
5001  protected void assertScan(final HRegion r, final byte[] fs, final byte[] firstValue)
5002      throws IOException {
5003    byte[][] families = { fs };
5004    Scan scan = new Scan();
5005    for (int i = 0; i < families.length; i++)
5006      scan.addFamily(families[i]);
5007    InternalScanner s = r.getScanner(scan);
5008    try {
5009      List<Cell> curVals = new ArrayList<>();
5010      boolean first = true;
5011      OUTER_LOOP: while (s.next(curVals)) {
5012        for (Cell kv : curVals) {
5013          byte[] val = CellUtil.cloneValue(kv);
5014          byte[] curval = val;
5015          if (first) {
5016            first = false;
5017            assertTrue(Bytes.compareTo(curval, firstValue) == 0);
5018          } else {
5019            // Not asserting anything. Might as well break.
5020            break OUTER_LOOP;
5021          }
5022        }
5023      }
5024    } finally {
5025      s.close();
5026    }
5027  }
5028
5029  /**
5030   * Test that we get the expected flush results back
5031   */
5032  @Test
5033  public void testFlushResult() throws IOException {
5034    byte[] family = Bytes.toBytes("family");
5035
5036    this.region = initHRegion(tableName, method, family);
5037
5038    // empty memstore, flush doesn't run
5039    HRegion.FlushResult fr = region.flush(true);
5040    assertFalse(fr.isFlushSucceeded());
5041    assertFalse(fr.isCompactionNeeded());
5042
5043    // Flush enough files to get up to the threshold, doesn't need compactions
5044    for (int i = 0; i < 2; i++) {
5045      Put put = new Put(tableName.toBytes()).addColumn(family, family, tableName.toBytes());
5046      region.put(put);
5047      fr = region.flush(true);
5048      assertTrue(fr.isFlushSucceeded());
5049      assertFalse(fr.isCompactionNeeded());
5050    }
5051
5052    // Two flushes after the threshold, compactions are needed
5053    for (int i = 0; i < 2; i++) {
5054      Put put = new Put(tableName.toBytes()).addColumn(family, family, tableName.toBytes());
5055      region.put(put);
5056      fr = region.flush(true);
5057      assertTrue(fr.isFlushSucceeded());
5058      assertTrue(fr.isCompactionNeeded());
5059    }
5060  }
5061
5062  protected Configuration initSplit() {
5063    // Always compact if there is more than one store file.
5064    CONF.setInt("hbase.hstore.compactionThreshold", 2);
5065
5066    CONF.setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, 10 * 1000);
5067
5068    // Increase the amount of time between client retries
5069    CONF.setLong("hbase.client.pause", 15 * 1000);
5070
5071    // This size should make it so we always split using the addContent
5072    // below. After adding all data, the first region is 1.3M
5073    CONF.setLong(HConstants.HREGION_MAX_FILESIZE, 1024 * 128);
5074    return CONF;
5075  }
5076
5077  /**
5078   * @return A region on which you must call
5079   *         {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} when done.
5080   */
5081  protected HRegion initHRegion(TableName tableName, String callingMethod, Configuration conf,
5082      byte[]... families) throws IOException {
5083    return initHRegion(tableName, callingMethod, conf, false, families);
5084  }
5085
5086  /**
5087   * @return A region on which you must call
5088   *         {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} when done.
5089   */
5090  protected HRegion initHRegion(TableName tableName, String callingMethod, Configuration conf,
5091      boolean isReadOnly, byte[]... families) throws IOException {
5092    return initHRegion(tableName, null, null, callingMethod, conf, isReadOnly, families);
5093  }
5094
5095  protected HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey,
5096      String callingMethod, Configuration conf, boolean isReadOnly, byte[]... families)
5097      throws IOException {
5098    Path logDir = TEST_UTIL.getDataTestDirOnTestFS(callingMethod + ".log");
5099    ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null);
5100    HRegionInfo hri = new HRegionInfo(tableName, startKey, stopKey);
5101    final WAL wal = HBaseTestingUtility.createWal(conf, logDir, hri);
5102    return initHRegion(tableName, startKey, stopKey, isReadOnly,
5103        Durability.SYNC_WAL, wal, families);
5104  }
5105
5106  /**
5107   * @return A region on which you must call
5108   *         {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} when done.
5109   */
5110  public HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey,
5111      boolean isReadOnly, Durability durability, WAL wal, byte[]... families) throws IOException {
5112    return TEST_UTIL.createLocalHRegion(tableName, startKey, stopKey,
5113        isReadOnly, durability, wal, families);
5114  }
5115
5116  /**
5117   * Assert that the passed in Cell has expected contents for the specified row,
5118   * column & timestamp.
5119   */
5120  private void checkOneCell(Cell kv, byte[] cf, int rowIdx, int colIdx, long ts) {
5121    String ctx = "rowIdx=" + rowIdx + "; colIdx=" + colIdx + "; ts=" + ts;
5122    assertEquals("Row mismatch which checking: " + ctx, "row:" + rowIdx,
5123        Bytes.toString(CellUtil.cloneRow(kv)));
5124    assertEquals("ColumnFamily mismatch while checking: " + ctx, Bytes.toString(cf),
5125        Bytes.toString(CellUtil.cloneFamily(kv)));
5126    assertEquals("Column qualifier mismatch while checking: " + ctx, "column:" + colIdx,
5127        Bytes.toString(CellUtil.cloneQualifier(kv)));
5128    assertEquals("Timestamp mismatch while checking: " + ctx, ts, kv.getTimestamp());
5129    assertEquals("Value mismatch while checking: " + ctx, "value-version-" + ts,
5130        Bytes.toString(CellUtil.cloneValue(kv)));
5131  }
5132
5133  @Test
5134  public void testReverseScanner_FromMemStore_SingleCF_Normal()
5135      throws IOException {
5136    byte[] rowC = Bytes.toBytes("rowC");
5137    byte[] rowA = Bytes.toBytes("rowA");
5138    byte[] rowB = Bytes.toBytes("rowB");
5139    byte[] cf = Bytes.toBytes("CF");
5140    byte[][] families = { cf };
5141    byte[] col = Bytes.toBytes("C");
5142    long ts = 1;
5143    this.region = initHRegion(tableName, method, families);
5144    try {
5145      KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null);
5146      KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put,
5147          null);
5148      KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null);
5149      KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null);
5150      Put put = null;
5151      put = new Put(rowC);
5152      put.add(kv1);
5153      put.add(kv11);
5154      region.put(put);
5155      put = new Put(rowA);
5156      put.add(kv2);
5157      region.put(put);
5158      put = new Put(rowB);
5159      put.add(kv3);
5160      region.put(put);
5161
5162      Scan scan = new Scan(rowC);
5163      scan.setMaxVersions(5);
5164      scan.setReversed(true);
5165      InternalScanner scanner = region.getScanner(scan);
5166      List<Cell> currRow = new ArrayList<>();
5167      boolean hasNext = scanner.next(currRow);
5168      assertEquals(2, currRow.size());
5169      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5170          .get(0).getRowLength(), rowC, 0, rowC.length));
5171      assertTrue(hasNext);
5172      currRow.clear();
5173      hasNext = scanner.next(currRow);
5174      assertEquals(1, currRow.size());
5175      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5176          .get(0).getRowLength(), rowB, 0, rowB.length));
5177      assertTrue(hasNext);
5178      currRow.clear();
5179      hasNext = scanner.next(currRow);
5180      assertEquals(1, currRow.size());
5181      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5182          .get(0).getRowLength(), rowA, 0, rowA.length));
5183      assertFalse(hasNext);
5184      scanner.close();
5185    } finally {
5186      HBaseTestingUtility.closeRegionAndWAL(this.region);
5187      this.region = null;
5188    }
5189  }
5190
5191  @Test
5192  public void testReverseScanner_FromMemStore_SingleCF_LargerKey()
5193      throws IOException {
5194    byte[] rowC = Bytes.toBytes("rowC");
5195    byte[] rowA = Bytes.toBytes("rowA");
5196    byte[] rowB = Bytes.toBytes("rowB");
5197    byte[] rowD = Bytes.toBytes("rowD");
5198    byte[] cf = Bytes.toBytes("CF");
5199    byte[][] families = { cf };
5200    byte[] col = Bytes.toBytes("C");
5201    long ts = 1;
5202    this.region = initHRegion(tableName, method, families);
5203    try {
5204      KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null);
5205      KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put,
5206          null);
5207      KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null);
5208      KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null);
5209      Put put = null;
5210      put = new Put(rowC);
5211      put.add(kv1);
5212      put.add(kv11);
5213      region.put(put);
5214      put = new Put(rowA);
5215      put.add(kv2);
5216      region.put(put);
5217      put = new Put(rowB);
5218      put.add(kv3);
5219      region.put(put);
5220
5221      Scan scan = new Scan(rowD);
5222      List<Cell> currRow = new ArrayList<>();
5223      scan.setReversed(true);
5224      scan.setMaxVersions(5);
5225      InternalScanner scanner = region.getScanner(scan);
5226      boolean hasNext = scanner.next(currRow);
5227      assertEquals(2, currRow.size());
5228      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5229          .get(0).getRowLength(), rowC, 0, rowC.length));
5230      assertTrue(hasNext);
5231      currRow.clear();
5232      hasNext = scanner.next(currRow);
5233      assertEquals(1, currRow.size());
5234      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5235          .get(0).getRowLength(), rowB, 0, rowB.length));
5236      assertTrue(hasNext);
5237      currRow.clear();
5238      hasNext = scanner.next(currRow);
5239      assertEquals(1, currRow.size());
5240      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5241          .get(0).getRowLength(), rowA, 0, rowA.length));
5242      assertFalse(hasNext);
5243      scanner.close();
5244    } finally {
5245      HBaseTestingUtility.closeRegionAndWAL(this.region);
5246      this.region = null;
5247    }
5248  }
5249
5250  @Test
5251  public void testReverseScanner_FromMemStore_SingleCF_FullScan()
5252      throws IOException {
5253    byte[] rowC = Bytes.toBytes("rowC");
5254    byte[] rowA = Bytes.toBytes("rowA");
5255    byte[] rowB = Bytes.toBytes("rowB");
5256    byte[] cf = Bytes.toBytes("CF");
5257    byte[][] families = { cf };
5258    byte[] col = Bytes.toBytes("C");
5259    long ts = 1;
5260    this.region = initHRegion(tableName, method, families);
5261    try {
5262      KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null);
5263      KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put,
5264          null);
5265      KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null);
5266      KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null);
5267      Put put = null;
5268      put = new Put(rowC);
5269      put.add(kv1);
5270      put.add(kv11);
5271      region.put(put);
5272      put = new Put(rowA);
5273      put.add(kv2);
5274      region.put(put);
5275      put = new Put(rowB);
5276      put.add(kv3);
5277      region.put(put);
5278      Scan scan = new Scan();
5279      List<Cell> currRow = new ArrayList<>();
5280      scan.setReversed(true);
5281      InternalScanner scanner = region.getScanner(scan);
5282      boolean hasNext = scanner.next(currRow);
5283      assertEquals(1, currRow.size());
5284      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5285          .get(0).getRowLength(), rowC, 0, rowC.length));
5286      assertTrue(hasNext);
5287      currRow.clear();
5288      hasNext = scanner.next(currRow);
5289      assertEquals(1, currRow.size());
5290      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5291          .get(0).getRowLength(), rowB, 0, rowB.length));
5292      assertTrue(hasNext);
5293      currRow.clear();
5294      hasNext = scanner.next(currRow);
5295      assertEquals(1, currRow.size());
5296      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5297          .get(0).getRowLength(), rowA, 0, rowA.length));
5298      assertFalse(hasNext);
5299      scanner.close();
5300    } finally {
5301      HBaseTestingUtility.closeRegionAndWAL(this.region);
5302      this.region = null;
5303    }
5304  }
5305
5306  @Test
5307  public void testReverseScanner_moreRowsMayExistAfter() throws IOException {
5308    // case for "INCLUDE_AND_SEEK_NEXT_ROW & SEEK_NEXT_ROW" endless loop
5309    byte[] rowA = Bytes.toBytes("rowA");
5310    byte[] rowB = Bytes.toBytes("rowB");
5311    byte[] rowC = Bytes.toBytes("rowC");
5312    byte[] rowD = Bytes.toBytes("rowD");
5313    byte[] rowE = Bytes.toBytes("rowE");
5314    byte[] cf = Bytes.toBytes("CF");
5315    byte[][] families = { cf };
5316    byte[] col1 = Bytes.toBytes("col1");
5317    byte[] col2 = Bytes.toBytes("col2");
5318    long ts = 1;
5319    this.region = initHRegion(tableName, method, families);
5320    try {
5321      KeyValue kv1 = new KeyValue(rowA, cf, col1, ts, KeyValue.Type.Put, null);
5322      KeyValue kv2 = new KeyValue(rowB, cf, col1, ts, KeyValue.Type.Put, null);
5323      KeyValue kv3 = new KeyValue(rowC, cf, col1, ts, KeyValue.Type.Put, null);
5324      KeyValue kv4_1 = new KeyValue(rowD, cf, col1, ts, KeyValue.Type.Put, null);
5325      KeyValue kv4_2 = new KeyValue(rowD, cf, col2, ts, KeyValue.Type.Put, null);
5326      KeyValue kv5 = new KeyValue(rowE, cf, col1, ts, KeyValue.Type.Put, null);
5327      Put put = null;
5328      put = new Put(rowA);
5329      put.add(kv1);
5330      region.put(put);
5331      put = new Put(rowB);
5332      put.add(kv2);
5333      region.put(put);
5334      put = new Put(rowC);
5335      put.add(kv3);
5336      region.put(put);
5337      put = new Put(rowD);
5338      put.add(kv4_1);
5339      region.put(put);
5340      put = new Put(rowD);
5341      put.add(kv4_2);
5342      region.put(put);
5343      put = new Put(rowE);
5344      put.add(kv5);
5345      region.put(put);
5346      region.flush(true);
5347      Scan scan = new Scan(rowD, rowA);
5348      scan.addColumn(families[0], col1);
5349      scan.setReversed(true);
5350      List<Cell> currRow = new ArrayList<>();
5351      InternalScanner scanner = region.getScanner(scan);
5352      boolean hasNext = scanner.next(currRow);
5353      assertEquals(1, currRow.size());
5354      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5355          .get(0).getRowLength(), rowD, 0, rowD.length));
5356      assertTrue(hasNext);
5357      currRow.clear();
5358      hasNext = scanner.next(currRow);
5359      assertEquals(1, currRow.size());
5360      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5361          .get(0).getRowLength(), rowC, 0, rowC.length));
5362      assertTrue(hasNext);
5363      currRow.clear();
5364      hasNext = scanner.next(currRow);
5365      assertEquals(1, currRow.size());
5366      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5367          .get(0).getRowLength(), rowB, 0, rowB.length));
5368      assertFalse(hasNext);
5369      scanner.close();
5370
5371      scan = new Scan(rowD, rowA);
5372      scan.addColumn(families[0], col2);
5373      scan.setReversed(true);
5374      currRow.clear();
5375      scanner = region.getScanner(scan);
5376      hasNext = scanner.next(currRow);
5377      assertEquals(1, currRow.size());
5378      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5379          .get(0).getRowLength(), rowD, 0, rowD.length));
5380      scanner.close();
5381    } finally {
5382      HBaseTestingUtility.closeRegionAndWAL(this.region);
5383      this.region = null;
5384    }
5385  }
5386
5387  @Test
5388  public void testReverseScanner_smaller_blocksize() throws IOException {
5389    // case to ensure no conflict with HFile index optimization
5390    byte[] rowA = Bytes.toBytes("rowA");
5391    byte[] rowB = Bytes.toBytes("rowB");
5392    byte[] rowC = Bytes.toBytes("rowC");
5393    byte[] rowD = Bytes.toBytes("rowD");
5394    byte[] rowE = Bytes.toBytes("rowE");
5395    byte[] cf = Bytes.toBytes("CF");
5396    byte[][] families = { cf };
5397    byte[] col1 = Bytes.toBytes("col1");
5398    byte[] col2 = Bytes.toBytes("col2");
5399    long ts = 1;
5400    HBaseConfiguration config = new HBaseConfiguration();
5401    config.setInt("test.block.size", 1);
5402    this.region = initHRegion(tableName, method, config, families);
5403    try {
5404      KeyValue kv1 = new KeyValue(rowA, cf, col1, ts, KeyValue.Type.Put, null);
5405      KeyValue kv2 = new KeyValue(rowB, cf, col1, ts, KeyValue.Type.Put, null);
5406      KeyValue kv3 = new KeyValue(rowC, cf, col1, ts, KeyValue.Type.Put, null);
5407      KeyValue kv4_1 = new KeyValue(rowD, cf, col1, ts, KeyValue.Type.Put, null);
5408      KeyValue kv4_2 = new KeyValue(rowD, cf, col2, ts, KeyValue.Type.Put, null);
5409      KeyValue kv5 = new KeyValue(rowE, cf, col1, ts, KeyValue.Type.Put, null);
5410      Put put = null;
5411      put = new Put(rowA);
5412      put.add(kv1);
5413      region.put(put);
5414      put = new Put(rowB);
5415      put.add(kv2);
5416      region.put(put);
5417      put = new Put(rowC);
5418      put.add(kv3);
5419      region.put(put);
5420      put = new Put(rowD);
5421      put.add(kv4_1);
5422      region.put(put);
5423      put = new Put(rowD);
5424      put.add(kv4_2);
5425      region.put(put);
5426      put = new Put(rowE);
5427      put.add(kv5);
5428      region.put(put);
5429      region.flush(true);
5430      Scan scan = new Scan(rowD, rowA);
5431      scan.addColumn(families[0], col1);
5432      scan.setReversed(true);
5433      List<Cell> currRow = new ArrayList<>();
5434      InternalScanner scanner = region.getScanner(scan);
5435      boolean hasNext = scanner.next(currRow);
5436      assertEquals(1, currRow.size());
5437      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5438          .get(0).getRowLength(), rowD, 0, rowD.length));
5439      assertTrue(hasNext);
5440      currRow.clear();
5441      hasNext = scanner.next(currRow);
5442      assertEquals(1, currRow.size());
5443      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5444          .get(0).getRowLength(), rowC, 0, rowC.length));
5445      assertTrue(hasNext);
5446      currRow.clear();
5447      hasNext = scanner.next(currRow);
5448      assertEquals(1, currRow.size());
5449      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5450          .get(0).getRowLength(), rowB, 0, rowB.length));
5451      assertFalse(hasNext);
5452      scanner.close();
5453
5454      scan = new Scan(rowD, rowA);
5455      scan.addColumn(families[0], col2);
5456      scan.setReversed(true);
5457      currRow.clear();
5458      scanner = region.getScanner(scan);
5459      hasNext = scanner.next(currRow);
5460      assertEquals(1, currRow.size());
5461      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5462          .get(0).getRowLength(), rowD, 0, rowD.length));
5463      scanner.close();
5464    } finally {
5465      HBaseTestingUtility.closeRegionAndWAL(this.region);
5466      this.region = null;
5467    }
5468  }
5469
5470  @Test
5471  public void testReverseScanner_FromMemStoreAndHFiles_MultiCFs1()
5472      throws IOException {
5473    byte[] row0 = Bytes.toBytes("row0"); // 1 kv
5474    byte[] row1 = Bytes.toBytes("row1"); // 2 kv
5475    byte[] row2 = Bytes.toBytes("row2"); // 4 kv
5476    byte[] row3 = Bytes.toBytes("row3"); // 2 kv
5477    byte[] row4 = Bytes.toBytes("row4"); // 5 kv
5478    byte[] row5 = Bytes.toBytes("row5"); // 2 kv
5479    byte[] cf1 = Bytes.toBytes("CF1");
5480    byte[] cf2 = Bytes.toBytes("CF2");
5481    byte[] cf3 = Bytes.toBytes("CF3");
5482    byte[][] families = { cf1, cf2, cf3 };
5483    byte[] col = Bytes.toBytes("C");
5484    long ts = 1;
5485    HBaseConfiguration conf = new HBaseConfiguration();
5486    // disable compactions in this test.
5487    conf.setInt("hbase.hstore.compactionThreshold", 10000);
5488    this.region = initHRegion(tableName, method, conf, families);
5489    try {
5490      // kv naming style: kv(row number) totalKvCountInThisRow seq no
5491      KeyValue kv0_1_1 = new KeyValue(row0, cf1, col, ts, KeyValue.Type.Put,
5492          null);
5493      KeyValue kv1_2_1 = new KeyValue(row1, cf2, col, ts, KeyValue.Type.Put,
5494          null);
5495      KeyValue kv1_2_2 = new KeyValue(row1, cf1, col, ts + 1,
5496          KeyValue.Type.Put, null);
5497      KeyValue kv2_4_1 = new KeyValue(row2, cf2, col, ts, KeyValue.Type.Put,
5498          null);
5499      KeyValue kv2_4_2 = new KeyValue(row2, cf1, col, ts, KeyValue.Type.Put,
5500          null);
5501      KeyValue kv2_4_3 = new KeyValue(row2, cf3, col, ts, KeyValue.Type.Put,
5502          null);
5503      KeyValue kv2_4_4 = new KeyValue(row2, cf1, col, ts + 4,
5504          KeyValue.Type.Put, null);
5505      KeyValue kv3_2_1 = new KeyValue(row3, cf2, col, ts, KeyValue.Type.Put,
5506          null);
5507      KeyValue kv3_2_2 = new KeyValue(row3, cf1, col, ts + 4,
5508          KeyValue.Type.Put, null);
5509      KeyValue kv4_5_1 = new KeyValue(row4, cf1, col, ts, KeyValue.Type.Put,
5510          null);
5511      KeyValue kv4_5_2 = new KeyValue(row4, cf3, col, ts, KeyValue.Type.Put,
5512          null);
5513      KeyValue kv4_5_3 = new KeyValue(row4, cf3, col, ts + 5,
5514          KeyValue.Type.Put, null);
5515      KeyValue kv4_5_4 = new KeyValue(row4, cf2, col, ts, KeyValue.Type.Put,
5516          null);
5517      KeyValue kv4_5_5 = new KeyValue(row4, cf1, col, ts + 3,
5518          KeyValue.Type.Put, null);
5519      KeyValue kv5_2_1 = new KeyValue(row5, cf2, col, ts, KeyValue.Type.Put,
5520          null);
5521      KeyValue kv5_2_2 = new KeyValue(row5, cf3, col, ts, KeyValue.Type.Put,
5522          null);
5523      // hfiles(cf1/cf2) :"row1"(1 kv) / "row2"(1 kv) / "row4"(2 kv)
5524      Put put = null;
5525      put = new Put(row1);
5526      put.add(kv1_2_1);
5527      region.put(put);
5528      put = new Put(row2);
5529      put.add(kv2_4_1);
5530      region.put(put);
5531      put = new Put(row4);
5532      put.add(kv4_5_4);
5533      put.add(kv4_5_5);
5534      region.put(put);
5535      region.flush(true);
5536      // hfiles(cf1/cf3) : "row1" (1 kvs) / "row2" (1 kv) / "row4" (2 kv)
5537      put = new Put(row4);
5538      put.add(kv4_5_1);
5539      put.add(kv4_5_3);
5540      region.put(put);
5541      put = new Put(row1);
5542      put.add(kv1_2_2);
5543      region.put(put);
5544      put = new Put(row2);
5545      put.add(kv2_4_4);
5546      region.put(put);
5547      region.flush(true);
5548      // hfiles(cf1/cf3) : "row2"(2 kv) / "row3"(1 kvs) / "row4" (1 kv)
5549      put = new Put(row4);
5550      put.add(kv4_5_2);
5551      region.put(put);
5552      put = new Put(row2);
5553      put.add(kv2_4_2);
5554      put.add(kv2_4_3);
5555      region.put(put);
5556      put = new Put(row3);
5557      put.add(kv3_2_2);
5558      region.put(put);
5559      region.flush(true);
5560      // memstore(cf1/cf2/cf3) : "row0" (1 kvs) / "row3" ( 1 kv) / "row5" (max)
5561      // ( 2 kv)
5562      put = new Put(row0);
5563      put.add(kv0_1_1);
5564      region.put(put);
5565      put = new Put(row3);
5566      put.add(kv3_2_1);
5567      region.put(put);
5568      put = new Put(row5);
5569      put.add(kv5_2_1);
5570      put.add(kv5_2_2);
5571      region.put(put);
5572      // scan range = ["row4", min), skip the max "row5"
5573      Scan scan = new Scan(row4);
5574      scan.setMaxVersions(5);
5575      scan.setBatch(3);
5576      scan.setReversed(true);
5577      InternalScanner scanner = region.getScanner(scan);
5578      List<Cell> currRow = new ArrayList<>();
5579      boolean hasNext = false;
5580      // 1. scan out "row4" (5 kvs), "row5" can't be scanned out since not
5581      // included in scan range
5582      // "row4" takes 2 next() calls since batch=3
5583      hasNext = scanner.next(currRow);
5584      assertEquals(3, currRow.size());
5585      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5586          .get(0).getRowLength(), row4, 0, row4.length));
5587      assertTrue(hasNext);
5588      currRow.clear();
5589      hasNext = scanner.next(currRow);
5590      assertEquals(2, currRow.size());
5591      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(),
5592          currRow.get(0).getRowLength(), row4, 0,
5593        row4.length));
5594      assertTrue(hasNext);
5595      // 2. scan out "row3" (2 kv)
5596      currRow.clear();
5597      hasNext = scanner.next(currRow);
5598      assertEquals(2, currRow.size());
5599      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5600          .get(0).getRowLength(), row3, 0, row3.length));
5601      assertTrue(hasNext);
5602      // 3. scan out "row2" (4 kvs)
5603      // "row2" takes 2 next() calls since batch=3
5604      currRow.clear();
5605      hasNext = scanner.next(currRow);
5606      assertEquals(3, currRow.size());
5607      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5608          .get(0).getRowLength(), row2, 0, row2.length));
5609      assertTrue(hasNext);
5610      currRow.clear();
5611      hasNext = scanner.next(currRow);
5612      assertEquals(1, currRow.size());
5613      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5614          .get(0).getRowLength(), row2, 0, row2.length));
5615      assertTrue(hasNext);
5616      // 4. scan out "row1" (2 kv)
5617      currRow.clear();
5618      hasNext = scanner.next(currRow);
5619      assertEquals(2, currRow.size());
5620      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5621          .get(0).getRowLength(), row1, 0, row1.length));
5622      assertTrue(hasNext);
5623      // 5. scan out "row0" (1 kv)
5624      currRow.clear();
5625      hasNext = scanner.next(currRow);
5626      assertEquals(1, currRow.size());
5627      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5628          .get(0).getRowLength(), row0, 0, row0.length));
5629      assertFalse(hasNext);
5630
5631      scanner.close();
5632    } finally {
5633      HBaseTestingUtility.closeRegionAndWAL(this.region);
5634      this.region = null;
5635    }
5636  }
5637
5638  @Test
5639  public void testReverseScanner_FromMemStoreAndHFiles_MultiCFs2()
5640      throws IOException {
5641    byte[] row1 = Bytes.toBytes("row1");
5642    byte[] row2 = Bytes.toBytes("row2");
5643    byte[] row3 = Bytes.toBytes("row3");
5644    byte[] row4 = Bytes.toBytes("row4");
5645    byte[] cf1 = Bytes.toBytes("CF1");
5646    byte[] cf2 = Bytes.toBytes("CF2");
5647    byte[] cf3 = Bytes.toBytes("CF3");
5648    byte[] cf4 = Bytes.toBytes("CF4");
5649    byte[][] families = { cf1, cf2, cf3, cf4 };
5650    byte[] col = Bytes.toBytes("C");
5651    long ts = 1;
5652    HBaseConfiguration conf = new HBaseConfiguration();
5653    // disable compactions in this test.
5654    conf.setInt("hbase.hstore.compactionThreshold", 10000);
5655    this.region = initHRegion(tableName, method, conf, families);
5656    try {
5657      KeyValue kv1 = new KeyValue(row1, cf1, col, ts, KeyValue.Type.Put, null);
5658      KeyValue kv2 = new KeyValue(row2, cf2, col, ts, KeyValue.Type.Put, null);
5659      KeyValue kv3 = new KeyValue(row3, cf3, col, ts, KeyValue.Type.Put, null);
5660      KeyValue kv4 = new KeyValue(row4, cf4, col, ts, KeyValue.Type.Put, null);
5661      // storefile1
5662      Put put = new Put(row1);
5663      put.add(kv1);
5664      region.put(put);
5665      region.flush(true);
5666      // storefile2
5667      put = new Put(row2);
5668      put.add(kv2);
5669      region.put(put);
5670      region.flush(true);
5671      // storefile3
5672      put = new Put(row3);
5673      put.add(kv3);
5674      region.put(put);
5675      region.flush(true);
5676      // memstore
5677      put = new Put(row4);
5678      put.add(kv4);
5679      region.put(put);
5680      // scan range = ["row4", min)
5681      Scan scan = new Scan(row4);
5682      scan.setReversed(true);
5683      scan.setBatch(10);
5684      InternalScanner scanner = region.getScanner(scan);
5685      List<Cell> currRow = new ArrayList<>();
5686      boolean hasNext = scanner.next(currRow);
5687      assertEquals(1, currRow.size());
5688      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5689          .get(0).getRowLength(), row4, 0, row4.length));
5690      assertTrue(hasNext);
5691      currRow.clear();
5692      hasNext = scanner.next(currRow);
5693      assertEquals(1, currRow.size());
5694      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5695          .get(0).getRowLength(), row3, 0, row3.length));
5696      assertTrue(hasNext);
5697      currRow.clear();
5698      hasNext = scanner.next(currRow);
5699      assertEquals(1, currRow.size());
5700      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5701          .get(0).getRowLength(), row2, 0, row2.length));
5702      assertTrue(hasNext);
5703      currRow.clear();
5704      hasNext = scanner.next(currRow);
5705      assertEquals(1, currRow.size());
5706      assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
5707          .get(0).getRowLength(), row1, 0, row1.length));
5708      assertFalse(hasNext);
5709    } finally {
5710      HBaseTestingUtility.closeRegionAndWAL(this.region);
5711      this.region = null;
5712    }
5713  }
5714
5715  /**
5716   * Test for HBASE-14497: Reverse Scan threw StackOverflow caused by readPt checking
5717   */
5718  @Test
5719  public void testReverseScanner_StackOverflow() throws IOException {
5720    byte[] cf1 = Bytes.toBytes("CF1");
5721    byte[][] families = {cf1};
5722    byte[] col = Bytes.toBytes("C");
5723    HBaseConfiguration conf = new HBaseConfiguration();
5724    this.region = initHRegion(tableName, method, conf, families);
5725    try {
5726      // setup with one storefile and one memstore, to create scanner and get an earlier readPt
5727      Put put = new Put(Bytes.toBytes("19998"));
5728      put.addColumn(cf1, col, Bytes.toBytes("val"));
5729      region.put(put);
5730      region.flushcache(true, true, FlushLifeCycleTracker.DUMMY);
5731      Put put2 = new Put(Bytes.toBytes("19997"));
5732      put2.addColumn(cf1, col, Bytes.toBytes("val"));
5733      region.put(put2);
5734
5735      Scan scan = new Scan(Bytes.toBytes("19998"));
5736      scan.setReversed(true);
5737      InternalScanner scanner = region.getScanner(scan);
5738
5739      // create one storefile contains many rows will be skipped
5740      // to check StoreFileScanner.seekToPreviousRow
5741      for (int i = 10000; i < 20000; i++) {
5742        Put p = new Put(Bytes.toBytes(""+i));
5743        p.addColumn(cf1, col, Bytes.toBytes("" + i));
5744        region.put(p);
5745      }
5746      region.flushcache(true, true, FlushLifeCycleTracker.DUMMY);
5747
5748      // create one memstore contains many rows will be skipped
5749      // to check MemStoreScanner.seekToPreviousRow
5750      for (int i = 10000; i < 20000; i++) {
5751        Put p = new Put(Bytes.toBytes(""+i));
5752        p.addColumn(cf1, col, Bytes.toBytes("" + i));
5753        region.put(p);
5754      }
5755
5756      List<Cell> currRow = new ArrayList<>();
5757      boolean hasNext;
5758      do {
5759        hasNext = scanner.next(currRow);
5760      } while (hasNext);
5761      assertEquals(2, currRow.size());
5762      assertEquals("19998", Bytes.toString(currRow.get(0).getRowArray(),
5763        currRow.get(0).getRowOffset(), currRow.get(0).getRowLength()));
5764      assertEquals("19997", Bytes.toString(currRow.get(1).getRowArray(),
5765        currRow.get(1).getRowOffset(), currRow.get(1).getRowLength()));
5766    } finally {
5767      HBaseTestingUtility.closeRegionAndWAL(this.region);
5768      this.region = null;
5769    }
5770  }
5771
5772  @Test
5773  public void testReverseScanShouldNotScanMemstoreIfReadPtLesser() throws Exception {
5774    byte[] cf1 = Bytes.toBytes("CF1");
5775    byte[][] families = { cf1 };
5776    byte[] col = Bytes.toBytes("C");
5777    HBaseConfiguration conf = new HBaseConfiguration();
5778    this.region = initHRegion(tableName, method, conf, families);
5779    try {
5780      // setup with one storefile and one memstore, to create scanner and get an earlier readPt
5781      Put put = new Put(Bytes.toBytes("19996"));
5782      put.addColumn(cf1, col, Bytes.toBytes("val"));
5783      region.put(put);
5784      Put put2 = new Put(Bytes.toBytes("19995"));
5785      put2.addColumn(cf1, col, Bytes.toBytes("val"));
5786      region.put(put2);
5787      // create a reverse scan
5788      Scan scan = new Scan(Bytes.toBytes("19996"));
5789      scan.setReversed(true);
5790      RegionScannerImpl scanner = region.getScanner(scan);
5791
5792      // flush the cache. This will reset the store scanner
5793      region.flushcache(true, true, FlushLifeCycleTracker.DUMMY);
5794
5795      // create one memstore contains many rows will be skipped
5796      // to check MemStoreScanner.seekToPreviousRow
5797      for (int i = 10000; i < 20000; i++) {
5798        Put p = new Put(Bytes.toBytes("" + i));
5799        p.addColumn(cf1, col, Bytes.toBytes("" + i));
5800        region.put(p);
5801      }
5802      List<Cell> currRow = new ArrayList<>();
5803      boolean hasNext;
5804      boolean assertDone = false;
5805      do {
5806        hasNext = scanner.next(currRow);
5807        // With HBASE-15871, after the scanner is reset the memstore scanner should not be
5808        // added here
5809        if (!assertDone) {
5810          StoreScanner current =
5811              (StoreScanner) (scanner.storeHeap).getCurrentForTesting();
5812          List<KeyValueScanner> scanners = current.getAllScannersForTesting();
5813          assertEquals("There should be only one scanner the store file scanner", 1,
5814            scanners.size());
5815          assertDone = true;
5816        }
5817      } while (hasNext);
5818      assertEquals(2, currRow.size());
5819      assertEquals("19996", Bytes.toString(currRow.get(0).getRowArray(),
5820        currRow.get(0).getRowOffset(), currRow.get(0).getRowLength()));
5821      assertEquals("19995", Bytes.toString(currRow.get(1).getRowArray(),
5822        currRow.get(1).getRowOffset(), currRow.get(1).getRowLength()));
5823    } finally {
5824      HBaseTestingUtility.closeRegionAndWAL(this.region);
5825      this.region = null;
5826    }
5827  }
5828
5829  @Test
5830  public void testReverseScanWhenPutCellsAfterOpenReverseScan() throws Exception {
5831    byte[] cf1 = Bytes.toBytes("CF1");
5832    byte[][] families = { cf1 };
5833    byte[] col = Bytes.toBytes("C");
5834
5835    HBaseConfiguration conf = new HBaseConfiguration();
5836    this.region = initHRegion(tableName, method, conf, families);
5837
5838    Put put = new Put(Bytes.toBytes("199996"));
5839    put.addColumn(cf1, col, Bytes.toBytes("val"));
5840    region.put(put);
5841    Put put2 = new Put(Bytes.toBytes("199995"));
5842    put2.addColumn(cf1, col, Bytes.toBytes("val"));
5843    region.put(put2);
5844
5845    // Create a reverse scan
5846    Scan scan = new Scan(Bytes.toBytes("199996"));
5847    scan.setReversed(true);
5848    RegionScannerImpl scanner = region.getScanner(scan);
5849
5850    // Put a lot of cells that have sequenceIDs grater than the readPt of the reverse scan
5851    for (int i = 100000; i < 200000; i++) {
5852      Put p = new Put(Bytes.toBytes("" + i));
5853      p.addColumn(cf1, col, Bytes.toBytes("" + i));
5854      region.put(p);
5855    }
5856    List<Cell> currRow = new ArrayList<>();
5857    boolean hasNext;
5858    do {
5859      hasNext = scanner.next(currRow);
5860    } while (hasNext);
5861
5862    assertEquals(2, currRow.size());
5863    assertEquals("199996", Bytes.toString(currRow.get(0).getRowArray(),
5864      currRow.get(0).getRowOffset(), currRow.get(0).getRowLength()));
5865    assertEquals("199995", Bytes.toString(currRow.get(1).getRowArray(),
5866      currRow.get(1).getRowOffset(), currRow.get(1).getRowLength()));
5867  }
5868
5869  @Test
5870  public void testWriteRequestsCounter() throws IOException {
5871    byte[] fam = Bytes.toBytes("info");
5872    byte[][] families = { fam };
5873    this.region = initHRegion(tableName, method, CONF, families);
5874
5875    Assert.assertEquals(0L, region.getWriteRequestsCount());
5876
5877    Put put = new Put(row);
5878    put.addColumn(fam, fam, fam);
5879
5880    Assert.assertEquals(0L, region.getWriteRequestsCount());
5881    region.put(put);
5882    Assert.assertEquals(1L, region.getWriteRequestsCount());
5883    region.put(put);
5884    Assert.assertEquals(2L, region.getWriteRequestsCount());
5885    region.put(put);
5886    Assert.assertEquals(3L, region.getWriteRequestsCount());
5887
5888    region.delete(new Delete(row));
5889    Assert.assertEquals(4L, region.getWriteRequestsCount());
5890
5891    HBaseTestingUtility.closeRegionAndWAL(this.region);
5892    this.region = null;
5893  }
5894
5895  @Test
5896  public void testOpenRegionWrittenToWAL() throws Exception {
5897    final ServerName serverName = ServerName.valueOf(name.getMethodName(), 100, 42);
5898    final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName));
5899
5900    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
5901    htd.addFamily(new HColumnDescriptor(fam1));
5902    htd.addFamily(new HColumnDescriptor(fam2));
5903
5904    HRegionInfo hri = new HRegionInfo(htd.getTableName(),
5905      HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY);
5906
5907    // open the region w/o rss and wal and flush some files
5908    HRegion region =
5909         HBaseTestingUtility.createRegionAndWAL(hri, TEST_UTIL.getDataTestDir(), TEST_UTIL
5910             .getConfiguration(), htd);
5911    assertNotNull(region);
5912
5913    // create a file in fam1 for the region before opening in OpenRegionHandler
5914    region.put(new Put(Bytes.toBytes("a")).addColumn(fam1, fam1, fam1));
5915    region.flush(true);
5916    HBaseTestingUtility.closeRegionAndWAL(region);
5917
5918    ArgumentCaptor<WALEdit> editCaptor = ArgumentCaptor.forClass(WALEdit.class);
5919
5920    // capture append() calls
5921    WAL wal = mockWAL();
5922    when(rss.getWAL((HRegionInfo) any())).thenReturn(wal);
5923
5924    try {
5925      region = HRegion.openHRegion(hri, htd, rss.getWAL(hri),
5926        TEST_UTIL.getConfiguration(), rss, null);
5927
5928      verify(wal, times(1)).append((HRegionInfo)any(), (WALKeyImpl)any()
5929        , editCaptor.capture(), anyBoolean());
5930
5931      WALEdit edit = editCaptor.getValue();
5932      assertNotNull(edit);
5933      assertNotNull(edit.getCells());
5934      assertEquals(1, edit.getCells().size());
5935      RegionEventDescriptor desc = WALEdit.getRegionEventDescriptor(edit.getCells().get(0));
5936      assertNotNull(desc);
5937
5938      LOG.info("RegionEventDescriptor from WAL: " + desc);
5939
5940      assertEquals(RegionEventDescriptor.EventType.REGION_OPEN, desc.getEventType());
5941      assertTrue(Bytes.equals(desc.getTableName().toByteArray(), htd.getTableName().toBytes()));
5942      assertTrue(Bytes.equals(desc.getEncodedRegionName().toByteArray(),
5943        hri.getEncodedNameAsBytes()));
5944      assertTrue(desc.getLogSequenceNumber() > 0);
5945      assertEquals(serverName, ProtobufUtil.toServerName(desc.getServer()));
5946      assertEquals(2, desc.getStoresCount());
5947
5948      StoreDescriptor store = desc.getStores(0);
5949      assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam1));
5950      assertEquals(store.getStoreHomeDir(), Bytes.toString(fam1));
5951      assertEquals(1, store.getStoreFileCount()); // 1store file
5952      assertFalse(store.getStoreFile(0).contains("/")); // ensure path is relative
5953
5954      store = desc.getStores(1);
5955      assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam2));
5956      assertEquals(store.getStoreHomeDir(), Bytes.toString(fam2));
5957      assertEquals(0, store.getStoreFileCount()); // no store files
5958
5959    } finally {
5960      HBaseTestingUtility.closeRegionAndWAL(region);
5961    }
5962  }
5963
5964  // Helper for test testOpenRegionWrittenToWALForLogReplay
5965  static class HRegionWithSeqId extends HRegion {
5966    public HRegionWithSeqId(final Path tableDir, final WAL wal, final FileSystem fs,
5967        final Configuration confParam, final RegionInfo regionInfo,
5968        final TableDescriptor htd, final RegionServerServices rsServices) {
5969      super(tableDir, wal, fs, confParam, regionInfo, htd, rsServices);
5970    }
5971    @Override
5972    protected long getNextSequenceId(WAL wal) throws IOException {
5973      return 42;
5974    }
5975  }
5976
5977  @Test
5978  public void testFlushedFileWithNoTags() throws Exception {
5979    final TableName tableName = TableName.valueOf(name.getMethodName());
5980    HTableDescriptor htd = new HTableDescriptor(tableName);
5981    htd.addFamily(new HColumnDescriptor(fam1));
5982    HRegionInfo info = new HRegionInfo(tableName, null, null, false);
5983    Path path = TEST_UTIL.getDataTestDir(getClass().getSimpleName());
5984    region = HBaseTestingUtility.createRegionAndWAL(info, path, TEST_UTIL.getConfiguration(), htd);
5985    Put put = new Put(Bytes.toBytes("a-b-0-0"));
5986    put.addColumn(fam1, qual1, Bytes.toBytes("c1-value"));
5987    region.put(put);
5988    region.flush(true);
5989    HStore store = region.getStore(fam1);
5990    Collection<HStoreFile> storefiles = store.getStorefiles();
5991    for (HStoreFile sf : storefiles) {
5992      assertFalse("Tags should not be present "
5993          ,sf.getReader().getHFileReader().getFileContext().isIncludesTags());
5994    }
5995  }
5996
5997  /**
5998   * Utility method to setup a WAL mock.
5999   * Needs to do the bit where we close latch on the WALKeyImpl on append else test hangs.
6000   * @return
6001   * @throws IOException
6002   */
6003  private WAL mockWAL() throws IOException {
6004    WAL wal = mock(WAL.class);
6005    Mockito.when(wal.append((HRegionInfo)Mockito.any(),
6006        (WALKeyImpl)Mockito.any(), (WALEdit)Mockito.any(), Mockito.anyBoolean())).
6007      thenAnswer(new Answer<Long>() {
6008        @Override
6009        public Long answer(InvocationOnMock invocation) throws Throwable {
6010          WALKeyImpl key = invocation.getArgument(1);
6011          MultiVersionConcurrencyControl.WriteEntry we = key.getMvcc().begin();
6012          key.setWriteEntry(we);
6013          return 1L;
6014        }
6015
6016    });
6017    return wal;
6018  }
6019
6020  @Test
6021  public void testCloseRegionWrittenToWAL() throws Exception {
6022
6023    Path rootDir = new Path(dir + name.getMethodName());
6024    FSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir);
6025
6026    final ServerName serverName = ServerName.valueOf("testCloseRegionWrittenToWAL", 100, 42);
6027    final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName));
6028
6029    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
6030    htd.addFamily(new HColumnDescriptor(fam1));
6031    htd.addFamily(new HColumnDescriptor(fam2));
6032
6033    final HRegionInfo hri = new HRegionInfo(htd.getTableName(),
6034      HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY);
6035
6036    ArgumentCaptor<WALEdit> editCaptor = ArgumentCaptor.forClass(WALEdit.class);
6037
6038    // capture append() calls
6039    WAL wal = mockWAL();
6040    when(rss.getWAL((HRegionInfo) any())).thenReturn(wal);
6041
6042
6043    // create and then open a region first so that it can be closed later
6044    region = HRegion.createHRegion(hri, rootDir, TEST_UTIL.getConfiguration(), htd, rss.getWAL(hri));
6045    region = HRegion.openHRegion(hri, htd, rss.getWAL(hri),
6046      TEST_UTIL.getConfiguration(), rss, null);
6047
6048    // close the region
6049    region.close(false);
6050
6051    // 2 times, one for region open, the other close region
6052    verify(wal, times(2)).append((HRegionInfo)any(), (WALKeyImpl)any(),
6053      editCaptor.capture(), anyBoolean());
6054
6055    WALEdit edit = editCaptor.getAllValues().get(1);
6056    assertNotNull(edit);
6057    assertNotNull(edit.getCells());
6058    assertEquals(1, edit.getCells().size());
6059    RegionEventDescriptor desc = WALEdit.getRegionEventDescriptor(edit.getCells().get(0));
6060    assertNotNull(desc);
6061
6062    LOG.info("RegionEventDescriptor from WAL: " + desc);
6063
6064    assertEquals(RegionEventDescriptor.EventType.REGION_CLOSE, desc.getEventType());
6065    assertTrue(Bytes.equals(desc.getTableName().toByteArray(), htd.getTableName().toBytes()));
6066    assertTrue(Bytes.equals(desc.getEncodedRegionName().toByteArray(),
6067      hri.getEncodedNameAsBytes()));
6068    assertTrue(desc.getLogSequenceNumber() > 0);
6069    assertEquals(serverName, ProtobufUtil.toServerName(desc.getServer()));
6070    assertEquals(2, desc.getStoresCount());
6071
6072    StoreDescriptor store = desc.getStores(0);
6073    assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam1));
6074    assertEquals(store.getStoreHomeDir(), Bytes.toString(fam1));
6075    assertEquals(0, store.getStoreFileCount()); // no store files
6076
6077    store = desc.getStores(1);
6078    assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam2));
6079    assertEquals(store.getStoreHomeDir(), Bytes.toString(fam2));
6080    assertEquals(0, store.getStoreFileCount()); // no store files
6081  }
6082
6083  /**
6084   * Test RegionTooBusyException thrown when region is busy
6085   */
6086  @Test
6087  public void testRegionTooBusy() throws IOException {
6088    byte[] family = Bytes.toBytes("family");
6089    long defaultBusyWaitDuration = CONF.getLong("hbase.busy.wait.duration",
6090      HRegion.DEFAULT_BUSY_WAIT_DURATION);
6091    CONF.setLong("hbase.busy.wait.duration", 1000);
6092    region = initHRegion(tableName, method, CONF, family);
6093    final AtomicBoolean stopped = new AtomicBoolean(true);
6094    Thread t = new Thread(new Runnable() {
6095      @Override
6096      public void run() {
6097        try {
6098          region.lock.writeLock().lock();
6099          stopped.set(false);
6100          while (!stopped.get()) {
6101            Thread.sleep(100);
6102          }
6103        } catch (InterruptedException ie) {
6104        } finally {
6105          region.lock.writeLock().unlock();
6106        }
6107      }
6108    });
6109    t.start();
6110    Get get = new Get(row);
6111    try {
6112      while (stopped.get()) {
6113        Thread.sleep(100);
6114      }
6115      region.get(get);
6116      fail("Should throw RegionTooBusyException");
6117    } catch (InterruptedException ie) {
6118      fail("test interrupted");
6119    } catch (RegionTooBusyException e) {
6120      // Good, expected
6121    } finally {
6122      stopped.set(true);
6123      try {
6124        t.join();
6125      } catch (Throwable e) {
6126      }
6127
6128      HBaseTestingUtility.closeRegionAndWAL(region);
6129      region = null;
6130      CONF.setLong("hbase.busy.wait.duration", defaultBusyWaitDuration);
6131    }
6132  }
6133
6134  @Test
6135  public void testCellTTLs() throws IOException {
6136    IncrementingEnvironmentEdge edge = new IncrementingEnvironmentEdge();
6137    EnvironmentEdgeManager.injectEdge(edge);
6138
6139    final byte[] row = Bytes.toBytes("testRow");
6140    final byte[] q1 = Bytes.toBytes("q1");
6141    final byte[] q2 = Bytes.toBytes("q2");
6142    final byte[] q3 = Bytes.toBytes("q3");
6143    final byte[] q4 = Bytes.toBytes("q4");
6144
6145    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
6146    HColumnDescriptor hcd = new HColumnDescriptor(fam1);
6147    hcd.setTimeToLive(10); // 10 seconds
6148    htd.addFamily(hcd);
6149
6150    Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
6151    conf.setInt(HFile.FORMAT_VERSION_KEY, HFile.MIN_FORMAT_VERSION_WITH_TAGS);
6152
6153    HRegion region = HBaseTestingUtility.createRegionAndWAL(new HRegionInfo(htd.getTableName(),
6154            HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY),
6155        TEST_UTIL.getDataTestDir(), conf, htd);
6156    assertNotNull(region);
6157    try {
6158      long now = EnvironmentEdgeManager.currentTime();
6159      // Add a cell that will expire in 5 seconds via cell TTL
6160      region.put(new Put(row).add(new KeyValue(row, fam1, q1, now,
6161        HConstants.EMPTY_BYTE_ARRAY, new ArrayBackedTag[] {
6162          // TTL tags specify ts in milliseconds
6163          new ArrayBackedTag(TagType.TTL_TAG_TYPE, Bytes.toBytes(5000L)) } )));
6164      // Add a cell that will expire after 10 seconds via family setting
6165      region.put(new Put(row).addColumn(fam1, q2, now, HConstants.EMPTY_BYTE_ARRAY));
6166      // Add a cell that will expire in 15 seconds via cell TTL
6167      region.put(new Put(row).add(new KeyValue(row, fam1, q3, now + 10000 - 1,
6168        HConstants.EMPTY_BYTE_ARRAY, new ArrayBackedTag[] {
6169          // TTL tags specify ts in milliseconds
6170          new ArrayBackedTag(TagType.TTL_TAG_TYPE, Bytes.toBytes(5000L)) } )));
6171      // Add a cell that will expire in 20 seconds via family setting
6172      region.put(new Put(row).addColumn(fam1, q4, now + 10000 - 1, HConstants.EMPTY_BYTE_ARRAY));
6173
6174      // Flush so we are sure store scanning gets this right
6175      region.flush(true);
6176
6177      // A query at time T+0 should return all cells
6178      Result r = region.get(new Get(row));
6179      assertNotNull(r.getValue(fam1, q1));
6180      assertNotNull(r.getValue(fam1, q2));
6181      assertNotNull(r.getValue(fam1, q3));
6182      assertNotNull(r.getValue(fam1, q4));
6183
6184      // Increment time to T+5 seconds
6185      edge.incrementTime(5000);
6186
6187      r = region.get(new Get(row));
6188      assertNull(r.getValue(fam1, q1));
6189      assertNotNull(r.getValue(fam1, q2));
6190      assertNotNull(r.getValue(fam1, q3));
6191      assertNotNull(r.getValue(fam1, q4));
6192
6193      // Increment time to T+10 seconds
6194      edge.incrementTime(5000);
6195
6196      r = region.get(new Get(row));
6197      assertNull(r.getValue(fam1, q1));
6198      assertNull(r.getValue(fam1, q2));
6199      assertNotNull(r.getValue(fam1, q3));
6200      assertNotNull(r.getValue(fam1, q4));
6201
6202      // Increment time to T+15 seconds
6203      edge.incrementTime(5000);
6204
6205      r = region.get(new Get(row));
6206      assertNull(r.getValue(fam1, q1));
6207      assertNull(r.getValue(fam1, q2));
6208      assertNull(r.getValue(fam1, q3));
6209      assertNotNull(r.getValue(fam1, q4));
6210
6211      // Increment time to T+20 seconds
6212      edge.incrementTime(10000);
6213
6214      r = region.get(new Get(row));
6215      assertNull(r.getValue(fam1, q1));
6216      assertNull(r.getValue(fam1, q2));
6217      assertNull(r.getValue(fam1, q3));
6218      assertNull(r.getValue(fam1, q4));
6219
6220      // Fun with disappearing increments
6221
6222      // Start at 1
6223      region.put(new Put(row).addColumn(fam1, q1, Bytes.toBytes(1L)));
6224      r = region.get(new Get(row));
6225      byte[] val = r.getValue(fam1, q1);
6226      assertNotNull(val);
6227      assertEquals(1L, Bytes.toLong(val));
6228
6229      // Increment with a TTL of 5 seconds
6230      Increment incr = new Increment(row).addColumn(fam1, q1, 1L);
6231      incr.setTTL(5000);
6232      region.increment(incr); // 2
6233
6234      // New value should be 2
6235      r = region.get(new Get(row));
6236      val = r.getValue(fam1, q1);
6237      assertNotNull(val);
6238      assertEquals(2L, Bytes.toLong(val));
6239
6240      // Increment time to T+25 seconds
6241      edge.incrementTime(5000);
6242
6243      // Value should be back to 1
6244      r = region.get(new Get(row));
6245      val = r.getValue(fam1, q1);
6246      assertNotNull(val);
6247      assertEquals(1L, Bytes.toLong(val));
6248
6249      // Increment time to T+30 seconds
6250      edge.incrementTime(5000);
6251
6252      // Original value written at T+20 should be gone now via family TTL
6253      r = region.get(new Get(row));
6254      assertNull(r.getValue(fam1, q1));
6255
6256    } finally {
6257      HBaseTestingUtility.closeRegionAndWAL(region);
6258    }
6259  }
6260
6261  @Test
6262  public void testIncrementTimestampsAreMonotonic() throws IOException {
6263    HRegion region = initHRegion(tableName, method, CONF, fam1);
6264    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
6265    EnvironmentEdgeManager.injectEdge(edge);
6266
6267    edge.setValue(10);
6268    Increment inc = new Increment(row);
6269    inc.setDurability(Durability.SKIP_WAL);
6270    inc.addColumn(fam1, qual1, 1L);
6271    region.increment(inc);
6272
6273    Result result = region.get(new Get(row));
6274    Cell c = result.getColumnLatestCell(fam1, qual1);
6275    assertNotNull(c);
6276    assertEquals(10L, c.getTimestamp());
6277
6278    edge.setValue(1); // clock goes back
6279    region.increment(inc);
6280    result = region.get(new Get(row));
6281    c = result.getColumnLatestCell(fam1, qual1);
6282    assertEquals(11L, c.getTimestamp());
6283    assertEquals(2L, Bytes.toLong(c.getValueArray(), c.getValueOffset(), c.getValueLength()));
6284  }
6285
6286  @Test
6287  public void testAppendTimestampsAreMonotonic() throws IOException {
6288    HRegion region = initHRegion(tableName, method, CONF, fam1);
6289    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
6290    EnvironmentEdgeManager.injectEdge(edge);
6291
6292    edge.setValue(10);
6293    Append a = new Append(row);
6294    a.setDurability(Durability.SKIP_WAL);
6295    a.addColumn(fam1, qual1, qual1);
6296    region.append(a);
6297
6298    Result result = region.get(new Get(row));
6299    Cell c = result.getColumnLatestCell(fam1, qual1);
6300    assertNotNull(c);
6301    assertEquals(10L, c.getTimestamp());
6302
6303    edge.setValue(1); // clock goes back
6304    region.append(a);
6305    result = region.get(new Get(row));
6306    c = result.getColumnLatestCell(fam1, qual1);
6307    assertEquals(11L, c.getTimestamp());
6308
6309    byte[] expected = new byte[qual1.length*2];
6310    System.arraycopy(qual1, 0, expected, 0, qual1.length);
6311    System.arraycopy(qual1, 0, expected, qual1.length, qual1.length);
6312
6313    assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(),
6314      expected, 0, expected.length));
6315  }
6316
6317  @Test
6318  public void testCheckAndMutateTimestampsAreMonotonic() throws IOException {
6319    HRegion region = initHRegion(tableName, method, CONF, fam1);
6320    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
6321    EnvironmentEdgeManager.injectEdge(edge);
6322
6323    edge.setValue(10);
6324    Put p = new Put(row);
6325    p.setDurability(Durability.SKIP_WAL);
6326    p.addColumn(fam1, qual1, qual1);
6327    region.put(p);
6328
6329    Result result = region.get(new Get(row));
6330    Cell c = result.getColumnLatestCell(fam1, qual1);
6331    assertNotNull(c);
6332    assertEquals(10L, c.getTimestamp());
6333
6334    edge.setValue(1); // clock goes back
6335    p = new Put(row);
6336    p.setDurability(Durability.SKIP_WAL);
6337    p.addColumn(fam1, qual1, qual2);
6338    region.checkAndMutate(row, fam1, qual1, CompareOperator.EQUAL, new BinaryComparator(qual1), p);
6339    result = region.get(new Get(row));
6340    c = result.getColumnLatestCell(fam1, qual1);
6341    assertEquals(10L, c.getTimestamp());
6342
6343    assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(),
6344      qual2, 0, qual2.length));
6345  }
6346
6347  @Test
6348  public void testBatchMutateWithWrongRegionException() throws Exception {
6349    final byte[] a = Bytes.toBytes("a");
6350    final byte[] b = Bytes.toBytes("b");
6351    final byte[] c = Bytes.toBytes("c"); // exclusive
6352
6353    int prevLockTimeout = CONF.getInt("hbase.rowlock.wait.duration", 30000);
6354    CONF.setInt("hbase.rowlock.wait.duration", 1000);
6355    final HRegion region = initHRegion(tableName, a, c, method, CONF, false, fam1);
6356
6357    Mutation[] mutations = new Mutation[] {
6358        new Put(a)
6359            .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
6360              .setRow(a)
6361              .setFamily(fam1)
6362              .setTimestamp(HConstants.LATEST_TIMESTAMP)
6363              .setType(Cell.Type.Put)
6364              .build()),
6365        // this is outside the region boundary
6366        new Put(c).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
6367              .setRow(c)
6368              .setFamily(fam1)
6369              .setTimestamp(HConstants.LATEST_TIMESTAMP)
6370              .setType(Type.Put)
6371              .build()),
6372        new Put(b).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
6373              .setRow(b)
6374              .setFamily(fam1)
6375              .setTimestamp(HConstants.LATEST_TIMESTAMP)
6376              .setType(Cell.Type.Put)
6377              .build())
6378    };
6379
6380    OperationStatus[] status = region.batchMutate(mutations);
6381    assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode());
6382    assertEquals(OperationStatusCode.SANITY_CHECK_FAILURE, status[1].getOperationStatusCode());
6383    assertEquals(OperationStatusCode.SUCCESS, status[2].getOperationStatusCode());
6384
6385
6386    // test with a row lock held for a long time
6387    final CountDownLatch obtainedRowLock = new CountDownLatch(1);
6388    ExecutorService exec = Executors.newFixedThreadPool(2);
6389    Future<Void> f1 = exec.submit(new Callable<Void>() {
6390      @Override
6391      public Void call() throws Exception {
6392        LOG.info("Acquiring row lock");
6393        RowLock rl = region.getRowLock(b);
6394        obtainedRowLock.countDown();
6395        LOG.info("Waiting for 5 seconds before releasing lock");
6396        Threads.sleep(5000);
6397        LOG.info("Releasing row lock");
6398        rl.release();
6399        return null;
6400      }
6401    });
6402    obtainedRowLock.await(30, TimeUnit.SECONDS);
6403
6404    Future<Void> f2 = exec.submit(new Callable<Void>() {
6405      @Override
6406      public Void call() throws Exception {
6407        Mutation[] mutations = new Mutation[] {
6408            new Put(a).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
6409                .setRow(a)
6410                .setFamily(fam1)
6411                .setTimestamp(HConstants.LATEST_TIMESTAMP)
6412                .setType(Cell.Type.Put)
6413                .build()),
6414            new Put(b).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
6415                .setRow(b)
6416                .setFamily(fam1)
6417                .setTimestamp(HConstants.LATEST_TIMESTAMP)
6418                .setType(Cell.Type.Put)
6419                .build()),
6420        };
6421
6422        // this will wait for the row lock, and it will eventually succeed
6423        OperationStatus[] status = region.batchMutate(mutations);
6424        assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode());
6425        assertEquals(OperationStatusCode.SUCCESS, status[1].getOperationStatusCode());
6426        return null;
6427      }
6428    });
6429
6430    f1.get();
6431    f2.get();
6432
6433    CONF.setInt("hbase.rowlock.wait.duration", prevLockTimeout);
6434  }
6435
6436  @Test
6437  public void testCheckAndRowMutateTimestampsAreMonotonic() throws IOException {
6438    HRegion region = initHRegion(tableName, method, CONF, fam1);
6439    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
6440    EnvironmentEdgeManager.injectEdge(edge);
6441
6442    edge.setValue(10);
6443    Put p = new Put(row);
6444    p.setDurability(Durability.SKIP_WAL);
6445    p.addColumn(fam1, qual1, qual1);
6446    region.put(p);
6447
6448    Result result = region.get(new Get(row));
6449    Cell c = result.getColumnLatestCell(fam1, qual1);
6450    assertNotNull(c);
6451    assertEquals(10L, c.getTimestamp());
6452
6453    edge.setValue(1); // clock goes back
6454    p = new Put(row);
6455    p.setDurability(Durability.SKIP_WAL);
6456    p.addColumn(fam1, qual1, qual2);
6457    RowMutations rm = new RowMutations(row);
6458    rm.add(p);
6459    assertTrue(region.checkAndRowMutate(row, fam1, qual1, CompareOperator.EQUAL,
6460        new BinaryComparator(qual1), rm));
6461    result = region.get(new Get(row));
6462    c = result.getColumnLatestCell(fam1, qual1);
6463    assertEquals(10L, c.getTimestamp());
6464    LOG.info("c value " +
6465      Bytes.toStringBinary(c.getValueArray(), c.getValueOffset(), c.getValueLength()));
6466
6467    assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(),
6468      qual2, 0, qual2.length));
6469  }
6470
6471  HRegion initHRegion(TableName tableName, String callingMethod,
6472      byte[]... families) throws IOException {
6473    return initHRegion(tableName, callingMethod, HBaseConfiguration.create(),
6474        families);
6475  }
6476
6477  /**
6478   * HBASE-16429 Make sure no stuck if roll writer when ring buffer is filled with appends
6479   * @throws IOException if IO error occurred during test
6480   */
6481  @Test
6482  public void testWritesWhileRollWriter() throws IOException {
6483    int testCount = 10;
6484    int numRows = 1024;
6485    int numFamilies = 2;
6486    int numQualifiers = 2;
6487    final byte[][] families = new byte[numFamilies][];
6488    for (int i = 0; i < numFamilies; i++) {
6489      families[i] = Bytes.toBytes("family" + i);
6490    }
6491    final byte[][] qualifiers = new byte[numQualifiers][];
6492    for (int i = 0; i < numQualifiers; i++) {
6493      qualifiers[i] = Bytes.toBytes("qual" + i);
6494    }
6495
6496    CONF.setInt("hbase.regionserver.wal.disruptor.event.count", 2);
6497    this.region = initHRegion(tableName, method, CONF, families);
6498    try {
6499      List<Thread> threads = new ArrayList<>();
6500      for (int i = 0; i < numRows; i++) {
6501        final int count = i;
6502        Thread t = new Thread(new Runnable() {
6503
6504          @Override
6505          public void run() {
6506            byte[] row = Bytes.toBytes("row" + count);
6507            Put put = new Put(row);
6508            put.setDurability(Durability.SYNC_WAL);
6509            byte[] value = Bytes.toBytes(String.valueOf(count));
6510            for (byte[] family : families) {
6511              for (byte[] qualifier : qualifiers) {
6512                put.addColumn(family, qualifier, count, value);
6513              }
6514            }
6515            try {
6516              region.put(put);
6517            } catch (IOException e) {
6518              throw new RuntimeException(e);
6519            }
6520          }
6521        });
6522        threads.add(t);
6523      }
6524      for (Thread t : threads) {
6525        t.start();
6526      }
6527
6528      for (int i = 0; i < testCount; i++) {
6529        region.getWAL().rollWriter();
6530        Thread.yield();
6531      }
6532    } finally {
6533      try {
6534        HBaseTestingUtility.closeRegionAndWAL(this.region);
6535        CONF.setInt("hbase.regionserver.wal.disruptor.event.count", 16 * 1024);
6536      } catch (DroppedSnapshotException dse) {
6537        // We could get this on way out because we interrupt the background flusher and it could
6538        // fail anywhere causing a DSE over in the background flusher... only it is not properly
6539        // dealt with so could still be memory hanging out when we get to here -- memory we can't
6540        // flush because the accounting is 'off' since original DSE.
6541      }
6542      this.region = null;
6543    }
6544  }
6545
6546  @Test
6547  public void testMutateRow_WriteRequestCount() throws Exception {
6548    byte[] row1 = Bytes.toBytes("row1");
6549    byte[] fam1 = Bytes.toBytes("fam1");
6550    byte[] qf1 = Bytes.toBytes("qualifier");
6551    byte[] val1 = Bytes.toBytes("value1");
6552
6553    RowMutations rm = new RowMutations(row1);
6554    Put put = new Put(row1);
6555    put.addColumn(fam1, qf1, val1);
6556    rm.add(put);
6557
6558    this.region = initHRegion(tableName, method, CONF, fam1);
6559    try {
6560      long wrcBeforeMutate = this.region.writeRequestsCount.longValue();
6561      this.region.mutateRow(rm);
6562      long wrcAfterMutate = this.region.writeRequestsCount.longValue();
6563      Assert.assertEquals(wrcBeforeMutate + rm.getMutations().size(), wrcAfterMutate);
6564    } finally {
6565      HBaseTestingUtility.closeRegionAndWAL(this.region);
6566      this.region = null;
6567    }
6568  }
6569
6570  @Test
6571  public void testBulkLoadReplicationEnabled() throws IOException {
6572    TEST_UTIL.getConfiguration().setBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY, true);
6573    final ServerName serverName = ServerName.valueOf(name.getMethodName(), 100, 42);
6574    final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName));
6575
6576    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
6577    htd.addFamily(new HColumnDescriptor(fam1));
6578    HRegionInfo hri = new HRegionInfo(htd.getTableName(),
6579        HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY);
6580    region = HRegion.openHRegion(hri, htd, rss.getWAL(hri), TEST_UTIL.getConfiguration(),
6581        rss, null);
6582
6583    assertTrue(region.conf.getBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY, false));
6584    String plugins = region.conf.get(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, "");
6585    String replicationCoprocessorClass = ReplicationObserver.class.getCanonicalName();
6586    assertTrue(plugins.contains(replicationCoprocessorClass));
6587    assertTrue(region.getCoprocessorHost().
6588        getCoprocessors().contains(ReplicationObserver.class.getSimpleName()));
6589
6590    region.close();
6591  }
6592
6593  /**
6594   * The same as HRegion class, the only difference is that instantiateHStore will
6595   * create a different HStore - HStoreForTesting. [HBASE-8518]
6596   */
6597  public static class HRegionForTesting extends HRegion {
6598
6599    public HRegionForTesting(final Path tableDir, final WAL wal, final FileSystem fs,
6600                             final Configuration confParam, final RegionInfo regionInfo,
6601                             final TableDescriptor htd, final RegionServerServices rsServices) {
6602      this(new HRegionFileSystem(confParam, fs, tableDir, regionInfo),
6603          wal, confParam, htd, rsServices);
6604    }
6605
6606    public HRegionForTesting(HRegionFileSystem fs, WAL wal,
6607                             Configuration confParam, TableDescriptor htd,
6608                             RegionServerServices rsServices) {
6609      super(fs, wal, confParam, htd, rsServices);
6610    }
6611
6612    /**
6613     * Create HStore instance.
6614     * @return If Mob is enabled, return HMobStore, otherwise return HStoreForTesting.
6615     */
6616    @Override
6617    protected HStore instantiateHStore(final ColumnFamilyDescriptor family) throws IOException {
6618      if (family.isMobEnabled()) {
6619        if (HFile.getFormatVersion(this.conf) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) {
6620          throw new IOException("A minimum HFile version of "
6621              + HFile.MIN_FORMAT_VERSION_WITH_TAGS
6622              + " is required for MOB feature. Consider setting " + HFile.FORMAT_VERSION_KEY
6623              + " accordingly.");
6624        }
6625        return new HMobStore(this, family, this.conf);
6626      }
6627      return new HStoreForTesting(this, family, this.conf);
6628    }
6629  }
6630
6631  /**
6632   * HStoreForTesting is merely the same as HStore, the difference is in the doCompaction method
6633   * of HStoreForTesting there is a checkpoint "hbase.hstore.compaction.complete" which
6634   * doesn't let hstore compaction complete. In the former edition, this config is set in
6635   * HStore class inside compact method, though this is just for testing, otherwise it
6636   * doesn't do any help. In HBASE-8518, we try to get rid of all "hbase.hstore.compaction.complete"
6637   * config (except for testing code).
6638   */
6639  public static class HStoreForTesting extends HStore {
6640
6641    protected HStoreForTesting(final HRegion region,
6642        final ColumnFamilyDescriptor family,
6643        final Configuration confParam) throws IOException {
6644      super(region, family, confParam);
6645    }
6646
6647    @Override
6648    protected List<HStoreFile> doCompaction(CompactionRequestImpl cr,
6649        Collection<HStoreFile> filesToCompact, User user, long compactionStartTime,
6650        List<Path> newFiles) throws IOException {
6651      // let compaction incomplete.
6652      if (!this.conf.getBoolean("hbase.hstore.compaction.complete", true)) {
6653        LOG.warn("hbase.hstore.compaction.complete is set to false");
6654        List<HStoreFile> sfs = new ArrayList<>(newFiles.size());
6655        final boolean evictOnClose =
6656            cacheConf != null? cacheConf.shouldEvictOnClose(): true;
6657        for (Path newFile : newFiles) {
6658          // Create storefile around what we wrote with a reader on it.
6659          HStoreFile sf = createStoreFileAndReader(newFile);
6660          sf.closeStoreFile(evictOnClose);
6661          sfs.add(sf);
6662        }
6663        return sfs;
6664      }
6665      return super.doCompaction(cr, filesToCompact, user, compactionStartTime, newFiles);
6666    }
6667  }
6668}