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