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