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