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