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  @Test
4450  public void testScannerOperationId() throws IOException {
4451    region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES);
4452    Scan scan = new Scan();
4453    RegionScanner scanner = region.getScanner(scan);
4454    assertNull(scanner.getOperationId());
4455    scanner.close();
4456
4457    String operationId = "test_operation_id_0101";
4458    scan = new Scan().setId(operationId);
4459    scanner = region.getScanner(scan);
4460    assertEquals(operationId, scanner.getOperationId());
4461    scanner.close();
4462
4463    HBaseTestingUtil.closeRegionAndWAL(this.region);
4464  }
4465
4466  /**
4467   * Write an HFile block full with Cells whose qualifier that are identical between
4468   * 0 and Short.MAX_VALUE. See HBASE-13329.
4469   * @throws Exception
4470   */
4471  @Test
4472  public void testLongQualifier() throws Exception {
4473    byte[] family = Bytes.toBytes("family");
4474    this.region = initHRegion(tableName, method, CONF, family);
4475    byte[] q = new byte[Short.MAX_VALUE+2];
4476    Arrays.fill(q, 0, q.length-1, (byte)42);
4477    for (byte i=0; i<10; i++) {
4478      Put p = new Put(Bytes.toBytes("row"));
4479      // qualifiers that differ past Short.MAX_VALUE
4480      q[q.length-1]=i;
4481      p.addColumn(family, q, q);
4482      region.put(p);
4483    }
4484    region.flush(false);
4485  }
4486
4487  /**
4488   * Flushes the cache in a thread while scanning. The tests verify that the
4489   * scan is coherent - e.g. the returned results are always of the same or
4490   * later update as the previous results.
4491   *
4492   * @throws IOException
4493   *           scan / compact
4494   * @throws InterruptedException
4495   *           thread join
4496   */
4497  @Test
4498  public void testFlushCacheWhileScanning() throws IOException, InterruptedException {
4499    byte[] family = Bytes.toBytes("family");
4500    int numRows = 1000;
4501    int flushAndScanInterval = 10;
4502    int compactInterval = 10 * flushAndScanInterval;
4503
4504    this.region = initHRegion(tableName, method, CONF, family);
4505    FlushThread flushThread = new FlushThread();
4506    try {
4507      flushThread.start();
4508
4509      Scan scan = new Scan();
4510      scan.addFamily(family);
4511      scan.setFilter(new SingleColumnValueFilter(family, qual1, CompareOperator.EQUAL,
4512          new BinaryComparator(Bytes.toBytes(5L))));
4513
4514      int expectedCount = 0;
4515      List<Cell> res = new ArrayList<>();
4516
4517      boolean toggle = true;
4518      for (long i = 0; i < numRows; i++) {
4519        Put put = new Put(Bytes.toBytes(i));
4520        put.setDurability(Durability.SKIP_WAL);
4521        put.addColumn(family, qual1, Bytes.toBytes(i % 10));
4522        region.put(put);
4523
4524        if (i != 0 && i % compactInterval == 0) {
4525          LOG.debug("iteration = " + i+ " ts=" + EnvironmentEdgeManager.currentTime());
4526          region.compact(true);
4527        }
4528
4529        if (i % 10 == 5L) {
4530          expectedCount++;
4531        }
4532
4533        if (i != 0 && i % flushAndScanInterval == 0) {
4534          res.clear();
4535          InternalScanner scanner = region.getScanner(scan);
4536          if (toggle) {
4537            flushThread.flush();
4538          }
4539          while (scanner.next(res))
4540            ;
4541          if (!toggle) {
4542            flushThread.flush();
4543          }
4544          assertEquals("toggle="+toggle+"i=" + i + " ts=" + EnvironmentEdgeManager.currentTime(),
4545              expectedCount, res.size());
4546          toggle = !toggle;
4547        }
4548      }
4549
4550    } finally {
4551      try {
4552        flushThread.done();
4553        flushThread.join();
4554        flushThread.checkNoError();
4555      } catch (InterruptedException ie) {
4556        LOG.warn("Caught exception when joining with flushThread", ie);
4557      }
4558      HBaseTestingUtil.closeRegionAndWAL(this.region);
4559      this.region = null;
4560    }
4561  }
4562
4563  protected class FlushThread extends Thread {
4564    private volatile boolean done;
4565    private Throwable error = null;
4566
4567    FlushThread() {
4568      super("FlushThread");
4569    }
4570
4571    public void done() {
4572      done = true;
4573      synchronized (this) {
4574        interrupt();
4575      }
4576    }
4577
4578    public void checkNoError() {
4579      if (error != null) {
4580        assertNull(error);
4581      }
4582    }
4583
4584    @Override
4585    public void run() {
4586      done = false;
4587      while (!done) {
4588        synchronized (this) {
4589          try {
4590            wait();
4591          } catch (InterruptedException ignored) {
4592            if (done) {
4593              break;
4594            }
4595          }
4596        }
4597        try {
4598          region.flush(true);
4599        } catch (IOException e) {
4600          if (!done) {
4601            LOG.error("Error while flushing cache", e);
4602            error = e;
4603          }
4604          break;
4605        } catch (Throwable t) {
4606          LOG.error("Uncaught exception", t);
4607          throw t;
4608        }
4609      }
4610    }
4611
4612    public void flush() {
4613      synchronized (this) {
4614        notify();
4615      }
4616    }
4617  }
4618
4619  /**
4620   * So can be overridden in subclasses.
4621   */
4622  int getNumQualifiersForTestWritesWhileScanning() {
4623    return 100;
4624  }
4625
4626  /**
4627   * So can be overridden in subclasses.
4628   */
4629  int getTestCountForTestWritesWhileScanning() {
4630    return 100;
4631  }
4632
4633  /**
4634   * Writes very wide records and scans for the latest every time.. Flushes and
4635   * compacts the region every now and then to keep things realistic.
4636   *
4637   * @throws IOException
4638   *           by flush / scan / compaction
4639   * @throws InterruptedException
4640   *           when joining threads
4641   */
4642  @Test
4643  public void testWritesWhileScanning() throws IOException, InterruptedException {
4644    int testCount = getTestCountForTestWritesWhileScanning();
4645    int numRows = 1;
4646    int numFamilies = 10;
4647    int numQualifiers = getNumQualifiersForTestWritesWhileScanning();
4648    int flushInterval = 7;
4649    int compactInterval = 5 * flushInterval;
4650    byte[][] families = new byte[numFamilies][];
4651    for (int i = 0; i < numFamilies; i++) {
4652      families[i] = Bytes.toBytes("family" + i);
4653    }
4654    byte[][] qualifiers = new byte[numQualifiers][];
4655    for (int i = 0; i < numQualifiers; i++) {
4656      qualifiers[i] = Bytes.toBytes("qual" + i);
4657    }
4658
4659    this.region = initHRegion(tableName, method, CONF, families);
4660    FlushThread flushThread = new FlushThread();
4661    PutThread putThread = new PutThread(numRows, families, qualifiers);
4662    try {
4663      putThread.start();
4664      putThread.waitForFirstPut();
4665
4666      flushThread.start();
4667
4668      Scan scan = new Scan().withStartRow(Bytes.toBytes("row0"))
4669        .withStopRow(Bytes.toBytes("row1"));
4670
4671      int expectedCount = numFamilies * numQualifiers;
4672      List<Cell> res = new ArrayList<>();
4673
4674      long prevTimestamp = 0L;
4675      for (int i = 0; i < testCount; i++) {
4676
4677        if (i != 0 && i % compactInterval == 0) {
4678          region.compact(true);
4679          for (HStore store : region.getStores()) {
4680            store.closeAndArchiveCompactedFiles();
4681          }
4682        }
4683
4684        if (i != 0 && i % flushInterval == 0) {
4685          flushThread.flush();
4686        }
4687
4688        boolean previousEmpty = res.isEmpty();
4689        res.clear();
4690        try (InternalScanner scanner = region.getScanner(scan)) {
4691          boolean moreRows;
4692          do {
4693            moreRows = scanner.next(res);
4694          } while (moreRows);
4695        }
4696        if (!res.isEmpty() || !previousEmpty || i > compactInterval) {
4697          assertEquals("i=" + i, expectedCount, res.size());
4698          long timestamp = res.get(0).getTimestamp();
4699          assertTrue("Timestamps were broke: " + timestamp + " prev: " + prevTimestamp,
4700              timestamp >= prevTimestamp);
4701          prevTimestamp = timestamp;
4702        }
4703      }
4704
4705      putThread.done();
4706
4707      region.flush(true);
4708
4709    } finally {
4710      try {
4711        flushThread.done();
4712        flushThread.join();
4713        flushThread.checkNoError();
4714
4715        putThread.join();
4716        putThread.checkNoError();
4717      } catch (InterruptedException ie) {
4718        LOG.warn("Caught exception when joining with flushThread", ie);
4719      }
4720
4721      try {
4722        HBaseTestingUtil.closeRegionAndWAL(this.region);
4723      } catch (DroppedSnapshotException dse) {
4724        // We could get this on way out because we interrupt the background flusher and it could
4725        // fail anywhere causing a DSE over in the background flusher... only it is not properly
4726        // dealt with so could still be memory hanging out when we get to here -- memory we can't
4727        // flush because the accounting is 'off' since original DSE.
4728      }
4729      this.region = null;
4730    }
4731  }
4732
4733  protected class PutThread extends Thread {
4734    private volatile boolean done;
4735    private volatile int numPutsFinished = 0;
4736
4737    private Throwable error = null;
4738    private int numRows;
4739    private byte[][] families;
4740    private byte[][] qualifiers;
4741
4742    private PutThread(int numRows, byte[][] families, byte[][] qualifiers) {
4743      super("PutThread");
4744      this.numRows = numRows;
4745      this.families = families;
4746      this.qualifiers = qualifiers;
4747    }
4748
4749    /**
4750     * Block calling thread until this instance of PutThread has put at least one row.
4751     */
4752    public void waitForFirstPut() throws InterruptedException {
4753      // wait until put thread actually puts some data
4754      while (isAlive() && numPutsFinished == 0) {
4755        checkNoError();
4756        Thread.sleep(50);
4757      }
4758    }
4759
4760    public void done() {
4761      done = true;
4762      synchronized (this) {
4763        interrupt();
4764      }
4765    }
4766
4767    public void checkNoError() {
4768      if (error != null) {
4769        assertNull(error);
4770      }
4771    }
4772
4773    @Override
4774    public void run() {
4775      done = false;
4776      while (!done) {
4777        try {
4778          for (int r = 0; r < numRows; r++) {
4779            byte[] row = Bytes.toBytes("row" + r);
4780            Put put = new Put(row);
4781            put.setDurability(Durability.SKIP_WAL);
4782            byte[] value = Bytes.toBytes(String.valueOf(numPutsFinished));
4783            for (byte[] family : families) {
4784              for (byte[] qualifier : qualifiers) {
4785                put.addColumn(family, qualifier, numPutsFinished, value);
4786              }
4787            }
4788            region.put(put);
4789            numPutsFinished++;
4790            if (numPutsFinished > 0 && numPutsFinished % 47 == 0) {
4791              LOG.debug("put iteration = {}", numPutsFinished);
4792              Delete delete = new Delete(row, (long) numPutsFinished - 30);
4793              region.delete(delete);
4794            }
4795            numPutsFinished++;
4796          }
4797        } catch (InterruptedIOException e) {
4798          // This is fine. It means we are done, or didn't get the lock on time
4799          LOG.info("Interrupted", e);
4800        } catch (IOException e) {
4801          LOG.error("Error while putting records", e);
4802          error = e;
4803          break;
4804        }
4805      }
4806
4807    }
4808
4809  }
4810
4811  /**
4812   * Writes very wide records and gets the latest row every time.. Flushes and
4813   * compacts the region aggressivly to catch issues.
4814   *
4815   * @throws IOException
4816   *           by flush / scan / compaction
4817   * @throws InterruptedException
4818   *           when joining threads
4819   */
4820  @Test
4821  public void testWritesWhileGetting() throws Exception {
4822    int testCount = 50;
4823    int numRows = 1;
4824    int numFamilies = 10;
4825    int numQualifiers = 100;
4826    int compactInterval = 100;
4827    byte[][] families = new byte[numFamilies][];
4828    for (int i = 0; i < numFamilies; i++) {
4829      families[i] = Bytes.toBytes("family" + i);
4830    }
4831    byte[][] qualifiers = new byte[numQualifiers][];
4832    for (int i = 0; i < numQualifiers; i++) {
4833      qualifiers[i] = Bytes.toBytes("qual" + i);
4834    }
4835
4836
4837    // This test flushes constantly and can cause many files to be created,
4838    // possibly
4839    // extending over the ulimit. Make sure compactions are aggressive in
4840    // reducing
4841    // the number of HFiles created.
4842    Configuration conf = HBaseConfiguration.create(CONF);
4843    conf.setInt("hbase.hstore.compaction.min", 1);
4844    conf.setInt("hbase.hstore.compaction.max", 1000);
4845    this.region = initHRegion(tableName, method, conf, families);
4846    PutThread putThread = null;
4847    MultithreadedTestUtil.TestContext ctx = new MultithreadedTestUtil.TestContext(conf);
4848    try {
4849      putThread = new PutThread(numRows, families, qualifiers);
4850      putThread.start();
4851      putThread.waitForFirstPut();
4852
4853      // Add a thread that flushes as fast as possible
4854      ctx.addThread(new RepeatingTestThread(ctx) {
4855
4856        @Override
4857        public void doAnAction() throws Exception {
4858          region.flush(true);
4859          // Compact regularly to avoid creating too many files and exceeding
4860          // the ulimit.
4861          region.compact(false);
4862          for (HStore store : region.getStores()) {
4863            store.closeAndArchiveCompactedFiles();
4864          }
4865        }
4866      });
4867      ctx.startThreads();
4868
4869      Get get = new Get(Bytes.toBytes("row0"));
4870      Result result = null;
4871
4872      int expectedCount = numFamilies * numQualifiers;
4873
4874      long prevTimestamp = 0L;
4875      for (int i = 0; i < testCount; i++) {
4876        LOG.info("testWritesWhileGetting verify turn " + i);
4877        boolean previousEmpty = result == null || result.isEmpty();
4878        result = region.get(get);
4879        if (!result.isEmpty() || !previousEmpty || i > compactInterval) {
4880          assertEquals("i=" + i, expectedCount, result.size());
4881          // TODO this was removed, now what dangit?!
4882          // search looking for the qualifier in question?
4883          long timestamp = 0;
4884          for (Cell kv : result.rawCells()) {
4885            if (CellUtil.matchingFamily(kv, families[0])
4886                && CellUtil.matchingQualifier(kv, qualifiers[0])) {
4887              timestamp = kv.getTimestamp();
4888            }
4889          }
4890          assertTrue(timestamp >= prevTimestamp);
4891          prevTimestamp = timestamp;
4892          Cell previousKV = null;
4893
4894          for (Cell kv : result.rawCells()) {
4895            byte[] thisValue = CellUtil.cloneValue(kv);
4896            if (previousKV != null) {
4897              if (Bytes.compareTo(CellUtil.cloneValue(previousKV), thisValue) != 0) {
4898                LOG.warn("These two KV should have the same value." + " Previous KV:" + previousKV
4899                    + "(memStoreTS:" + previousKV.getSequenceId() + ")" + ", New KV: " + kv
4900                    + "(memStoreTS:" + kv.getSequenceId() + ")");
4901                assertEquals(0, Bytes.compareTo(CellUtil.cloneValue(previousKV), thisValue));
4902              }
4903            }
4904            previousKV = kv;
4905          }
4906        }
4907      }
4908    } finally {
4909      if (putThread != null)
4910        putThread.done();
4911
4912      region.flush(true);
4913
4914      if (putThread != null) {
4915        putThread.join();
4916        putThread.checkNoError();
4917      }
4918
4919      ctx.stop();
4920      HBaseTestingUtil.closeRegionAndWAL(this.region);
4921      this.region = null;
4922    }
4923  }
4924
4925  @Test
4926  public void testHolesInMeta() throws Exception {
4927    byte[] family = Bytes.toBytes("family");
4928    this.region = initHRegion(tableName, Bytes.toBytes("x"), Bytes.toBytes("z"), method, CONF,
4929        false, family);
4930    byte[] rowNotServed = Bytes.toBytes("a");
4931    Get g = new Get(rowNotServed);
4932    try {
4933      region.get(g);
4934      fail();
4935    } catch (WrongRegionException x) {
4936      // OK
4937    }
4938    byte[] row = Bytes.toBytes("y");
4939    g = new Get(row);
4940    region.get(g);
4941  }
4942
4943  @Test
4944  public void testIndexesScanWithOneDeletedRow() throws IOException {
4945    byte[] family = Bytes.toBytes("family");
4946
4947    // Setting up region
4948    this.region = initHRegion(tableName, method, CONF, family);
4949    Put put = new Put(Bytes.toBytes(1L));
4950    put.addColumn(family, qual1, 1L, Bytes.toBytes(1L));
4951    region.put(put);
4952
4953    region.flush(true);
4954
4955    Delete delete = new Delete(Bytes.toBytes(1L), 1L);
4956    region.delete(delete);
4957
4958    put = new Put(Bytes.toBytes(2L));
4959    put.addColumn(family, qual1, 2L, Bytes.toBytes(2L));
4960    region.put(put);
4961
4962    Scan idxScan = new Scan();
4963    idxScan.addFamily(family);
4964    idxScan.setFilter(new FilterList(FilterList.Operator.MUST_PASS_ALL, Arrays.<Filter> asList(
4965        new SingleColumnValueFilter(family, qual1, CompareOperator.GREATER_OR_EQUAL,
4966            new BinaryComparator(Bytes.toBytes(0L))), new SingleColumnValueFilter(family, qual1,
4967                    CompareOperator.LESS_OR_EQUAL, new BinaryComparator(Bytes.toBytes(3L))))));
4968    InternalScanner scanner = region.getScanner(idxScan);
4969    List<Cell> res = new ArrayList<>();
4970
4971    while (scanner.next(res)) {
4972      // Ignore res value.
4973    }
4974    assertEquals(1L, res.size());
4975  }
4976
4977  // ////////////////////////////////////////////////////////////////////////////
4978  // Bloom filter test
4979  // ////////////////////////////////////////////////////////////////////////////
4980  @Test
4981  public void testBloomFilterSize() throws IOException {
4982    byte[] fam1 = Bytes.toBytes("fam1");
4983    byte[] qf1 = Bytes.toBytes("col");
4984    byte[] val1 = Bytes.toBytes("value1");
4985    // Create Table
4986    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
4987      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(fam1)
4988        .setMaxVersions(Integer.MAX_VALUE).setBloomFilterType(BloomType.ROWCOL).build())
4989      .build();
4990    RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build();
4991    this.region = TEST_UTIL.createLocalHRegion(info, tableDescriptor);
4992    int num_unique_rows = 10;
4993    int duplicate_multiplier = 2;
4994    int num_storefiles = 4;
4995
4996    int version = 0;
4997    for (int f = 0; f < num_storefiles; f++) {
4998      for (int i = 0; i < duplicate_multiplier; i++) {
4999        for (int j = 0; j < num_unique_rows; j++) {
5000          Put put = new Put(Bytes.toBytes("row" + j));
5001          put.setDurability(Durability.SKIP_WAL);
5002          long ts = version++;
5003          put.addColumn(fam1, qf1, ts, val1);
5004          region.put(put);
5005        }
5006      }
5007      region.flush(true);
5008    }
5009    // before compaction
5010    HStore store = region.getStore(fam1);
5011    Collection<HStoreFile> storeFiles = store.getStorefiles();
5012    for (HStoreFile storefile : storeFiles) {
5013      StoreFileReader reader = storefile.getReader();
5014      reader.loadFileInfo();
5015      reader.loadBloomfilter();
5016      assertEquals(num_unique_rows * duplicate_multiplier, reader.getEntries());
5017      assertEquals(num_unique_rows, reader.getFilterEntries());
5018    }
5019
5020    region.compact(true);
5021
5022    // after compaction
5023    storeFiles = store.getStorefiles();
5024    for (HStoreFile storefile : storeFiles) {
5025      StoreFileReader reader = storefile.getReader();
5026      reader.loadFileInfo();
5027      reader.loadBloomfilter();
5028      assertEquals(num_unique_rows * duplicate_multiplier * num_storefiles, reader.getEntries());
5029      assertEquals(num_unique_rows, reader.getFilterEntries());
5030    }
5031  }
5032
5033  @Test
5034  public void testAllColumnsWithBloomFilter() throws IOException {
5035    byte[] TABLE = Bytes.toBytes(name.getMethodName());
5036    byte[] FAMILY = Bytes.toBytes("family");
5037
5038    // Create table
5039    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf(TABLE))
5040      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(FAMILY)
5041        .setMaxVersions(Integer.MAX_VALUE).setBloomFilterType(BloomType.ROWCOL).build())
5042      .build();
5043    RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build();
5044    this.region = TEST_UTIL.createLocalHRegion(info, tableDescriptor);
5045    // For row:0, col:0: insert versions 1 through 5.
5046    byte[] row = Bytes.toBytes("row:" + 0);
5047    byte[] column = Bytes.toBytes("column:" + 0);
5048    Put put = new Put(row);
5049    put.setDurability(Durability.SKIP_WAL);
5050    for (long idx = 1; idx <= 4; idx++) {
5051      put.addColumn(FAMILY, column, idx, Bytes.toBytes("value-version-" + idx));
5052    }
5053    region.put(put);
5054
5055    // Flush
5056    region.flush(true);
5057
5058    // Get rows
5059    Get get = new Get(row);
5060    get.readAllVersions();
5061    Cell[] kvs = region.get(get).rawCells();
5062
5063    // Check if rows are correct
5064    assertEquals(4, kvs.length);
5065    checkOneCell(kvs[0], FAMILY, 0, 0, 4);
5066    checkOneCell(kvs[1], FAMILY, 0, 0, 3);
5067    checkOneCell(kvs[2], FAMILY, 0, 0, 2);
5068    checkOneCell(kvs[3], FAMILY, 0, 0, 1);
5069  }
5070
5071  /**
5072   * Testcase to cover bug-fix for HBASE-2823 Ensures correct delete when
5073   * issuing delete row on columns with bloom filter set to row+col
5074   * (BloomType.ROWCOL)
5075   */
5076  @Test
5077  public void testDeleteRowWithBloomFilter() throws IOException {
5078    byte[] familyName = Bytes.toBytes("familyName");
5079
5080    // Create Table
5081    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
5082      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(familyName)
5083        .setMaxVersions(Integer.MAX_VALUE).setBloomFilterType(BloomType.ROWCOL).build())
5084      .build();
5085    RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build();
5086    this.region = TEST_UTIL.createLocalHRegion(info, tableDescriptor);
5087    // Insert some data
5088    byte[] row = Bytes.toBytes("row1");
5089    byte[] col = Bytes.toBytes("col1");
5090
5091    Put put = new Put(row);
5092    put.addColumn(familyName, col, 1, Bytes.toBytes("SomeRandomValue"));
5093    region.put(put);
5094    region.flush(true);
5095
5096    Delete del = new Delete(row);
5097    region.delete(del);
5098    region.flush(true);
5099
5100    // Get remaining rows (should have none)
5101    Get get = new Get(row);
5102    get.addColumn(familyName, col);
5103
5104    Cell[] keyValues = region.get(get).rawCells();
5105    assertEquals(0, keyValues.length);
5106  }
5107
5108  @Test
5109  public void testgetHDFSBlocksDistribution() throws Exception {
5110    HBaseTestingUtil htu = new HBaseTestingUtil();
5111    // Why do we set the block size in this test?  If we set it smaller than the kvs, then we'll
5112    // break up the file in to more pieces that can be distributed across the three nodes and we
5113    // won't be able to have the condition this test asserts; that at least one node has
5114    // a copy of all replicas -- if small block size, then blocks are spread evenly across the
5115    // the three nodes.  hfilev3 with tags seems to put us over the block size.  St.Ack.
5116    // final int DEFAULT_BLOCK_SIZE = 1024;
5117    // htu.getConfiguration().setLong("dfs.blocksize", DEFAULT_BLOCK_SIZE);
5118    htu.getConfiguration().setInt("dfs.replication", 2);
5119
5120    // set up a cluster with 3 nodes
5121    SingleProcessHBaseCluster cluster = null;
5122    String dataNodeHosts[] = new String[] { "host1", "host2", "host3" };
5123    int regionServersCount = 3;
5124
5125    try {
5126      StartTestingClusterOption option = StartTestingClusterOption.builder()
5127          .numRegionServers(regionServersCount).dataNodeHosts(dataNodeHosts).build();
5128      cluster = htu.startMiniCluster(option);
5129      byte[][] families = { fam1, fam2 };
5130      Table ht = htu.createTable(tableName, families);
5131
5132      // Setting up region
5133      byte row[] = Bytes.toBytes("row1");
5134      byte col[] = Bytes.toBytes("col1");
5135
5136      Put put = new Put(row);
5137      put.addColumn(fam1, col, 1, Bytes.toBytes("test1"));
5138      put.addColumn(fam2, col, 1, Bytes.toBytes("test2"));
5139      ht.put(put);
5140
5141      HRegion firstRegion = htu.getHBaseCluster().getRegions(tableName).get(0);
5142      firstRegion.flush(true);
5143      HDFSBlocksDistribution blocksDistribution1 = firstRegion.getHDFSBlocksDistribution();
5144
5145      // Given the default replication factor is 2 and we have 2 HFiles,
5146      // we will have total of 4 replica of blocks on 3 datanodes; thus there
5147      // must be at least one host that have replica for 2 HFiles. That host's
5148      // weight will be equal to the unique block weight.
5149      long uniqueBlocksWeight1 = blocksDistribution1.getUniqueBlocksTotalWeight();
5150      StringBuilder sb = new StringBuilder();
5151      for (String host: blocksDistribution1.getTopHosts()) {
5152        if (sb.length() > 0) sb.append(", ");
5153        sb.append(host);
5154        sb.append("=");
5155        sb.append(blocksDistribution1.getWeight(host));
5156      }
5157
5158      String topHost = blocksDistribution1.getTopHosts().get(0);
5159      long topHostWeight = blocksDistribution1.getWeight(topHost);
5160      String msg = "uniqueBlocksWeight=" + uniqueBlocksWeight1 + ", topHostWeight=" +
5161        topHostWeight + ", topHost=" + topHost + "; " + sb.toString();
5162      LOG.info(msg);
5163      assertTrue(msg, uniqueBlocksWeight1 == topHostWeight);
5164
5165      // use the static method to compute the value, it should be the same.
5166      // static method is used by load balancer or other components
5167      HDFSBlocksDistribution blocksDistribution2 = HRegion.computeHDFSBlocksDistribution(
5168          htu.getConfiguration(), firstRegion.getTableDescriptor(), firstRegion.getRegionInfo());
5169      long uniqueBlocksWeight2 = blocksDistribution2.getUniqueBlocksTotalWeight();
5170
5171      assertTrue(uniqueBlocksWeight1 == uniqueBlocksWeight2);
5172
5173      ht.close();
5174    } finally {
5175      if (cluster != null) {
5176        htu.shutdownMiniCluster();
5177      }
5178    }
5179  }
5180
5181  /**
5182   * Testcase to check state of region initialization task set to ABORTED or not
5183   * if any exceptions during initialization
5184   *
5185   * @throws Exception
5186   */
5187  @Test
5188  public void testStatusSettingToAbortIfAnyExceptionDuringRegionInitilization() throws Exception {
5189    RegionInfo info;
5190    try {
5191      FileSystem fs = Mockito.mock(FileSystem.class);
5192      Mockito.when(fs.exists((Path) Mockito.anyObject())).thenThrow(new IOException());
5193      TableDescriptorBuilder tableDescriptorBuilder =
5194        TableDescriptorBuilder.newBuilder(tableName);
5195      ColumnFamilyDescriptor columnFamilyDescriptor =
5196        ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf")).build();
5197      tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
5198      info = RegionInfoBuilder.newBuilder(tableName).build();
5199      Path path = new Path(dir + "testStatusSettingToAbortIfAnyExceptionDuringRegionInitilization");
5200      region = HRegion.newHRegion(path, null, fs, CONF, info,
5201        tableDescriptorBuilder.build(), null);
5202      // region initialization throws IOException and set task state to ABORTED.
5203      region.initialize();
5204      fail("Region initialization should fail due to IOException");
5205    } catch (IOException io) {
5206      List<MonitoredTask> tasks = TaskMonitor.get().getTasks();
5207      for (MonitoredTask monitoredTask : tasks) {
5208        if (!(monitoredTask instanceof MonitoredRPCHandler)
5209            && monitoredTask.getDescription().contains(region.toString())) {
5210          assertTrue("Region state should be ABORTED.",
5211              monitoredTask.getState().equals(MonitoredTask.State.ABORTED));
5212          break;
5213        }
5214      }
5215    }
5216  }
5217
5218  /**
5219   * Verifies that the .regioninfo file is written on region creation and that
5220   * is recreated if missing during region opening.
5221   */
5222  @Test
5223  public void testRegionInfoFileCreation() throws IOException {
5224    Path rootDir = new Path(dir + "testRegionInfoFileCreation");
5225
5226    TableDescriptorBuilder tableDescriptorBuilder =
5227      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
5228    ColumnFamilyDescriptor columnFamilyDescriptor =
5229      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf")).build();
5230    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
5231    TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
5232
5233    RegionInfo hri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build();
5234
5235    // Create a region and skip the initialization (like CreateTableHandler)
5236    region = HBaseTestingUtil.createRegionAndWAL(hri, rootDir, CONF,
5237      tableDescriptor, false);
5238    Path regionDir = region.getRegionFileSystem().getRegionDir();
5239    FileSystem fs = region.getRegionFileSystem().getFileSystem();
5240    HBaseTestingUtil.closeRegionAndWAL(region);
5241
5242    Path regionInfoFile = new Path(regionDir, HRegionFileSystem.REGION_INFO_FILE);
5243
5244    // Verify that the .regioninfo file is present
5245    assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir",
5246        fs.exists(regionInfoFile));
5247
5248    // Try to open the region
5249    region = HRegion.openHRegion(rootDir, hri, tableDescriptor, null, CONF);
5250    assertEquals(regionDir, region.getRegionFileSystem().getRegionDir());
5251    HBaseTestingUtil.closeRegionAndWAL(region);
5252
5253    // Verify that the .regioninfo file is still there
5254    assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir",
5255        fs.exists(regionInfoFile));
5256
5257    // Remove the .regioninfo file and verify is recreated on region open
5258    fs.delete(regionInfoFile, true);
5259    assertFalse(HRegionFileSystem.REGION_INFO_FILE + " should be removed from the region dir",
5260        fs.exists(regionInfoFile));
5261
5262    region = HRegion.openHRegion(rootDir, hri, tableDescriptor, null, CONF);
5263//    region = TEST_UTIL.openHRegion(hri, htd);
5264    assertEquals(regionDir, region.getRegionFileSystem().getRegionDir());
5265    HBaseTestingUtil.closeRegionAndWAL(region);
5266
5267    // Verify that the .regioninfo file is still there
5268    assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir",
5269        fs.exists(new Path(regionDir, HRegionFileSystem.REGION_INFO_FILE)));
5270
5271    region = null;
5272  }
5273
5274  /**
5275   * TestCase for increment
5276   */
5277  private static class Incrementer implements Runnable {
5278    private HRegion region;
5279    private final static byte[] incRow = Bytes.toBytes("incRow");
5280    private final static byte[] family = Bytes.toBytes("family");
5281    private final static byte[] qualifier = Bytes.toBytes("qualifier");
5282    private final static long ONE = 1L;
5283    private int incCounter;
5284
5285    public Incrementer(HRegion region, int incCounter) {
5286      this.region = region;
5287      this.incCounter = incCounter;
5288    }
5289
5290    @Override
5291    public void run() {
5292      int count = 0;
5293      while (count < incCounter) {
5294        Increment inc = new Increment(incRow);
5295        inc.addColumn(family, qualifier, ONE);
5296        count++;
5297        try {
5298          region.increment(inc);
5299        } catch (IOException e) {
5300          LOG.info("Count=" + count + ", " + e);
5301          break;
5302        }
5303      }
5304    }
5305  }
5306
5307  /**
5308   * Test case to check increment function with memstore flushing
5309   * @throws Exception
5310   */
5311  @Test
5312  public void testParallelIncrementWithMemStoreFlush() throws Exception {
5313    byte[] family = Incrementer.family;
5314    this.region = initHRegion(tableName, method, CONF, family);
5315    final HRegion region = this.region;
5316    final AtomicBoolean incrementDone = new AtomicBoolean(false);
5317    Runnable flusher = new Runnable() {
5318      @Override
5319      public void run() {
5320        while (!incrementDone.get()) {
5321          try {
5322            region.flush(true);
5323          } catch (Exception e) {
5324            e.printStackTrace();
5325          }
5326        }
5327      }
5328    };
5329
5330    // after all increment finished, the row will increment to 20*100 = 2000
5331    int threadNum = 20;
5332    int incCounter = 100;
5333    long expected = (long) threadNum * incCounter;
5334    Thread[] incrementers = new Thread[threadNum];
5335    Thread flushThread = new Thread(flusher);
5336    for (int i = 0; i < threadNum; i++) {
5337      incrementers[i] = new Thread(new Incrementer(this.region, incCounter));
5338      incrementers[i].start();
5339    }
5340    flushThread.start();
5341    for (int i = 0; i < threadNum; i++) {
5342      incrementers[i].join();
5343    }
5344
5345    incrementDone.set(true);
5346    flushThread.join();
5347
5348    Get get = new Get(Incrementer.incRow);
5349    get.addColumn(Incrementer.family, Incrementer.qualifier);
5350    get.readVersions(1);
5351    Result res = this.region.get(get);
5352    List<Cell> kvs = res.getColumnCells(Incrementer.family, Incrementer.qualifier);
5353
5354    // we just got the latest version
5355    assertEquals(1, kvs.size());
5356    Cell kv = kvs.get(0);
5357    assertEquals(expected, Bytes.toLong(kv.getValueArray(), kv.getValueOffset()));
5358  }
5359
5360  /**
5361   * TestCase for append
5362   */
5363  private static class Appender implements Runnable {
5364    private HRegion region;
5365    private final static byte[] appendRow = Bytes.toBytes("appendRow");
5366    private final static byte[] family = Bytes.toBytes("family");
5367    private final static byte[] qualifier = Bytes.toBytes("qualifier");
5368    private final static byte[] CHAR = Bytes.toBytes("a");
5369    private int appendCounter;
5370
5371    public Appender(HRegion region, int appendCounter) {
5372      this.region = region;
5373      this.appendCounter = appendCounter;
5374    }
5375
5376    @Override
5377    public void run() {
5378      int count = 0;
5379      while (count < appendCounter) {
5380        Append app = new Append(appendRow);
5381        app.addColumn(family, qualifier, CHAR);
5382        count++;
5383        try {
5384          region.append(app);
5385        } catch (IOException e) {
5386          LOG.info("Count=" + count + ", max=" + appendCounter + ", " + e);
5387          break;
5388        }
5389      }
5390    }
5391  }
5392
5393  /**
5394   * Test case to check append function with memstore flushing
5395   * @throws Exception
5396   */
5397  @Test
5398  public void testParallelAppendWithMemStoreFlush() throws Exception {
5399    byte[] family = Appender.family;
5400    this.region = initHRegion(tableName, method, CONF, family);
5401    final HRegion region = this.region;
5402    final AtomicBoolean appendDone = new AtomicBoolean(false);
5403    Runnable flusher = new Runnable() {
5404      @Override
5405      public void run() {
5406        while (!appendDone.get()) {
5407          try {
5408            region.flush(true);
5409          } catch (Exception e) {
5410            e.printStackTrace();
5411          }
5412        }
5413      }
5414    };
5415
5416    // After all append finished, the value will append to threadNum *
5417    // appendCounter Appender.CHAR
5418    int threadNum = 20;
5419    int appendCounter = 100;
5420    byte[] expected = new byte[threadNum * appendCounter];
5421    for (int i = 0; i < threadNum * appendCounter; i++) {
5422      System.arraycopy(Appender.CHAR, 0, expected, i, 1);
5423    }
5424    Thread[] appenders = new Thread[threadNum];
5425    Thread flushThread = new Thread(flusher);
5426    for (int i = 0; i < threadNum; i++) {
5427      appenders[i] = new Thread(new Appender(this.region, appendCounter));
5428      appenders[i].start();
5429    }
5430    flushThread.start();
5431    for (int i = 0; i < threadNum; i++) {
5432      appenders[i].join();
5433    }
5434
5435    appendDone.set(true);
5436    flushThread.join();
5437
5438    Get get = new Get(Appender.appendRow);
5439    get.addColumn(Appender.family, Appender.qualifier);
5440    get.readVersions(1);
5441    Result res = this.region.get(get);
5442    List<Cell> kvs = res.getColumnCells(Appender.family, Appender.qualifier);
5443
5444    // we just got the latest version
5445    assertEquals(1, kvs.size());
5446    Cell kv = kvs.get(0);
5447    byte[] appendResult = new byte[kv.getValueLength()];
5448    System.arraycopy(kv.getValueArray(), kv.getValueOffset(), appendResult, 0, kv.getValueLength());
5449    assertArrayEquals(expected, appendResult);
5450  }
5451
5452  /**
5453   * Test case to check put function with memstore flushing for same row, same ts
5454   * @throws Exception
5455   */
5456  @Test
5457  public void testPutWithMemStoreFlush() throws Exception {
5458    byte[] family = Bytes.toBytes("family");
5459    byte[] qualifier = Bytes.toBytes("qualifier");
5460    byte[] row = Bytes.toBytes("putRow");
5461    byte[] value = null;
5462    this.region = initHRegion(tableName, method, CONF, family);
5463    Put put = null;
5464    Get get = null;
5465    List<Cell> kvs = null;
5466    Result res = null;
5467
5468    put = new Put(row);
5469    value = Bytes.toBytes("value0");
5470    put.addColumn(family, qualifier, 1234567L, value);
5471    region.put(put);
5472    get = new Get(row);
5473    get.addColumn(family, qualifier);
5474    get.readAllVersions();
5475    res = this.region.get(get);
5476    kvs = res.getColumnCells(family, qualifier);
5477    assertEquals(1, kvs.size());
5478    assertArrayEquals(Bytes.toBytes("value0"), CellUtil.cloneValue(kvs.get(0)));
5479
5480    region.flush(true);
5481    get = new Get(row);
5482    get.addColumn(family, qualifier);
5483    get.readAllVersions();
5484    res = this.region.get(get);
5485    kvs = res.getColumnCells(family, qualifier);
5486    assertEquals(1, kvs.size());
5487    assertArrayEquals(Bytes.toBytes("value0"), CellUtil.cloneValue(kvs.get(0)));
5488
5489    put = new Put(row);
5490    value = Bytes.toBytes("value1");
5491    put.addColumn(family, qualifier, 1234567L, value);
5492    region.put(put);
5493    get = new Get(row);
5494    get.addColumn(family, qualifier);
5495    get.readAllVersions();
5496    res = this.region.get(get);
5497    kvs = res.getColumnCells(family, qualifier);
5498    assertEquals(1, kvs.size());
5499    assertArrayEquals(Bytes.toBytes("value1"), CellUtil.cloneValue(kvs.get(0)));
5500
5501    region.flush(true);
5502    get = new Get(row);
5503    get.addColumn(family, qualifier);
5504    get.readAllVersions();
5505    res = this.region.get(get);
5506    kvs = res.getColumnCells(family, qualifier);
5507    assertEquals(1, kvs.size());
5508    assertArrayEquals(Bytes.toBytes("value1"), CellUtil.cloneValue(kvs.get(0)));
5509  }
5510
5511  @Test
5512  public void testDurability() throws Exception {
5513    // there are 5 x 5 cases:
5514    // table durability(SYNC,FSYNC,ASYC,SKIP,USE_DEFAULT) x mutation
5515    // durability(SYNC,FSYNC,ASYC,SKIP,USE_DEFAULT)
5516
5517    // expected cases for append and sync wal
5518    durabilityTest(method, Durability.SYNC_WAL, Durability.SYNC_WAL, 0, true, true, false);
5519    durabilityTest(method, Durability.SYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false);
5520    durabilityTest(method, Durability.SYNC_WAL, Durability.USE_DEFAULT, 0, true, true, false);
5521
5522    durabilityTest(method, Durability.FSYNC_WAL, Durability.SYNC_WAL, 0, true, true, false);
5523    durabilityTest(method, Durability.FSYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false);
5524    durabilityTest(method, Durability.FSYNC_WAL, Durability.USE_DEFAULT, 0, true, true, false);
5525
5526    durabilityTest(method, Durability.ASYNC_WAL, Durability.SYNC_WAL, 0, true, true, false);
5527    durabilityTest(method, Durability.ASYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false);
5528
5529    durabilityTest(method, Durability.SKIP_WAL, Durability.SYNC_WAL, 0, true, true, false);
5530    durabilityTest(method, Durability.SKIP_WAL, Durability.FSYNC_WAL, 0, true, true, false);
5531
5532    durabilityTest(method, Durability.USE_DEFAULT, Durability.SYNC_WAL, 0, true, true, false);
5533    durabilityTest(method, Durability.USE_DEFAULT, Durability.FSYNC_WAL, 0, true, true, false);
5534    durabilityTest(method, Durability.USE_DEFAULT, Durability.USE_DEFAULT, 0, true, true, false);
5535
5536    // expected cases for async wal
5537    durabilityTest(method, Durability.SYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false);
5538    durabilityTest(method, Durability.FSYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false);
5539    durabilityTest(method, Durability.ASYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false);
5540    durabilityTest(method, Durability.SKIP_WAL, Durability.ASYNC_WAL, 0, true, false, false);
5541    durabilityTest(method, Durability.USE_DEFAULT, Durability.ASYNC_WAL, 0, true, false, false);
5542    durabilityTest(method, Durability.ASYNC_WAL, Durability.USE_DEFAULT, 0, true, false, false);
5543
5544    durabilityTest(method, Durability.SYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true);
5545    durabilityTest(method, Durability.FSYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true);
5546    durabilityTest(method, Durability.ASYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true);
5547    durabilityTest(method, Durability.SKIP_WAL, Durability.ASYNC_WAL, 5000, true, false, true);
5548    durabilityTest(method, Durability.USE_DEFAULT, Durability.ASYNC_WAL, 5000, true, false, true);
5549    durabilityTest(method, Durability.ASYNC_WAL, Durability.USE_DEFAULT, 5000, true, false, true);
5550
5551    // expect skip wal cases
5552    durabilityTest(method, Durability.SYNC_WAL, Durability.SKIP_WAL, 0, false, false, false);
5553    durabilityTest(method, Durability.FSYNC_WAL, Durability.SKIP_WAL, 0, false, false, false);
5554    durabilityTest(method, Durability.ASYNC_WAL, Durability.SKIP_WAL, 0, false, false, false);
5555    durabilityTest(method, Durability.SKIP_WAL, Durability.SKIP_WAL, 0, false, false, false);
5556    durabilityTest(method, Durability.USE_DEFAULT, Durability.SKIP_WAL, 0, false, false, false);
5557    durabilityTest(method, Durability.SKIP_WAL, Durability.USE_DEFAULT, 0, false, false, false);
5558
5559  }
5560
5561  private void durabilityTest(String method, Durability tableDurability,
5562      Durability mutationDurability, long timeout, boolean expectAppend, final boolean expectSync,
5563      final boolean expectSyncFromLogSyncer) throws Exception {
5564    Configuration conf = HBaseConfiguration.create(CONF);
5565    method = method + "_" + tableDurability.name() + "_" + mutationDurability.name();
5566    byte[] family = Bytes.toBytes("family");
5567    Path logDir = new Path(new Path(dir + method), "log");
5568    final Configuration walConf = new Configuration(conf);
5569    CommonFSUtils.setRootDir(walConf, logDir);
5570    // XXX: The spied AsyncFSWAL can not work properly because of a Mockito defect that can not
5571    // deal with classes which have a field of an inner class. See discussions in HBASE-15536.
5572    walConf.set(WALFactory.WAL_PROVIDER, "filesystem");
5573    final WALFactory wals = new WALFactory(walConf, HBaseTestingUtil.getRandomUUID().toString());
5574    final WAL wal = spy(wals.getWAL(RegionInfoBuilder.newBuilder(tableName).build()));
5575    this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW,
5576        HConstants.EMPTY_END_ROW, CONF, false, tableDurability, wal,
5577        new byte[][] { family });
5578
5579    Put put = new Put(Bytes.toBytes("r1"));
5580    put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1"));
5581    put.setDurability(mutationDurability);
5582    region.put(put);
5583
5584    // verify append called or not
5585    verify(wal, expectAppend ? times(1) : never()).appendData((RegionInfo) any(),
5586      (WALKeyImpl) any(), (WALEdit) any());
5587
5588    // verify sync called or not
5589    if (expectSync || expectSyncFromLogSyncer) {
5590      TEST_UTIL.waitFor(timeout, new Waiter.Predicate<Exception>() {
5591        @Override
5592        public boolean evaluate() throws Exception {
5593          try {
5594            if (expectSync) {
5595              verify(wal, times(1)).sync(anyLong()); // Hregion calls this one
5596            } else if (expectSyncFromLogSyncer) {
5597              verify(wal, times(1)).sync(); // wal syncer calls this one
5598            }
5599          } catch (Throwable ignore) {
5600          }
5601          return true;
5602        }
5603      });
5604    } else {
5605      //verify(wal, never()).sync(anyLong());
5606      verify(wal, never()).sync();
5607    }
5608
5609    HBaseTestingUtil.closeRegionAndWAL(this.region);
5610    wals.close();
5611    this.region = null;
5612  }
5613
5614  @Test
5615  public void testRegionReplicaSecondary() throws IOException {
5616    // create a primary region, load some data and flush
5617    // create a secondary region, and do a get against that
5618    Path rootDir = new Path(dir + name.getMethodName());
5619    CommonFSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir);
5620
5621    byte[][] families = new byte[][] {
5622        Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3")
5623    };
5624    byte[] cq = Bytes.toBytes("cq");
5625    TableDescriptorBuilder builder =
5626      TableDescriptorBuilder.newBuilder(
5627        TableName.valueOf(name.getMethodName()));
5628    for (byte[] family : families) {
5629      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family));
5630    }
5631    TableDescriptor tableDescriptor = builder.build();
5632    long time = EnvironmentEdgeManager.currentTime();
5633    RegionInfo primaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName())
5634      .setRegionId(time).setReplicaId(0).build();
5635    RegionInfo secondaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName())
5636      .setRegionId(time).setReplicaId(1).build();
5637
5638    HRegion primaryRegion = null, secondaryRegion = null;
5639
5640    try {
5641      primaryRegion = HBaseTestingUtil.createRegionAndWAL(primaryHri,
5642          rootDir, TEST_UTIL.getConfiguration(), tableDescriptor);
5643
5644      // load some data
5645      putData(primaryRegion, 0, 1000, cq, families);
5646
5647      // flush region
5648      primaryRegion.flush(true);
5649
5650      // open secondary region
5651      secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, tableDescriptor, null, CONF);
5652
5653      verifyData(secondaryRegion, 0, 1000, cq, families);
5654    } finally {
5655      if (primaryRegion != null) {
5656        HBaseTestingUtil.closeRegionAndWAL(primaryRegion);
5657      }
5658      if (secondaryRegion != null) {
5659        HBaseTestingUtil.closeRegionAndWAL(secondaryRegion);
5660      }
5661    }
5662  }
5663
5664  @Test
5665  public void testRegionReplicaSecondaryIsReadOnly() throws IOException {
5666    // create a primary region, load some data and flush
5667    // create a secondary region, and do a put against that
5668    Path rootDir = new Path(dir + name.getMethodName());
5669    CommonFSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir);
5670
5671    byte[][] families = new byte[][] {
5672        Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3")
5673    };
5674    byte[] cq = Bytes.toBytes("cq");
5675    TableDescriptorBuilder builder =
5676      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
5677    for (byte[] family : families) {
5678      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family));
5679    }
5680    TableDescriptor tableDescriptor = builder.build();
5681    long time = EnvironmentEdgeManager.currentTime();
5682    RegionInfo primaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName())
5683      .setRegionId(time).setReplicaId(0).build();
5684    RegionInfo secondaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName())
5685      .setRegionId(time).setReplicaId(1).build();
5686
5687    HRegion primaryRegion = null, secondaryRegion = null;
5688
5689    try {
5690      primaryRegion = HBaseTestingUtil.createRegionAndWAL(primaryHri,
5691          rootDir, TEST_UTIL.getConfiguration(), tableDescriptor);
5692
5693      // load some data
5694      putData(primaryRegion, 0, 1000, cq, families);
5695
5696      // flush region
5697      primaryRegion.flush(true);
5698
5699      // open secondary region
5700      secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, tableDescriptor, null, CONF);
5701
5702      try {
5703        putData(secondaryRegion, 0, 1000, cq, families);
5704        fail("Should have thrown exception");
5705      } catch (IOException ex) {
5706        // expected
5707      }
5708    } finally {
5709      if (primaryRegion != null) {
5710        HBaseTestingUtil.closeRegionAndWAL(primaryRegion);
5711      }
5712      if (secondaryRegion != null) {
5713        HBaseTestingUtil.closeRegionAndWAL(secondaryRegion);
5714      }
5715    }
5716  }
5717
5718  static WALFactory createWALFactory(Configuration conf, Path rootDir) throws IOException {
5719    Configuration confForWAL = new Configuration(conf);
5720    confForWAL.set(HConstants.HBASE_DIR, rootDir.toString());
5721    return new WALFactory(confForWAL, "hregion-" + RandomStringUtils.randomNumeric(8));
5722  }
5723
5724  @Test
5725  public void testCompactionFromPrimary() throws IOException {
5726    Path rootDir = new Path(dir + name.getMethodName());
5727    CommonFSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir);
5728
5729    byte[][] families = new byte[][] {
5730        Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3")
5731    };
5732    byte[] cq = Bytes.toBytes("cq");
5733    TableDescriptorBuilder builder =
5734      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
5735    for (byte[] family : families) {
5736      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family));
5737    }
5738    TableDescriptor tableDescriptor = builder.build();
5739    long time = EnvironmentEdgeManager.currentTime();
5740    RegionInfo primaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName())
5741      .setRegionId(time).setReplicaId(0).build();
5742    RegionInfo secondaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName())
5743      .setRegionId(time).setReplicaId(1).build();
5744
5745    HRegion primaryRegion = null, secondaryRegion = null;
5746
5747    try {
5748      primaryRegion = HBaseTestingUtil.createRegionAndWAL(primaryHri,
5749          rootDir, TEST_UTIL.getConfiguration(), tableDescriptor);
5750
5751      // load some data
5752      putData(primaryRegion, 0, 1000, cq, families);
5753
5754      // flush region
5755      primaryRegion.flush(true);
5756
5757      // open secondary region
5758      secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, tableDescriptor, null, CONF);
5759
5760      // move the file of the primary region to the archive, simulating a compaction
5761      Collection<HStoreFile> storeFiles = primaryRegion.getStore(families[0]).getStorefiles();
5762      primaryRegion.getRegionFileSystem().removeStoreFiles(Bytes.toString(families[0]), storeFiles);
5763      Collection<StoreFileInfo> storeFileInfos = primaryRegion.getRegionFileSystem()
5764          .getStoreFiles(families[0]);
5765      Assert.assertTrue(storeFileInfos == null || storeFileInfos.isEmpty());
5766
5767      verifyData(secondaryRegion, 0, 1000, cq, families);
5768    } finally {
5769      if (primaryRegion != null) {
5770        HBaseTestingUtil.closeRegionAndWAL(primaryRegion);
5771      }
5772      if (secondaryRegion != null) {
5773        HBaseTestingUtil.closeRegionAndWAL(secondaryRegion);
5774      }
5775    }
5776  }
5777
5778  private void putData(int startRow, int numRows, byte[] qf, byte[]... families) throws
5779      IOException {
5780    putData(this.region, startRow, numRows, qf, families);
5781  }
5782
5783  private void putData(HRegion region,
5784      int startRow, int numRows, byte[] qf, byte[]... families) throws IOException {
5785    putData(region, Durability.SKIP_WAL, startRow, numRows, qf, families);
5786  }
5787
5788  static void putData(HRegion region, Durability durability,
5789      int startRow, int numRows, byte[] qf, byte[]... families) throws IOException {
5790    for (int i = startRow; i < startRow + numRows; i++) {
5791      Put put = new Put(Bytes.toBytes("" + i));
5792      put.setDurability(durability);
5793      for (byte[] family : families) {
5794        put.addColumn(family, qf, null);
5795      }
5796      region.put(put);
5797      LOG.info(put.toString());
5798    }
5799  }
5800
5801  static void verifyData(HRegion newReg, int startRow, int numRows, byte[] qf, byte[]... families)
5802      throws IOException {
5803    for (int i = startRow; i < startRow + numRows; i++) {
5804      byte[] row = Bytes.toBytes("" + i);
5805      Get get = new Get(row);
5806      for (byte[] family : families) {
5807        get.addColumn(family, qf);
5808      }
5809      Result result = newReg.get(get);
5810      Cell[] raw = result.rawCells();
5811      assertEquals(families.length, result.size());
5812      for (int j = 0; j < families.length; j++) {
5813        assertTrue(CellUtil.matchingRows(raw[j], row));
5814        assertTrue(CellUtil.matchingFamily(raw[j], families[j]));
5815        assertTrue(CellUtil.matchingQualifier(raw[j], qf));
5816      }
5817    }
5818  }
5819
5820  static void assertGet(final HRegion r, final byte[] family, final byte[] k) throws IOException {
5821    // Now I have k, get values out and assert they are as expected.
5822    Get get = new Get(k).addFamily(family).readAllVersions();
5823    Cell[] results = r.get(get).rawCells();
5824    for (int j = 0; j < results.length; j++) {
5825      byte[] tmp = CellUtil.cloneValue(results[j]);
5826      // Row should be equal to value every time.
5827      assertTrue(Bytes.equals(k, tmp));
5828    }
5829  }
5830
5831  /*
5832   * Assert first value in the passed region is <code>firstValue</code>.
5833   *
5834   * @param r
5835   *
5836   * @param fs
5837   *
5838   * @param firstValue
5839   *
5840   * @throws IOException
5841   */
5842  protected void assertScan(final HRegion r, final byte[] fs, final byte[] firstValue)
5843      throws IOException {
5844    byte[][] families = { fs };
5845    Scan scan = new Scan();
5846    for (int i = 0; i < families.length; i++)
5847      scan.addFamily(families[i]);
5848    InternalScanner s = r.getScanner(scan);
5849    try {
5850      List<Cell> curVals = new ArrayList<>();
5851      boolean first = true;
5852      OUTER_LOOP: while (s.next(curVals)) {
5853        for (Cell kv : curVals) {
5854          byte[] val = CellUtil.cloneValue(kv);
5855          byte[] curval = val;
5856          if (first) {
5857            first = false;
5858            assertTrue(Bytes.compareTo(curval, firstValue) == 0);
5859          } else {
5860            // Not asserting anything. Might as well break.
5861            break OUTER_LOOP;
5862          }
5863        }
5864      }
5865    } finally {
5866      s.close();
5867    }
5868  }
5869
5870  /**
5871   * Test that we get the expected flush results back
5872   */
5873  @Test
5874  public void testFlushResult() throws IOException {
5875    byte[] family = Bytes.toBytes("family");
5876
5877    this.region = initHRegion(tableName, method, family);
5878
5879    // empty memstore, flush doesn't run
5880    HRegion.FlushResult fr = region.flush(true);
5881    assertFalse(fr.isFlushSucceeded());
5882    assertFalse(fr.isCompactionNeeded());
5883
5884    // Flush enough files to get up to the threshold, doesn't need compactions
5885    for (int i = 0; i < 2; i++) {
5886      Put put = new Put(tableName.toBytes()).addColumn(family, family, tableName.toBytes());
5887      region.put(put);
5888      fr = region.flush(true);
5889      assertTrue(fr.isFlushSucceeded());
5890      assertFalse(fr.isCompactionNeeded());
5891    }
5892
5893    // Two flushes after the threshold, compactions are needed
5894    for (int i = 0; i < 2; i++) {
5895      Put put = new Put(tableName.toBytes()).addColumn(family, family, tableName.toBytes());
5896      region.put(put);
5897      fr = region.flush(true);
5898      assertTrue(fr.isFlushSucceeded());
5899      assertTrue(fr.isCompactionNeeded());
5900    }
5901  }
5902
5903  protected Configuration initSplit() {
5904    // Always compact if there is more than one store file.
5905    CONF.setInt("hbase.hstore.compactionThreshold", 2);
5906
5907    CONF.setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, 10 * 1000);
5908
5909    // Increase the amount of time between client retries
5910    CONF.setLong("hbase.client.pause", 15 * 1000);
5911
5912    // This size should make it so we always split using the addContent
5913    // below. After adding all data, the first region is 1.3M
5914    CONF.setLong(HConstants.HREGION_MAX_FILESIZE, 1024 * 128);
5915    return CONF;
5916  }
5917
5918  /**
5919   * @return A region on which you must call
5920   *         {@link HBaseTestingUtil#closeRegionAndWAL(HRegion)} when done.
5921   */
5922  protected HRegion initHRegion(TableName tableName, String callingMethod, Configuration conf,
5923      byte[]... families) throws IOException {
5924    return initHRegion(tableName, callingMethod, conf, false, families);
5925  }
5926
5927  /**
5928   * @return A region on which you must call
5929   *         {@link HBaseTestingUtil#closeRegionAndWAL(HRegion)} when done.
5930   */
5931  protected HRegion initHRegion(TableName tableName, String callingMethod, Configuration conf,
5932      boolean isReadOnly, byte[]... families) throws IOException {
5933    return initHRegion(tableName, null, null, callingMethod, conf, isReadOnly, families);
5934  }
5935
5936  protected HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey,
5937    String callingMethod, Configuration conf, boolean isReadOnly, byte[]... families)
5938    throws IOException {
5939    Path logDir = TEST_UTIL.getDataTestDirOnTestFS(callingMethod + ".log");
5940    RegionInfo hri =
5941      RegionInfoBuilder.newBuilder(tableName).setStartKey(startKey).setEndKey(stopKey).build();
5942    final WAL wal = HBaseTestingUtil.createWal(conf, logDir, hri);
5943    return initHRegion(tableName, startKey, stopKey, conf, isReadOnly, Durability.SYNC_WAL, wal,
5944      families);
5945  }
5946
5947  /**
5948   * @return A region on which you must call
5949   *         {@link HBaseTestingUtil#closeRegionAndWAL(HRegion)} when done.
5950   */
5951  public HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey,
5952      Configuration conf, boolean isReadOnly, Durability durability, WAL wal,
5953      byte[]... families) throws IOException {
5954    ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0,
5955      0, null, MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT);
5956    return TEST_UTIL.createLocalHRegion(tableName, startKey, stopKey,
5957        conf, isReadOnly, durability, wal, families);
5958  }
5959
5960  /**
5961   * Assert that the passed in Cell has expected contents for the specified row,
5962   * column & timestamp.
5963   */
5964  private void checkOneCell(Cell kv, byte[] cf, int rowIdx, int colIdx, long ts) {
5965    String ctx = "rowIdx=" + rowIdx + "; colIdx=" + colIdx + "; ts=" + ts;
5966    assertEquals("Row mismatch which checking: " + ctx, "row:" + rowIdx,
5967        Bytes.toString(CellUtil.cloneRow(kv)));
5968    assertEquals("ColumnFamily mismatch while checking: " + ctx, Bytes.toString(cf),
5969        Bytes.toString(CellUtil.cloneFamily(kv)));
5970    assertEquals("Column qualifier mismatch while checking: " + ctx, "column:" + colIdx,
5971        Bytes.toString(CellUtil.cloneQualifier(kv)));
5972    assertEquals("Timestamp mismatch while checking: " + ctx, ts, kv.getTimestamp());
5973    assertEquals("Value mismatch while checking: " + ctx, "value-version-" + ts,
5974        Bytes.toString(CellUtil.cloneValue(kv)));
5975  }
5976
5977  @Test
5978  public void testReverseScanner_FromMemStore_SingleCF_Normal()
5979      throws IOException {
5980    byte[] rowC = Bytes.toBytes("rowC");
5981    byte[] rowA = Bytes.toBytes("rowA");
5982    byte[] rowB = Bytes.toBytes("rowB");
5983    byte[] cf = Bytes.toBytes("CF");
5984    byte[][] families = { cf };
5985    byte[] col = Bytes.toBytes("C");
5986    long ts = 1;
5987    this.region = initHRegion(tableName, method, families);
5988    KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null);
5989    KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put,
5990        null);
5991    KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null);
5992    KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null);
5993    Put put = null;
5994    put = new Put(rowC);
5995    put.add(kv1);
5996    put.add(kv11);
5997    region.put(put);
5998    put = new Put(rowA);
5999    put.add(kv2);
6000    region.put(put);
6001    put = new Put(rowB);
6002    put.add(kv3);
6003    region.put(put);
6004
6005    Scan scan = new Scan().withStartRow(rowC);
6006    scan.readVersions(5);
6007    scan.setReversed(true);
6008    InternalScanner scanner = region.getScanner(scan);
6009    List<Cell> currRow = new ArrayList<>();
6010    boolean hasNext = scanner.next(currRow);
6011    assertEquals(2, currRow.size());
6012    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6013        .get(0).getRowLength(), rowC, 0, rowC.length));
6014    assertTrue(hasNext);
6015    currRow.clear();
6016    hasNext = scanner.next(currRow);
6017    assertEquals(1, currRow.size());
6018    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6019        .get(0).getRowLength(), rowB, 0, rowB.length));
6020    assertTrue(hasNext);
6021    currRow.clear();
6022    hasNext = scanner.next(currRow);
6023    assertEquals(1, currRow.size());
6024    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6025        .get(0).getRowLength(), rowA, 0, rowA.length));
6026    assertFalse(hasNext);
6027    scanner.close();
6028  }
6029
6030  @Test
6031  public void testReverseScanner_FromMemStore_SingleCF_LargerKey()
6032      throws IOException {
6033    byte[] rowC = Bytes.toBytes("rowC");
6034    byte[] rowA = Bytes.toBytes("rowA");
6035    byte[] rowB = Bytes.toBytes("rowB");
6036    byte[] rowD = Bytes.toBytes("rowD");
6037    byte[] cf = Bytes.toBytes("CF");
6038    byte[][] families = { cf };
6039    byte[] col = Bytes.toBytes("C");
6040    long ts = 1;
6041    this.region = initHRegion(tableName, method, families);
6042    KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null);
6043    KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put,
6044        null);
6045    KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null);
6046    KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null);
6047    Put put = null;
6048    put = new Put(rowC);
6049    put.add(kv1);
6050    put.add(kv11);
6051    region.put(put);
6052    put = new Put(rowA);
6053    put.add(kv2);
6054    region.put(put);
6055    put = new Put(rowB);
6056    put.add(kv3);
6057    region.put(put);
6058
6059    Scan scan = new Scan().withStartRow(rowD);
6060    List<Cell> currRow = new ArrayList<>();
6061    scan.setReversed(true);
6062    scan.readVersions(5);
6063    InternalScanner scanner = region.getScanner(scan);
6064    boolean hasNext = scanner.next(currRow);
6065    assertEquals(2, currRow.size());
6066    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6067        .get(0).getRowLength(), rowC, 0, rowC.length));
6068    assertTrue(hasNext);
6069    currRow.clear();
6070    hasNext = scanner.next(currRow);
6071    assertEquals(1, currRow.size());
6072    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6073        .get(0).getRowLength(), rowB, 0, rowB.length));
6074    assertTrue(hasNext);
6075    currRow.clear();
6076    hasNext = scanner.next(currRow);
6077    assertEquals(1, currRow.size());
6078    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6079        .get(0).getRowLength(), rowA, 0, rowA.length));
6080    assertFalse(hasNext);
6081    scanner.close();
6082  }
6083
6084  @Test
6085  public void testReverseScanner_FromMemStore_SingleCF_FullScan()
6086      throws IOException {
6087    byte[] rowC = Bytes.toBytes("rowC");
6088    byte[] rowA = Bytes.toBytes("rowA");
6089    byte[] rowB = Bytes.toBytes("rowB");
6090    byte[] cf = Bytes.toBytes("CF");
6091    byte[][] families = { cf };
6092    byte[] col = Bytes.toBytes("C");
6093    long ts = 1;
6094    this.region = initHRegion(tableName, method, families);
6095    KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null);
6096    KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put,
6097        null);
6098    KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null);
6099    KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null);
6100    Put put = null;
6101    put = new Put(rowC);
6102    put.add(kv1);
6103    put.add(kv11);
6104    region.put(put);
6105    put = new Put(rowA);
6106    put.add(kv2);
6107    region.put(put);
6108    put = new Put(rowB);
6109    put.add(kv3);
6110    region.put(put);
6111    Scan scan = new Scan();
6112    List<Cell> currRow = new ArrayList<>();
6113    scan.setReversed(true);
6114    InternalScanner scanner = region.getScanner(scan);
6115    boolean hasNext = scanner.next(currRow);
6116    assertEquals(1, currRow.size());
6117    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6118        .get(0).getRowLength(), rowC, 0, rowC.length));
6119    assertTrue(hasNext);
6120    currRow.clear();
6121    hasNext = scanner.next(currRow);
6122    assertEquals(1, currRow.size());
6123    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6124        .get(0).getRowLength(), rowB, 0, rowB.length));
6125    assertTrue(hasNext);
6126    currRow.clear();
6127    hasNext = scanner.next(currRow);
6128    assertEquals(1, currRow.size());
6129    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6130        .get(0).getRowLength(), rowA, 0, rowA.length));
6131    assertFalse(hasNext);
6132    scanner.close();
6133  }
6134
6135  @Test
6136  public void testReverseScanner_moreRowsMayExistAfter() throws IOException {
6137    // case for "INCLUDE_AND_SEEK_NEXT_ROW & SEEK_NEXT_ROW" endless loop
6138    byte[] rowA = Bytes.toBytes("rowA");
6139    byte[] rowB = Bytes.toBytes("rowB");
6140    byte[] rowC = Bytes.toBytes("rowC");
6141    byte[] rowD = Bytes.toBytes("rowD");
6142    byte[] rowE = Bytes.toBytes("rowE");
6143    byte[] cf = Bytes.toBytes("CF");
6144    byte[][] families = { cf };
6145    byte[] col1 = Bytes.toBytes("col1");
6146    byte[] col2 = Bytes.toBytes("col2");
6147    long ts = 1;
6148    this.region = initHRegion(tableName, method, families);
6149    KeyValue kv1 = new KeyValue(rowA, cf, col1, ts, KeyValue.Type.Put, null);
6150    KeyValue kv2 = new KeyValue(rowB, cf, col1, ts, KeyValue.Type.Put, null);
6151    KeyValue kv3 = new KeyValue(rowC, cf, col1, ts, KeyValue.Type.Put, null);
6152    KeyValue kv4_1 = new KeyValue(rowD, cf, col1, ts, KeyValue.Type.Put, null);
6153    KeyValue kv4_2 = new KeyValue(rowD, cf, col2, ts, KeyValue.Type.Put, null);
6154    KeyValue kv5 = new KeyValue(rowE, cf, col1, ts, KeyValue.Type.Put, null);
6155    Put put = null;
6156    put = new Put(rowA);
6157    put.add(kv1);
6158    region.put(put);
6159    put = new Put(rowB);
6160    put.add(kv2);
6161    region.put(put);
6162    put = new Put(rowC);
6163    put.add(kv3);
6164    region.put(put);
6165    put = new Put(rowD);
6166    put.add(kv4_1);
6167    region.put(put);
6168    put = new Put(rowD);
6169    put.add(kv4_2);
6170    region.put(put);
6171    put = new Put(rowE);
6172    put.add(kv5);
6173    region.put(put);
6174    region.flush(true);
6175    Scan scan = new Scan().withStartRow(rowD).withStopRow(rowA);
6176    scan.addColumn(families[0], col1);
6177    scan.setReversed(true);
6178    List<Cell> currRow = new ArrayList<>();
6179    InternalScanner scanner = region.getScanner(scan);
6180    boolean hasNext = scanner.next(currRow);
6181    assertEquals(1, currRow.size());
6182    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6183        .get(0).getRowLength(), rowD, 0, rowD.length));
6184    assertTrue(hasNext);
6185    currRow.clear();
6186    hasNext = scanner.next(currRow);
6187    assertEquals(1, currRow.size());
6188    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6189        .get(0).getRowLength(), rowC, 0, rowC.length));
6190    assertTrue(hasNext);
6191    currRow.clear();
6192    hasNext = scanner.next(currRow);
6193    assertEquals(1, currRow.size());
6194    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6195        .get(0).getRowLength(), rowB, 0, rowB.length));
6196    assertFalse(hasNext);
6197    scanner.close();
6198
6199    scan = new Scan().withStartRow(rowD).withStopRow(rowA);
6200    scan.addColumn(families[0], col2);
6201    scan.setReversed(true);
6202    currRow.clear();
6203    scanner = region.getScanner(scan);
6204    hasNext = scanner.next(currRow);
6205    assertEquals(1, currRow.size());
6206    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6207        .get(0).getRowLength(), rowD, 0, rowD.length));
6208    scanner.close();
6209  }
6210
6211  @Test
6212  public void testReverseScanner_smaller_blocksize() throws IOException {
6213    // case to ensure no conflict with HFile index optimization
6214    byte[] rowA = Bytes.toBytes("rowA");
6215    byte[] rowB = Bytes.toBytes("rowB");
6216    byte[] rowC = Bytes.toBytes("rowC");
6217    byte[] rowD = Bytes.toBytes("rowD");
6218    byte[] rowE = Bytes.toBytes("rowE");
6219    byte[] cf = Bytes.toBytes("CF");
6220    byte[][] families = { cf };
6221    byte[] col1 = Bytes.toBytes("col1");
6222    byte[] col2 = Bytes.toBytes("col2");
6223    long ts = 1;
6224    Configuration conf = new Configuration(CONF);
6225    conf.setInt("test.block.size", 1);
6226    this.region = initHRegion(tableName, method, conf, families);
6227    KeyValue kv1 = new KeyValue(rowA, cf, col1, ts, KeyValue.Type.Put, null);
6228    KeyValue kv2 = new KeyValue(rowB, cf, col1, ts, KeyValue.Type.Put, null);
6229    KeyValue kv3 = new KeyValue(rowC, cf, col1, ts, KeyValue.Type.Put, null);
6230    KeyValue kv4_1 = new KeyValue(rowD, cf, col1, ts, KeyValue.Type.Put, null);
6231    KeyValue kv4_2 = new KeyValue(rowD, cf, col2, ts, KeyValue.Type.Put, null);
6232    KeyValue kv5 = new KeyValue(rowE, cf, col1, ts, KeyValue.Type.Put, null);
6233    Put put = null;
6234    put = new Put(rowA);
6235    put.add(kv1);
6236    region.put(put);
6237    put = new Put(rowB);
6238    put.add(kv2);
6239    region.put(put);
6240    put = new Put(rowC);
6241    put.add(kv3);
6242    region.put(put);
6243    put = new Put(rowD);
6244    put.add(kv4_1);
6245    region.put(put);
6246    put = new Put(rowD);
6247    put.add(kv4_2);
6248    region.put(put);
6249    put = new Put(rowE);
6250    put.add(kv5);
6251    region.put(put);
6252    region.flush(true);
6253    Scan scan = new Scan().withStartRow(rowD).withStopRow(rowA);
6254    scan.addColumn(families[0], col1);
6255    scan.setReversed(true);
6256    List<Cell> currRow = new ArrayList<>();
6257    InternalScanner scanner = region.getScanner(scan);
6258    boolean hasNext = scanner.next(currRow);
6259    assertEquals(1, currRow.size());
6260    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6261        .get(0).getRowLength(), rowD, 0, rowD.length));
6262    assertTrue(hasNext);
6263    currRow.clear();
6264    hasNext = scanner.next(currRow);
6265    assertEquals(1, currRow.size());
6266    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6267        .get(0).getRowLength(), rowC, 0, rowC.length));
6268    assertTrue(hasNext);
6269    currRow.clear();
6270    hasNext = scanner.next(currRow);
6271    assertEquals(1, currRow.size());
6272    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6273        .get(0).getRowLength(), rowB, 0, rowB.length));
6274    assertFalse(hasNext);
6275    scanner.close();
6276
6277    scan = new Scan().withStartRow(rowD).withStopRow(rowA);
6278    scan.addColumn(families[0], col2);
6279    scan.setReversed(true);
6280    currRow.clear();
6281    scanner = region.getScanner(scan);
6282    hasNext = scanner.next(currRow);
6283    assertEquals(1, currRow.size());
6284    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6285        .get(0).getRowLength(), rowD, 0, rowD.length));
6286    scanner.close();
6287  }
6288
6289  @Test
6290  public void testReverseScanner_FromMemStoreAndHFiles_MultiCFs1()
6291      throws IOException {
6292    byte[] row0 = Bytes.toBytes("row0"); // 1 kv
6293    byte[] row1 = Bytes.toBytes("row1"); // 2 kv
6294    byte[] row2 = Bytes.toBytes("row2"); // 4 kv
6295    byte[] row3 = Bytes.toBytes("row3"); // 2 kv
6296    byte[] row4 = Bytes.toBytes("row4"); // 5 kv
6297    byte[] row5 = Bytes.toBytes("row5"); // 2 kv
6298    byte[] cf1 = Bytes.toBytes("CF1");
6299    byte[] cf2 = Bytes.toBytes("CF2");
6300    byte[] cf3 = Bytes.toBytes("CF3");
6301    byte[][] families = { cf1, cf2, cf3 };
6302    byte[] col = Bytes.toBytes("C");
6303    long ts = 1;
6304    Configuration conf = new Configuration(CONF);
6305    // disable compactions in this test.
6306    conf.setInt("hbase.hstore.compactionThreshold", 10000);
6307    this.region = initHRegion(tableName, method, conf, families);
6308    // kv naming style: kv(row number) totalKvCountInThisRow seq no
6309    KeyValue kv0_1_1 = new KeyValue(row0, cf1, col, ts, KeyValue.Type.Put,
6310        null);
6311    KeyValue kv1_2_1 = new KeyValue(row1, cf2, col, ts, KeyValue.Type.Put,
6312        null);
6313    KeyValue kv1_2_2 = new KeyValue(row1, cf1, col, ts + 1,
6314        KeyValue.Type.Put, null);
6315    KeyValue kv2_4_1 = new KeyValue(row2, cf2, col, ts, KeyValue.Type.Put,
6316        null);
6317    KeyValue kv2_4_2 = new KeyValue(row2, cf1, col, ts, KeyValue.Type.Put,
6318        null);
6319    KeyValue kv2_4_3 = new KeyValue(row2, cf3, col, ts, KeyValue.Type.Put,
6320        null);
6321    KeyValue kv2_4_4 = new KeyValue(row2, cf1, col, ts + 4,
6322        KeyValue.Type.Put, null);
6323    KeyValue kv3_2_1 = new KeyValue(row3, cf2, col, ts, KeyValue.Type.Put,
6324        null);
6325    KeyValue kv3_2_2 = new KeyValue(row3, cf1, col, ts + 4,
6326        KeyValue.Type.Put, null);
6327    KeyValue kv4_5_1 = new KeyValue(row4, cf1, col, ts, KeyValue.Type.Put,
6328        null);
6329    KeyValue kv4_5_2 = new KeyValue(row4, cf3, col, ts, KeyValue.Type.Put,
6330        null);
6331    KeyValue kv4_5_3 = new KeyValue(row4, cf3, col, ts + 5,
6332        KeyValue.Type.Put, null);
6333    KeyValue kv4_5_4 = new KeyValue(row4, cf2, col, ts, KeyValue.Type.Put,
6334        null);
6335    KeyValue kv4_5_5 = new KeyValue(row4, cf1, col, ts + 3,
6336        KeyValue.Type.Put, null);
6337    KeyValue kv5_2_1 = new KeyValue(row5, cf2, col, ts, KeyValue.Type.Put,
6338        null);
6339    KeyValue kv5_2_2 = new KeyValue(row5, cf3, col, ts, KeyValue.Type.Put,
6340        null);
6341    // hfiles(cf1/cf2) :"row1"(1 kv) / "row2"(1 kv) / "row4"(2 kv)
6342    Put put = null;
6343    put = new Put(row1);
6344    put.add(kv1_2_1);
6345    region.put(put);
6346    put = new Put(row2);
6347    put.add(kv2_4_1);
6348    region.put(put);
6349    put = new Put(row4);
6350    put.add(kv4_5_4);
6351    put.add(kv4_5_5);
6352    region.put(put);
6353    region.flush(true);
6354    // hfiles(cf1/cf3) : "row1" (1 kvs) / "row2" (1 kv) / "row4" (2 kv)
6355    put = new Put(row4);
6356    put.add(kv4_5_1);
6357    put.add(kv4_5_3);
6358    region.put(put);
6359    put = new Put(row1);
6360    put.add(kv1_2_2);
6361    region.put(put);
6362    put = new Put(row2);
6363    put.add(kv2_4_4);
6364    region.put(put);
6365    region.flush(true);
6366    // hfiles(cf1/cf3) : "row2"(2 kv) / "row3"(1 kvs) / "row4" (1 kv)
6367    put = new Put(row4);
6368    put.add(kv4_5_2);
6369    region.put(put);
6370    put = new Put(row2);
6371    put.add(kv2_4_2);
6372    put.add(kv2_4_3);
6373    region.put(put);
6374    put = new Put(row3);
6375    put.add(kv3_2_2);
6376    region.put(put);
6377    region.flush(true);
6378    // memstore(cf1/cf2/cf3) : "row0" (1 kvs) / "row3" ( 1 kv) / "row5" (max)
6379    // ( 2 kv)
6380    put = new Put(row0);
6381    put.add(kv0_1_1);
6382    region.put(put);
6383    put = new Put(row3);
6384    put.add(kv3_2_1);
6385    region.put(put);
6386    put = new Put(row5);
6387    put.add(kv5_2_1);
6388    put.add(kv5_2_2);
6389    region.put(put);
6390    // scan range = ["row4", min), skip the max "row5"
6391    Scan scan = new Scan().withStartRow(row4);
6392    scan.readVersions(5);
6393    scan.setBatch(3);
6394    scan.setReversed(true);
6395    InternalScanner scanner = region.getScanner(scan);
6396    List<Cell> currRow = new ArrayList<>();
6397    boolean hasNext = false;
6398    // 1. scan out "row4" (5 kvs), "row5" can't be scanned out since not
6399    // included in scan range
6400    // "row4" takes 2 next() calls since batch=3
6401    hasNext = scanner.next(currRow);
6402    assertEquals(3, currRow.size());
6403    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6404        .get(0).getRowLength(), row4, 0, row4.length));
6405    assertTrue(hasNext);
6406    currRow.clear();
6407    hasNext = scanner.next(currRow);
6408    assertEquals(2, currRow.size());
6409    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(),
6410        currRow.get(0).getRowLength(), row4, 0,
6411      row4.length));
6412    assertTrue(hasNext);
6413    // 2. scan out "row3" (2 kv)
6414    currRow.clear();
6415    hasNext = scanner.next(currRow);
6416    assertEquals(2, currRow.size());
6417    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6418        .get(0).getRowLength(), row3, 0, row3.length));
6419    assertTrue(hasNext);
6420    // 3. scan out "row2" (4 kvs)
6421    // "row2" takes 2 next() calls since batch=3
6422    currRow.clear();
6423    hasNext = scanner.next(currRow);
6424    assertEquals(3, currRow.size());
6425    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6426        .get(0).getRowLength(), row2, 0, row2.length));
6427    assertTrue(hasNext);
6428    currRow.clear();
6429    hasNext = scanner.next(currRow);
6430    assertEquals(1, currRow.size());
6431    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6432        .get(0).getRowLength(), row2, 0, row2.length));
6433    assertTrue(hasNext);
6434    // 4. scan out "row1" (2 kv)
6435    currRow.clear();
6436    hasNext = scanner.next(currRow);
6437    assertEquals(2, currRow.size());
6438    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6439        .get(0).getRowLength(), row1, 0, row1.length));
6440    assertTrue(hasNext);
6441    // 5. scan out "row0" (1 kv)
6442    currRow.clear();
6443    hasNext = scanner.next(currRow);
6444    assertEquals(1, currRow.size());
6445    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6446        .get(0).getRowLength(), row0, 0, row0.length));
6447    assertFalse(hasNext);
6448
6449    scanner.close();
6450  }
6451
6452  @Test
6453  public void testReverseScanner_FromMemStoreAndHFiles_MultiCFs2()
6454      throws IOException {
6455    byte[] row1 = Bytes.toBytes("row1");
6456    byte[] row2 = Bytes.toBytes("row2");
6457    byte[] row3 = Bytes.toBytes("row3");
6458    byte[] row4 = Bytes.toBytes("row4");
6459    byte[] cf1 = Bytes.toBytes("CF1");
6460    byte[] cf2 = Bytes.toBytes("CF2");
6461    byte[] cf3 = Bytes.toBytes("CF3");
6462    byte[] cf4 = Bytes.toBytes("CF4");
6463    byte[][] families = { cf1, cf2, cf3, cf4 };
6464    byte[] col = Bytes.toBytes("C");
6465    long ts = 1;
6466    Configuration conf = new Configuration(CONF);
6467    // disable compactions in this test.
6468    conf.setInt("hbase.hstore.compactionThreshold", 10000);
6469    this.region = initHRegion(tableName, method, conf, families);
6470    KeyValue kv1 = new KeyValue(row1, cf1, col, ts, KeyValue.Type.Put, null);
6471    KeyValue kv2 = new KeyValue(row2, cf2, col, ts, KeyValue.Type.Put, null);
6472    KeyValue kv3 = new KeyValue(row3, cf3, col, ts, KeyValue.Type.Put, null);
6473    KeyValue kv4 = new KeyValue(row4, cf4, col, ts, KeyValue.Type.Put, null);
6474    // storefile1
6475    Put put = new Put(row1);
6476    put.add(kv1);
6477    region.put(put);
6478    region.flush(true);
6479    // storefile2
6480    put = new Put(row2);
6481    put.add(kv2);
6482    region.put(put);
6483    region.flush(true);
6484    // storefile3
6485    put = new Put(row3);
6486    put.add(kv3);
6487    region.put(put);
6488    region.flush(true);
6489    // memstore
6490    put = new Put(row4);
6491    put.add(kv4);
6492    region.put(put);
6493    // scan range = ["row4", min)
6494    Scan scan = new Scan().withStartRow(row4);
6495    scan.setReversed(true);
6496    scan.setBatch(10);
6497    InternalScanner scanner = region.getScanner(scan);
6498    List<Cell> currRow = new ArrayList<>();
6499    boolean hasNext = scanner.next(currRow);
6500    assertEquals(1, currRow.size());
6501    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6502        .get(0).getRowLength(), row4, 0, row4.length));
6503    assertTrue(hasNext);
6504    currRow.clear();
6505    hasNext = scanner.next(currRow);
6506    assertEquals(1, currRow.size());
6507    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6508        .get(0).getRowLength(), row3, 0, row3.length));
6509    assertTrue(hasNext);
6510    currRow.clear();
6511    hasNext = scanner.next(currRow);
6512    assertEquals(1, currRow.size());
6513    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6514        .get(0).getRowLength(), row2, 0, row2.length));
6515    assertTrue(hasNext);
6516    currRow.clear();
6517    hasNext = scanner.next(currRow);
6518    assertEquals(1, currRow.size());
6519    assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), currRow
6520        .get(0).getRowLength(), row1, 0, row1.length));
6521    assertFalse(hasNext);
6522  }
6523
6524  /**
6525   * Test for HBASE-14497: Reverse Scan threw StackOverflow caused by readPt checking
6526   */
6527  @Test
6528  public void testReverseScanner_StackOverflow() throws IOException {
6529    byte[] cf1 = Bytes.toBytes("CF1");
6530    byte[][] families = {cf1};
6531    byte[] col = Bytes.toBytes("C");
6532    Configuration conf = new Configuration(CONF);
6533    this.region = initHRegion(tableName, method, conf, families);
6534    // setup with one storefile and one memstore, to create scanner and get an earlier readPt
6535    Put put = new Put(Bytes.toBytes("19998"));
6536    put.addColumn(cf1, col, Bytes.toBytes("val"));
6537    region.put(put);
6538    region.flushcache(true, true, FlushLifeCycleTracker.DUMMY);
6539    Put put2 = new Put(Bytes.toBytes("19997"));
6540    put2.addColumn(cf1, col, Bytes.toBytes("val"));
6541    region.put(put2);
6542
6543    Scan scan = new Scan().withStartRow(Bytes.toBytes("19998"));
6544    scan.setReversed(true);
6545    InternalScanner scanner = region.getScanner(scan);
6546
6547    // create one storefile contains many rows will be skipped
6548    // to check StoreFileScanner.seekToPreviousRow
6549    for (int i = 10000; i < 20000; i++) {
6550      Put p = new Put(Bytes.toBytes(""+i));
6551      p.addColumn(cf1, col, Bytes.toBytes("" + i));
6552      region.put(p);
6553    }
6554    region.flushcache(true, true, FlushLifeCycleTracker.DUMMY);
6555
6556    // create one memstore contains many rows will be skipped
6557    // to check MemStoreScanner.seekToPreviousRow
6558    for (int i = 10000; i < 20000; i++) {
6559      Put p = new Put(Bytes.toBytes(""+i));
6560      p.addColumn(cf1, col, Bytes.toBytes("" + i));
6561      region.put(p);
6562    }
6563
6564    List<Cell> currRow = new ArrayList<>();
6565    boolean hasNext;
6566    do {
6567      hasNext = scanner.next(currRow);
6568    } while (hasNext);
6569    assertEquals(2, currRow.size());
6570    assertEquals("19998", Bytes.toString(currRow.get(0).getRowArray(),
6571      currRow.get(0).getRowOffset(), currRow.get(0).getRowLength()));
6572    assertEquals("19997", Bytes.toString(currRow.get(1).getRowArray(),
6573      currRow.get(1).getRowOffset(), currRow.get(1).getRowLength()));
6574  }
6575
6576  @Test
6577  public void testReverseScanShouldNotScanMemstoreIfReadPtLesser() throws Exception {
6578    byte[] cf1 = Bytes.toBytes("CF1");
6579    byte[][] families = { cf1 };
6580    byte[] col = Bytes.toBytes("C");
6581    this.region = initHRegion(tableName, method, CONF, families);
6582    // setup with one storefile and one memstore, to create scanner and get an earlier readPt
6583    Put put = new Put(Bytes.toBytes("19996"));
6584    put.addColumn(cf1, col, Bytes.toBytes("val"));
6585    region.put(put);
6586    Put put2 = new Put(Bytes.toBytes("19995"));
6587    put2.addColumn(cf1, col, Bytes.toBytes("val"));
6588    region.put(put2);
6589    // create a reverse scan
6590    Scan scan = new Scan().withStartRow(Bytes.toBytes("19996"));
6591    scan.setReversed(true);
6592    RegionScannerImpl scanner = region.getScanner(scan);
6593
6594    // flush the cache. This will reset the store scanner
6595    region.flushcache(true, true, FlushLifeCycleTracker.DUMMY);
6596
6597    // create one memstore contains many rows will be skipped
6598    // to check MemStoreScanner.seekToPreviousRow
6599    for (int i = 10000; i < 20000; i++) {
6600      Put p = new Put(Bytes.toBytes("" + i));
6601      p.addColumn(cf1, col, Bytes.toBytes("" + i));
6602      region.put(p);
6603    }
6604    List<Cell> currRow = new ArrayList<>();
6605    boolean hasNext;
6606    boolean assertDone = false;
6607    do {
6608      hasNext = scanner.next(currRow);
6609      // With HBASE-15871, after the scanner is reset the memstore scanner should not be
6610      // added here
6611      if (!assertDone) {
6612        StoreScanner current =
6613            (StoreScanner) (scanner.storeHeap).getCurrentForTesting();
6614        List<KeyValueScanner> scanners = current.getAllScannersForTesting();
6615        assertEquals("There should be only one scanner the store file scanner", 1,
6616          scanners.size());
6617        assertDone = true;
6618      }
6619    } while (hasNext);
6620    assertEquals(2, currRow.size());
6621    assertEquals("19996", Bytes.toString(currRow.get(0).getRowArray(),
6622      currRow.get(0).getRowOffset(), currRow.get(0).getRowLength()));
6623    assertEquals("19995", Bytes.toString(currRow.get(1).getRowArray(),
6624      currRow.get(1).getRowOffset(), currRow.get(1).getRowLength()));
6625  }
6626
6627  @Test
6628  public void testReverseScanWhenPutCellsAfterOpenReverseScan() throws Exception {
6629    byte[] cf1 = Bytes.toBytes("CF1");
6630    byte[][] families = { cf1 };
6631    byte[] col = Bytes.toBytes("C");
6632
6633    this.region = initHRegion(tableName, method, CONF, families);
6634
6635    Put put = new Put(Bytes.toBytes("199996"));
6636    put.addColumn(cf1, col, Bytes.toBytes("val"));
6637    region.put(put);
6638    Put put2 = new Put(Bytes.toBytes("199995"));
6639    put2.addColumn(cf1, col, Bytes.toBytes("val"));
6640    region.put(put2);
6641
6642    // Create a reverse scan
6643    Scan scan = new Scan().withStartRow(Bytes.toBytes("199996"));
6644    scan.setReversed(true);
6645    RegionScannerImpl scanner = region.getScanner(scan);
6646
6647    // Put a lot of cells that have sequenceIDs grater than the readPt of the reverse scan
6648    for (int i = 100000; i < 200000; i++) {
6649      Put p = new Put(Bytes.toBytes("" + i));
6650      p.addColumn(cf1, col, Bytes.toBytes("" + i));
6651      region.put(p);
6652    }
6653    List<Cell> currRow = new ArrayList<>();
6654    boolean hasNext;
6655    do {
6656      hasNext = scanner.next(currRow);
6657    } while (hasNext);
6658
6659    assertEquals(2, currRow.size());
6660    assertEquals("199996", Bytes.toString(currRow.get(0).getRowArray(),
6661      currRow.get(0).getRowOffset(), currRow.get(0).getRowLength()));
6662    assertEquals("199995", Bytes.toString(currRow.get(1).getRowArray(),
6663      currRow.get(1).getRowOffset(), currRow.get(1).getRowLength()));
6664  }
6665
6666  @Test
6667  public void testWriteRequestsCounter() throws IOException {
6668    byte[] fam = Bytes.toBytes("info");
6669    byte[][] families = { fam };
6670    this.region = initHRegion(tableName, method, CONF, families);
6671
6672    Assert.assertEquals(0L, region.getWriteRequestsCount());
6673
6674    Put put = new Put(row);
6675    put.addColumn(fam, fam, fam);
6676
6677    Assert.assertEquals(0L, region.getWriteRequestsCount());
6678    region.put(put);
6679    Assert.assertEquals(1L, region.getWriteRequestsCount());
6680    region.put(put);
6681    Assert.assertEquals(2L, region.getWriteRequestsCount());
6682    region.put(put);
6683    Assert.assertEquals(3L, region.getWriteRequestsCount());
6684
6685    region.delete(new Delete(row));
6686    Assert.assertEquals(4L, region.getWriteRequestsCount());
6687  }
6688
6689  @Test
6690  public void testOpenRegionWrittenToWAL() throws Exception {
6691    final ServerName serverName = ServerName.valueOf(name.getMethodName(), 100, 42);
6692    final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName));
6693
6694    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
6695      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1))
6696      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam2)).build();
6697    RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).build();
6698
6699    // open the region w/o rss and wal and flush some files
6700    region =
6701         HBaseTestingUtil.createRegionAndWAL(hri, TEST_UTIL.getDataTestDir(), TEST_UTIL
6702             .getConfiguration(), htd);
6703    assertNotNull(region);
6704
6705    // create a file in fam1 for the region before opening in OpenRegionHandler
6706    region.put(new Put(Bytes.toBytes("a")).addColumn(fam1, fam1, fam1));
6707    region.flush(true);
6708    HBaseTestingUtil.closeRegionAndWAL(region);
6709
6710    ArgumentCaptor<WALEdit> editCaptor = ArgumentCaptor.forClass(WALEdit.class);
6711
6712    // capture append() calls
6713    WAL wal = mockWAL();
6714    when(rss.getWAL(any(RegionInfo.class))).thenReturn(wal);
6715
6716    region = HRegion.openHRegion(hri, htd, rss.getWAL(hri),
6717      TEST_UTIL.getConfiguration(), rss, null);
6718
6719    verify(wal, times(1)).appendMarker(any(RegionInfo.class), any(WALKeyImpl.class),
6720      editCaptor.capture());
6721
6722    WALEdit edit = editCaptor.getValue();
6723    assertNotNull(edit);
6724    assertNotNull(edit.getCells());
6725    assertEquals(1, edit.getCells().size());
6726    RegionEventDescriptor desc = WALEdit.getRegionEventDescriptor(edit.getCells().get(0));
6727    assertNotNull(desc);
6728
6729    LOG.info("RegionEventDescriptor from WAL: " + desc);
6730
6731    assertEquals(RegionEventDescriptor.EventType.REGION_OPEN, desc.getEventType());
6732    assertTrue(Bytes.equals(desc.getTableName().toByteArray(), htd.getTableName().toBytes()));
6733    assertTrue(Bytes.equals(desc.getEncodedRegionName().toByteArray(),
6734      hri.getEncodedNameAsBytes()));
6735    assertTrue(desc.getLogSequenceNumber() > 0);
6736    assertEquals(serverName, ProtobufUtil.toServerName(desc.getServer()));
6737    assertEquals(2, desc.getStoresCount());
6738
6739    StoreDescriptor store = desc.getStores(0);
6740    assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam1));
6741    assertEquals(store.getStoreHomeDir(), Bytes.toString(fam1));
6742    assertEquals(1, store.getStoreFileCount()); // 1store file
6743    assertFalse(store.getStoreFile(0).contains("/")); // ensure path is relative
6744
6745    store = desc.getStores(1);
6746    assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam2));
6747    assertEquals(store.getStoreHomeDir(), Bytes.toString(fam2));
6748    assertEquals(0, store.getStoreFileCount()); // no store files
6749  }
6750
6751  // Helper for test testOpenRegionWrittenToWALForLogReplay
6752  static class HRegionWithSeqId extends HRegion {
6753    public HRegionWithSeqId(final Path tableDir, final WAL wal, final FileSystem fs,
6754        final Configuration confParam, final RegionInfo regionInfo,
6755        final TableDescriptor htd, final RegionServerServices rsServices) {
6756      super(tableDir, wal, fs, confParam, regionInfo, htd, rsServices);
6757    }
6758    @Override
6759    protected long getNextSequenceId(WAL wal) throws IOException {
6760      return 42;
6761    }
6762  }
6763
6764  @Test
6765  public void testFlushedFileWithNoTags() throws Exception {
6766    final TableName tableName = TableName.valueOf(name.getMethodName());
6767    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
6768      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)).build();
6769    RegionInfo info = RegionInfoBuilder.newBuilder(tableName).build();
6770    Path path = TEST_UTIL.getDataTestDir(getClass().getSimpleName());
6771    region = HBaseTestingUtil.createRegionAndWAL(info, path,
6772      TEST_UTIL.getConfiguration(), tableDescriptor);
6773    Put put = new Put(Bytes.toBytes("a-b-0-0"));
6774    put.addColumn(fam1, qual1, Bytes.toBytes("c1-value"));
6775    region.put(put);
6776    region.flush(true);
6777    HStore store = region.getStore(fam1);
6778    Collection<HStoreFile> storefiles = store.getStorefiles();
6779    for (HStoreFile sf : storefiles) {
6780      assertFalse("Tags should not be present "
6781          ,sf.getReader().getHFileReader().getFileContext().isIncludesTags());
6782    }
6783  }
6784
6785  /**
6786   * Utility method to setup a WAL mock.
6787   * <p/>
6788   * Needs to do the bit where we close latch on the WALKeyImpl on append else test hangs.
6789   * @return a mock WAL
6790   */
6791  private WAL mockWAL() throws IOException {
6792    WAL wal = mock(WAL.class);
6793    when(wal.appendData(any(RegionInfo.class), any(WALKeyImpl.class), any(WALEdit.class)))
6794      .thenAnswer(new Answer<Long>() {
6795        @Override
6796        public Long answer(InvocationOnMock invocation) throws Throwable {
6797          WALKeyImpl key = invocation.getArgument(1);
6798          MultiVersionConcurrencyControl.WriteEntry we = key.getMvcc().begin();
6799          key.setWriteEntry(we);
6800          return 1L;
6801        }
6802      });
6803    when(wal.appendMarker(any(RegionInfo.class), any(WALKeyImpl.class), any(WALEdit.class))).
6804        thenAnswer(new Answer<Long>() {
6805          @Override
6806          public Long answer(InvocationOnMock invocation) throws Throwable {
6807            WALKeyImpl key = invocation.getArgument(1);
6808            MultiVersionConcurrencyControl.WriteEntry we = key.getMvcc().begin();
6809            key.setWriteEntry(we);
6810            return 1L;
6811          }
6812        });
6813    return wal;
6814  }
6815
6816  @Test
6817  public void testCloseRegionWrittenToWAL() throws Exception {
6818    Path rootDir = new Path(dir + name.getMethodName());
6819    CommonFSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir);
6820
6821    final ServerName serverName = ServerName.valueOf("testCloseRegionWrittenToWAL", 100, 42);
6822    final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName));
6823
6824    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
6825      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1))
6826      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam2)).build();
6827    RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).build();
6828
6829    ArgumentCaptor<WALEdit> editCaptor = ArgumentCaptor.forClass(WALEdit.class);
6830
6831    // capture append() calls
6832    WAL wal = mockWAL();
6833    when(rss.getWAL(any(RegionInfo.class))).thenReturn(wal);
6834
6835
6836    // create and then open a region first so that it can be closed later
6837    region = HRegion.createHRegion(hri, rootDir, TEST_UTIL.getConfiguration(), htd, rss.getWAL(hri));
6838    region = HRegion.openHRegion(hri, htd, rss.getWAL(hri),
6839      TEST_UTIL.getConfiguration(), rss, null);
6840
6841    // close the region
6842    region.close(false);
6843
6844    // 2 times, one for region open, the other close region
6845    verify(wal, times(2)).appendMarker(any(RegionInfo.class),
6846        (WALKeyImpl) any(WALKeyImpl.class), editCaptor.capture());
6847
6848    WALEdit edit = editCaptor.getAllValues().get(1);
6849    assertNotNull(edit);
6850    assertNotNull(edit.getCells());
6851    assertEquals(1, edit.getCells().size());
6852    RegionEventDescriptor desc = WALEdit.getRegionEventDescriptor(edit.getCells().get(0));
6853    assertNotNull(desc);
6854
6855    LOG.info("RegionEventDescriptor from WAL: " + desc);
6856
6857    assertEquals(RegionEventDescriptor.EventType.REGION_CLOSE, desc.getEventType());
6858    assertTrue(Bytes.equals(desc.getTableName().toByteArray(), htd.getTableName().toBytes()));
6859    assertTrue(Bytes.equals(desc.getEncodedRegionName().toByteArray(),
6860      hri.getEncodedNameAsBytes()));
6861    assertTrue(desc.getLogSequenceNumber() > 0);
6862    assertEquals(serverName, ProtobufUtil.toServerName(desc.getServer()));
6863    assertEquals(2, desc.getStoresCount());
6864
6865    StoreDescriptor store = desc.getStores(0);
6866    assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam1));
6867    assertEquals(store.getStoreHomeDir(), Bytes.toString(fam1));
6868    assertEquals(0, store.getStoreFileCount()); // no store files
6869
6870    store = desc.getStores(1);
6871    assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam2));
6872    assertEquals(store.getStoreHomeDir(), Bytes.toString(fam2));
6873    assertEquals(0, store.getStoreFileCount()); // no store files
6874  }
6875
6876  /**
6877   * Test RegionTooBusyException thrown when region is busy
6878   */
6879  @Test
6880  public void testRegionTooBusy() throws IOException {
6881    byte[] family = Bytes.toBytes("family");
6882    long defaultBusyWaitDuration = CONF.getLong("hbase.busy.wait.duration",
6883      HRegion.DEFAULT_BUSY_WAIT_DURATION);
6884    CONF.setLong("hbase.busy.wait.duration", 1000);
6885    region = initHRegion(tableName, method, CONF, family);
6886    final AtomicBoolean stopped = new AtomicBoolean(true);
6887    Thread t = new Thread(new Runnable() {
6888      @Override
6889      public void run() {
6890        try {
6891          region.lock.writeLock().lock();
6892          stopped.set(false);
6893          while (!stopped.get()) {
6894            Thread.sleep(100);
6895          }
6896        } catch (InterruptedException ie) {
6897        } finally {
6898          region.lock.writeLock().unlock();
6899        }
6900      }
6901    });
6902    t.start();
6903    Get get = new Get(row);
6904    try {
6905      while (stopped.get()) {
6906        Thread.sleep(100);
6907      }
6908      region.get(get);
6909      fail("Should throw RegionTooBusyException");
6910    } catch (InterruptedException ie) {
6911      fail("test interrupted");
6912    } catch (RegionTooBusyException e) {
6913      // Good, expected
6914    } finally {
6915      stopped.set(true);
6916      try {
6917        t.join();
6918      } catch (Throwable e) {
6919      }
6920
6921      HBaseTestingUtil.closeRegionAndWAL(region);
6922      region = null;
6923      CONF.setLong("hbase.busy.wait.duration", defaultBusyWaitDuration);
6924    }
6925  }
6926
6927  @Test
6928  public void testCellTTLs() throws IOException {
6929    IncrementingEnvironmentEdge edge = new IncrementingEnvironmentEdge();
6930    EnvironmentEdgeManager.injectEdge(edge);
6931
6932    final byte[] row = Bytes.toBytes("testRow");
6933    final byte[] q1 = Bytes.toBytes("q1");
6934    final byte[] q2 = Bytes.toBytes("q2");
6935    final byte[] q3 = Bytes.toBytes("q3");
6936    final byte[] q4 = Bytes.toBytes("q4");
6937
6938    // 10 seconds
6939    TableDescriptor tableDescriptor =
6940      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
6941        .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(fam1).setTimeToLive(10).build())
6942        .build();
6943
6944    Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
6945    conf.setInt(HFile.FORMAT_VERSION_KEY, HFile.MIN_FORMAT_VERSION_WITH_TAGS);
6946
6947    region = HBaseTestingUtil.createRegionAndWAL(
6948      RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(),
6949      TEST_UTIL.getDataTestDir(), conf, tableDescriptor);
6950    assertNotNull(region);
6951    long now = EnvironmentEdgeManager.currentTime();
6952    // Add a cell that will expire in 5 seconds via cell TTL
6953    region.put(new Put(row).add(new KeyValue(row, fam1, q1, now,
6954      HConstants.EMPTY_BYTE_ARRAY, new ArrayBackedTag[] {
6955        // TTL tags specify ts in milliseconds
6956        new ArrayBackedTag(TagType.TTL_TAG_TYPE, Bytes.toBytes(5000L)) })));
6957    // Add a cell that will expire after 10 seconds via family setting
6958    region.put(new Put(row).addColumn(fam1, q2, now, HConstants.EMPTY_BYTE_ARRAY));
6959    // Add a cell that will expire in 15 seconds via cell TTL
6960    region.put(new Put(row).add(new KeyValue(row, fam1, q3, now + 10000 - 1,
6961      HConstants.EMPTY_BYTE_ARRAY, new ArrayBackedTag[] {
6962        // TTL tags specify ts in milliseconds
6963        new ArrayBackedTag(TagType.TTL_TAG_TYPE, Bytes.toBytes(5000L)) })));
6964    // Add a cell that will expire in 20 seconds via family setting
6965    region.put(new Put(row).addColumn(fam1, q4, now + 10000 - 1, HConstants.EMPTY_BYTE_ARRAY));
6966
6967    // Flush so we are sure store scanning gets this right
6968    region.flush(true);
6969
6970    // A query at time T+0 should return all cells
6971    Result r = region.get(new Get(row));
6972    assertNotNull(r.getValue(fam1, q1));
6973    assertNotNull(r.getValue(fam1, q2));
6974    assertNotNull(r.getValue(fam1, q3));
6975    assertNotNull(r.getValue(fam1, q4));
6976
6977    // Increment time to T+5 seconds
6978    edge.incrementTime(5000);
6979
6980    r = region.get(new Get(row));
6981    assertNull(r.getValue(fam1, q1));
6982    assertNotNull(r.getValue(fam1, q2));
6983    assertNotNull(r.getValue(fam1, q3));
6984    assertNotNull(r.getValue(fam1, q4));
6985
6986    // Increment time to T+10 seconds
6987    edge.incrementTime(5000);
6988
6989    r = region.get(new Get(row));
6990    assertNull(r.getValue(fam1, q1));
6991    assertNull(r.getValue(fam1, q2));
6992    assertNotNull(r.getValue(fam1, q3));
6993    assertNotNull(r.getValue(fam1, q4));
6994
6995    // Increment time to T+15 seconds
6996    edge.incrementTime(5000);
6997
6998    r = region.get(new Get(row));
6999    assertNull(r.getValue(fam1, q1));
7000    assertNull(r.getValue(fam1, q2));
7001    assertNull(r.getValue(fam1, q3));
7002    assertNotNull(r.getValue(fam1, q4));
7003
7004    // Increment time to T+20 seconds
7005    edge.incrementTime(10000);
7006
7007    r = region.get(new Get(row));
7008    assertNull(r.getValue(fam1, q1));
7009    assertNull(r.getValue(fam1, q2));
7010    assertNull(r.getValue(fam1, q3));
7011    assertNull(r.getValue(fam1, q4));
7012
7013    // Fun with disappearing increments
7014
7015    // Start at 1
7016    region.put(new Put(row).addColumn(fam1, q1, Bytes.toBytes(1L)));
7017    r = region.get(new Get(row));
7018    byte[] val = r.getValue(fam1, q1);
7019    assertNotNull(val);
7020    assertEquals(1L, Bytes.toLong(val));
7021
7022    // Increment with a TTL of 5 seconds
7023    Increment incr = new Increment(row).addColumn(fam1, q1, 1L);
7024    incr.setTTL(5000);
7025    region.increment(incr); // 2
7026
7027    // New value should be 2
7028    r = region.get(new Get(row));
7029    val = r.getValue(fam1, q1);
7030    assertNotNull(val);
7031    assertEquals(2L, Bytes.toLong(val));
7032
7033    // Increment time to T+25 seconds
7034    edge.incrementTime(5000);
7035
7036    // Value should be back to 1
7037    r = region.get(new Get(row));
7038    val = r.getValue(fam1, q1);
7039    assertNotNull(val);
7040    assertEquals(1L, Bytes.toLong(val));
7041
7042    // Increment time to T+30 seconds
7043    edge.incrementTime(5000);
7044
7045    // Original value written at T+20 should be gone now via family TTL
7046    r = region.get(new Get(row));
7047    assertNull(r.getValue(fam1, q1));
7048  }
7049
7050  @Test
7051  public void testIncrementTimestampsAreMonotonic() throws IOException {
7052    region = initHRegion(tableName, method, CONF, fam1);
7053    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
7054    EnvironmentEdgeManager.injectEdge(edge);
7055
7056    edge.setValue(10);
7057    Increment inc = new Increment(row);
7058    inc.setDurability(Durability.SKIP_WAL);
7059    inc.addColumn(fam1, qual1, 1L);
7060    region.increment(inc);
7061
7062    Result result = region.get(new Get(row));
7063    Cell c = result.getColumnLatestCell(fam1, qual1);
7064    assertNotNull(c);
7065    assertEquals(10L, c.getTimestamp());
7066
7067    edge.setValue(1); // clock goes back
7068    region.increment(inc);
7069    result = region.get(new Get(row));
7070    c = result.getColumnLatestCell(fam1, qual1);
7071    assertEquals(11L, c.getTimestamp());
7072    assertEquals(2L, Bytes.toLong(c.getValueArray(), c.getValueOffset(), c.getValueLength()));
7073  }
7074
7075  @Test
7076  public void testAppendTimestampsAreMonotonic() throws IOException {
7077    region = initHRegion(tableName, method, CONF, fam1);
7078    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
7079    EnvironmentEdgeManager.injectEdge(edge);
7080
7081    edge.setValue(10);
7082    Append a = new Append(row);
7083    a.setDurability(Durability.SKIP_WAL);
7084    a.addColumn(fam1, qual1, qual1);
7085    region.append(a);
7086
7087    Result result = region.get(new Get(row));
7088    Cell c = result.getColumnLatestCell(fam1, qual1);
7089    assertNotNull(c);
7090    assertEquals(10L, c.getTimestamp());
7091
7092    edge.setValue(1); // clock goes back
7093    region.append(a);
7094    result = region.get(new Get(row));
7095    c = result.getColumnLatestCell(fam1, qual1);
7096    assertEquals(11L, c.getTimestamp());
7097
7098    byte[] expected = new byte[qual1.length*2];
7099    System.arraycopy(qual1, 0, expected, 0, qual1.length);
7100    System.arraycopy(qual1, 0, expected, qual1.length, qual1.length);
7101
7102    assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(),
7103      expected, 0, expected.length));
7104  }
7105
7106  @Test
7107  public void testCheckAndMutateTimestampsAreMonotonic() throws IOException {
7108    region = initHRegion(tableName, method, CONF, fam1);
7109    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
7110    EnvironmentEdgeManager.injectEdge(edge);
7111
7112    edge.setValue(10);
7113    Put p = new Put(row);
7114    p.setDurability(Durability.SKIP_WAL);
7115    p.addColumn(fam1, qual1, qual1);
7116    region.put(p);
7117
7118    Result result = region.get(new Get(row));
7119    Cell c = result.getColumnLatestCell(fam1, qual1);
7120    assertNotNull(c);
7121    assertEquals(10L, c.getTimestamp());
7122
7123    edge.setValue(1); // clock goes back
7124    p = new Put(row);
7125    p.setDurability(Durability.SKIP_WAL);
7126    p.addColumn(fam1, qual1, qual2);
7127    region.checkAndMutate(row, fam1, qual1, CompareOperator.EQUAL, new BinaryComparator(qual1), p);
7128    result = region.get(new Get(row));
7129    c = result.getColumnLatestCell(fam1, qual1);
7130    assertEquals(10L, c.getTimestamp());
7131
7132    assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(),
7133      qual2, 0, qual2.length));
7134  }
7135
7136  @Test
7137  public void testBatchMutateWithWrongRegionException() throws Exception {
7138    final byte[] a = Bytes.toBytes("a");
7139    final byte[] b = Bytes.toBytes("b");
7140    final byte[] c = Bytes.toBytes("c"); // exclusive
7141
7142    int prevLockTimeout = CONF.getInt("hbase.rowlock.wait.duration", 30000);
7143    CONF.setInt("hbase.rowlock.wait.duration", 1000);
7144    region = initHRegion(tableName, a, c, method, CONF, false, fam1);
7145
7146    Mutation[] mutations = new Mutation[] {
7147        new Put(a)
7148            .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7149              .setRow(a)
7150              .setFamily(fam1)
7151              .setTimestamp(HConstants.LATEST_TIMESTAMP)
7152              .setType(Cell.Type.Put)
7153              .build()),
7154        // this is outside the region boundary
7155        new Put(c).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7156              .setRow(c)
7157              .setFamily(fam1)
7158              .setTimestamp(HConstants.LATEST_TIMESTAMP)
7159              .setType(Type.Put)
7160              .build()),
7161        new Put(b).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7162              .setRow(b)
7163              .setFamily(fam1)
7164              .setTimestamp(HConstants.LATEST_TIMESTAMP)
7165              .setType(Cell.Type.Put)
7166              .build())
7167    };
7168
7169    OperationStatus[] status = region.batchMutate(mutations);
7170    assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode());
7171    assertEquals(OperationStatusCode.SANITY_CHECK_FAILURE, status[1].getOperationStatusCode());
7172    assertEquals(OperationStatusCode.SUCCESS, status[2].getOperationStatusCode());
7173
7174
7175    // test with a row lock held for a long time
7176    final CountDownLatch obtainedRowLock = new CountDownLatch(1);
7177    ExecutorService exec = Executors.newFixedThreadPool(2);
7178    Future<Void> f1 = exec.submit(new Callable<Void>() {
7179      @Override
7180      public Void call() throws Exception {
7181        LOG.info("Acquiring row lock");
7182        RowLock rl = region.getRowLock(b);
7183        obtainedRowLock.countDown();
7184        LOG.info("Waiting for 5 seconds before releasing lock");
7185        Threads.sleep(5000);
7186        LOG.info("Releasing row lock");
7187        rl.release();
7188        return null;
7189      }
7190    });
7191    obtainedRowLock.await(30, TimeUnit.SECONDS);
7192
7193    Future<Void> f2 = exec.submit(new Callable<Void>() {
7194      @Override
7195      public Void call() throws Exception {
7196        Mutation[] mutations = new Mutation[] {
7197            new Put(a).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7198                .setRow(a)
7199                .setFamily(fam1)
7200                .setTimestamp(HConstants.LATEST_TIMESTAMP)
7201                .setType(Cell.Type.Put)
7202                .build()),
7203            new Put(b).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7204                .setRow(b)
7205                .setFamily(fam1)
7206                .setTimestamp(HConstants.LATEST_TIMESTAMP)
7207                .setType(Cell.Type.Put)
7208                .build()),
7209        };
7210
7211        // this will wait for the row lock, and it will eventually succeed
7212        OperationStatus[] status = region.batchMutate(mutations);
7213        assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode());
7214        assertEquals(OperationStatusCode.SUCCESS, status[1].getOperationStatusCode());
7215        return null;
7216      }
7217    });
7218
7219    f1.get();
7220    f2.get();
7221
7222    CONF.setInt("hbase.rowlock.wait.duration", prevLockTimeout);
7223  }
7224
7225  @Test
7226  public void testBatchMutateWithZeroRowLockWait() throws Exception {
7227    final byte[] a = Bytes.toBytes("a");
7228    final byte[] b = Bytes.toBytes("b");
7229    final byte[] c = Bytes.toBytes("c"); // exclusive
7230
7231    Configuration conf = new Configuration(CONF);
7232    conf.setInt("hbase.rowlock.wait.duration", 0);
7233    final RegionInfo hri =
7234      RegionInfoBuilder.newBuilder(tableName).setStartKey(a).setEndKey(c).build();
7235    final TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
7236      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)).build();
7237    region = HRegion.createHRegion(hri, TEST_UTIL.getDataTestDir(), conf, htd,
7238      HBaseTestingUtil.createWal(conf, TEST_UTIL.getDataTestDirOnTestFS(method + ".log"), hri));
7239
7240    Mutation[] mutations = new Mutation[] {
7241        new Put(a)
7242            .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7243              .setRow(a)
7244              .setFamily(fam1)
7245              .setTimestamp(HConstants.LATEST_TIMESTAMP)
7246              .setType(Cell.Type.Put)
7247              .build()),
7248        new Put(b).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7249              .setRow(b)
7250              .setFamily(fam1)
7251              .setTimestamp(HConstants.LATEST_TIMESTAMP)
7252              .setType(Cell.Type.Put)
7253              .build())
7254    };
7255
7256    OperationStatus[] status = region.batchMutate(mutations);
7257    assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode());
7258    assertEquals(OperationStatusCode.SUCCESS, status[1].getOperationStatusCode());
7259
7260
7261    // test with a row lock held for a long time
7262    final CountDownLatch obtainedRowLock = new CountDownLatch(1);
7263    ExecutorService exec = Executors.newFixedThreadPool(2);
7264    Future<Void> f1 = exec.submit(new Callable<Void>() {
7265      @Override
7266      public Void call() throws Exception {
7267        LOG.info("Acquiring row lock");
7268        RowLock rl = region.getRowLock(b);
7269        obtainedRowLock.countDown();
7270        LOG.info("Waiting for 5 seconds before releasing lock");
7271        Threads.sleep(5000);
7272        LOG.info("Releasing row lock");
7273        rl.release();
7274        return null;
7275      }
7276    });
7277    obtainedRowLock.await(30, TimeUnit.SECONDS);
7278
7279    Future<Void> f2 = exec.submit(new Callable<Void>() {
7280      @Override
7281      public Void call() throws Exception {
7282        Mutation[] mutations = new Mutation[] {
7283            new Put(a).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7284                .setRow(a)
7285                .setFamily(fam1)
7286                .setTimestamp(HConstants.LATEST_TIMESTAMP)
7287                .setType(Cell.Type.Put)
7288                .build()),
7289            new Put(b).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
7290                .setRow(b)
7291                .setFamily(fam1)
7292                .setTimestamp(HConstants.LATEST_TIMESTAMP)
7293                .setType(Cell.Type.Put)
7294                .build()),
7295        };
7296        // when handling row b we are going to spin on the failure to get the row lock
7297        // until the lock above is released, but we will still succeed so long as that
7298        // takes less time then the test time out.
7299        OperationStatus[] status = region.batchMutate(mutations);
7300        assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode());
7301        assertEquals(OperationStatusCode.SUCCESS, status[1].getOperationStatusCode());
7302        return null;
7303      }
7304    });
7305
7306    f1.get();
7307    f2.get();
7308  }
7309
7310  @Test
7311  public void testCheckAndRowMutateTimestampsAreMonotonic() throws IOException {
7312    region = initHRegion(tableName, method, CONF, fam1);
7313    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
7314    EnvironmentEdgeManager.injectEdge(edge);
7315
7316    edge.setValue(10);
7317    Put p = new Put(row);
7318    p.setDurability(Durability.SKIP_WAL);
7319    p.addColumn(fam1, qual1, qual1);
7320    region.put(p);
7321
7322    Result result = region.get(new Get(row));
7323    Cell c = result.getColumnLatestCell(fam1, qual1);
7324    assertNotNull(c);
7325    assertEquals(10L, c.getTimestamp());
7326
7327    edge.setValue(1); // clock goes back
7328    p = new Put(row);
7329    p.setDurability(Durability.SKIP_WAL);
7330    p.addColumn(fam1, qual1, qual2);
7331    RowMutations rm = new RowMutations(row);
7332    rm.add(p);
7333    assertTrue(region.checkAndRowMutate(row, fam1, qual1, CompareOperator.EQUAL,
7334        new BinaryComparator(qual1), rm));
7335    result = region.get(new Get(row));
7336    c = result.getColumnLatestCell(fam1, qual1);
7337    assertEquals(10L, c.getTimestamp());
7338    LOG.info("c value " +
7339      Bytes.toStringBinary(c.getValueArray(), c.getValueOffset(), c.getValueLength()));
7340
7341    assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(),
7342      qual2, 0, qual2.length));
7343  }
7344
7345  HRegion initHRegion(TableName tableName, String callingMethod,
7346      byte[]... families) throws IOException {
7347    return initHRegion(tableName, callingMethod, HBaseConfiguration.create(),
7348        families);
7349  }
7350
7351  /**
7352   * HBASE-16429 Make sure no stuck if roll writer when ring buffer is filled with appends
7353   * @throws IOException if IO error occurred during test
7354   */
7355  @Test
7356  public void testWritesWhileRollWriter() throws IOException {
7357    int testCount = 10;
7358    int numRows = 1024;
7359    int numFamilies = 2;
7360    int numQualifiers = 2;
7361    final byte[][] families = new byte[numFamilies][];
7362    for (int i = 0; i < numFamilies; i++) {
7363      families[i] = Bytes.toBytes("family" + i);
7364    }
7365    final byte[][] qualifiers = new byte[numQualifiers][];
7366    for (int i = 0; i < numQualifiers; i++) {
7367      qualifiers[i] = Bytes.toBytes("qual" + i);
7368    }
7369
7370    CONF.setInt("hbase.regionserver.wal.disruptor.event.count", 2);
7371    this.region = initHRegion(tableName, method, CONF, families);
7372    try {
7373      List<Thread> threads = new ArrayList<>();
7374      for (int i = 0; i < numRows; i++) {
7375        final int count = i;
7376        Thread t = new Thread(new Runnable() {
7377
7378          @Override
7379          public void run() {
7380            byte[] row = Bytes.toBytes("row" + count);
7381            Put put = new Put(row);
7382            put.setDurability(Durability.SYNC_WAL);
7383            byte[] value = Bytes.toBytes(String.valueOf(count));
7384            for (byte[] family : families) {
7385              for (byte[] qualifier : qualifiers) {
7386                put.addColumn(family, qualifier, count, value);
7387              }
7388            }
7389            try {
7390              region.put(put);
7391            } catch (IOException e) {
7392              throw new RuntimeException(e);
7393            }
7394          }
7395        });
7396        threads.add(t);
7397      }
7398      for (Thread t : threads) {
7399        t.start();
7400      }
7401
7402      for (int i = 0; i < testCount; i++) {
7403        region.getWAL().rollWriter();
7404        Thread.yield();
7405      }
7406    } finally {
7407      try {
7408        HBaseTestingUtil.closeRegionAndWAL(this.region);
7409        CONF.setInt("hbase.regionserver.wal.disruptor.event.count", 16 * 1024);
7410      } catch (DroppedSnapshotException dse) {
7411        // We could get this on way out because we interrupt the background flusher and it could
7412        // fail anywhere causing a DSE over in the background flusher... only it is not properly
7413        // dealt with so could still be memory hanging out when we get to here -- memory we can't
7414        // flush because the accounting is 'off' since original DSE.
7415      }
7416      this.region = null;
7417    }
7418  }
7419
7420  @Test
7421  public void testMutateRow() throws Exception {
7422    final byte[] row = Bytes.toBytes("row");
7423    final byte[] q1 = Bytes.toBytes("q1");
7424    final byte[] q2 = Bytes.toBytes("q2");
7425    final byte[] q3 = Bytes.toBytes("q3");
7426    final byte[] q4 = Bytes.toBytes("q4");
7427    final String v1 = "v1";
7428
7429    region = initHRegion(tableName, method, CONF, fam1);
7430
7431    // Initial values
7432    region.batchMutate(new Mutation[] {
7433      new Put(row).addColumn(fam1, q2, Bytes.toBytes("toBeDeleted")),
7434      new Put(row).addColumn(fam1, q3, Bytes.toBytes(5L)),
7435      new Put(row).addColumn(fam1, q4, Bytes.toBytes("a")),
7436    });
7437
7438    // Do mutateRow
7439    Result result = region.mutateRow(new RowMutations(row).add(Arrays.asList(
7440      new Put(row).addColumn(fam1, q1, Bytes.toBytes(v1)),
7441      new Delete(row).addColumns(fam1, q2),
7442      new Increment(row).addColumn(fam1, q3, 1),
7443      new Append(row).addColumn(fam1, q4, Bytes.toBytes("b")))));
7444
7445    assertNotNull(result);
7446    assertEquals(6L, Bytes.toLong(result.getValue(fam1, q3)));
7447    assertEquals("ab", Bytes.toString(result.getValue(fam1, q4)));
7448
7449    // Verify the value
7450    result = region.get(new Get(row));
7451    assertEquals(v1, Bytes.toString(result.getValue(fam1, q1)));
7452    assertNull(result.getValue(fam1, q2));
7453    assertEquals(6L, Bytes.toLong(result.getValue(fam1, q3)));
7454    assertEquals("ab", Bytes.toString(result.getValue(fam1, q4)));
7455  }
7456
7457  @Test
7458  public void testMutateRowInParallel() throws Exception {
7459    final int numReaderThreads = 100;
7460    final CountDownLatch latch = new CountDownLatch(numReaderThreads);
7461
7462    final byte[] row = Bytes.toBytes("row");
7463    final byte[] q1 = Bytes.toBytes("q1");
7464    final byte[] q2 = Bytes.toBytes("q2");
7465    final byte[] q3 = Bytes.toBytes("q3");
7466    final byte[] q4 = Bytes.toBytes("q4");
7467    final String v1 = "v1";
7468    final String v2 = "v2";
7469
7470    // We need to ensure the timestamp of the delete operation is more than the previous one
7471    final AtomicLong deleteTimestamp = new AtomicLong();
7472
7473    region = initHRegion(tableName, method, CONF, fam1);
7474
7475    // Initial values
7476    region.batchMutate(new Mutation[] {
7477      new Put(row).addColumn(fam1, q1, Bytes.toBytes(v1))
7478        .addColumn(fam1, q2, deleteTimestamp.getAndIncrement(), Bytes.toBytes(v2))
7479        .addColumn(fam1, q3, Bytes.toBytes(1L))
7480        .addColumn(fam1, q4, Bytes.toBytes("a"))
7481    });
7482
7483    final AtomicReference<AssertionError> assertionError = new AtomicReference<>();
7484
7485    // Writer thread
7486    Thread writerThread = new Thread(() -> {
7487      try {
7488        while (true) {
7489          // If all the reader threads finish, then stop the writer thread
7490          if (latch.await(0, TimeUnit.MILLISECONDS)) {
7491            return;
7492          }
7493
7494          // Execute the mutations. This should be done atomically
7495          region.mutateRow(new RowMutations(row).add(Arrays.asList(
7496            new Put(row).addColumn(fam1, q1, Bytes.toBytes(v2)),
7497            new Delete(row).addColumns(fam1, q2, deleteTimestamp.getAndIncrement()),
7498            new Increment(row).addColumn(fam1, q3, 1L),
7499            new Append(row).addColumn(fam1, q4, Bytes.toBytes("b")))));
7500
7501          // We need to ensure the timestamps of the Increment/Append operations are more than the
7502          // previous ones
7503          Result result = region.get(new Get(row).addColumn(fam1, q3).addColumn(fam1, q4));
7504          long tsIncrement = result.getColumnLatestCell(fam1, q3).getTimestamp();
7505          long tsAppend = result.getColumnLatestCell(fam1, q4).getTimestamp();
7506
7507          // Put the initial values
7508          region.batchMutate(new Mutation[] {
7509            new Put(row).addColumn(fam1, q1, Bytes.toBytes(v1))
7510              .addColumn(fam1, q2, deleteTimestamp.getAndIncrement(), Bytes.toBytes(v2))
7511              .addColumn(fam1, q3, tsIncrement + 1, Bytes.toBytes(1L))
7512              .addColumn(fam1, q4, tsAppend + 1, Bytes.toBytes("a"))
7513          });
7514        }
7515      } catch (Exception e) {
7516        assertionError.set(new AssertionError(e));
7517      }
7518    });
7519    writerThread.start();
7520
7521    // Reader threads
7522    for (int i = 0; i < numReaderThreads; i++) {
7523      new Thread(() -> {
7524        try {
7525          for (int j = 0; j < 10000; j++) {
7526            // Verify the values
7527            Result result = region.get(new Get(row));
7528
7529            // The values should be equals to either the initial values or the values after
7530            // executing the mutations
7531            String q1Value = Bytes.toString(result.getValue(fam1, q1));
7532            if (v1.equals(q1Value)) {
7533              assertEquals(v2, Bytes.toString(result.getValue(fam1, q2)));
7534              assertEquals(1L, Bytes.toLong(result.getValue(fam1, q3)));
7535              assertEquals("a", Bytes.toString(result.getValue(fam1, q4)));
7536            } else if (v2.equals(q1Value)) {
7537              assertNull(Bytes.toString(result.getValue(fam1, q2)));
7538              assertEquals(2L, Bytes.toLong(result.getValue(fam1, q3)));
7539              assertEquals("ab", Bytes.toString(result.getValue(fam1, q4)));
7540            } else {
7541              fail("the qualifier " + Bytes.toString(q1) + " should be " + v1 + " or " + v2 +
7542                ", but " + q1Value);
7543            }
7544          }
7545        } catch (Exception e) {
7546          assertionError.set(new AssertionError(e));
7547        } catch (AssertionError e) {
7548          assertionError.set(e);
7549        }
7550
7551        latch.countDown();
7552      }).start();
7553    }
7554
7555    writerThread.join();
7556
7557    if (assertionError.get() != null) {
7558      throw assertionError.get();
7559    }
7560  }
7561
7562  @Test
7563  public void testMutateRow_WriteRequestCount() throws Exception {
7564    byte[] row1 = Bytes.toBytes("row1");
7565    byte[] fam1 = Bytes.toBytes("fam1");
7566    byte[] qf1 = Bytes.toBytes("qualifier");
7567    byte[] val1 = Bytes.toBytes("value1");
7568
7569    RowMutations rm = new RowMutations(row1);
7570    Put put = new Put(row1);
7571    put.addColumn(fam1, qf1, val1);
7572    rm.add(put);
7573
7574    this.region = initHRegion(tableName, method, CONF, fam1);
7575    long wrcBeforeMutate = this.region.writeRequestsCount.longValue();
7576    this.region.mutateRow(rm);
7577    long wrcAfterMutate = this.region.writeRequestsCount.longValue();
7578    Assert.assertEquals(wrcBeforeMutate + rm.getMutations().size(), wrcAfterMutate);
7579  }
7580
7581  @Test
7582  public void testBulkLoadReplicationEnabled() throws IOException {
7583    TEST_UTIL.getConfiguration().setBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY, true);
7584    final ServerName serverName = ServerName.valueOf(name.getMethodName(), 100, 42);
7585    final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName));
7586
7587    TableDescriptor tableDescriptor =
7588      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
7589        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)).build();
7590    RegionInfo hri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build();
7591    region = HRegion.openHRegion(hri, tableDescriptor, rss.getWAL(hri),
7592      TEST_UTIL.getConfiguration(), rss, null);
7593
7594    assertTrue(region.conf.getBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY, false));
7595    String plugins = region.conf.get(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, "");
7596    String replicationCoprocessorClass = ReplicationObserver.class.getCanonicalName();
7597    assertTrue(plugins.contains(replicationCoprocessorClass));
7598    assertTrue(region.getCoprocessorHost().
7599        getCoprocessors().contains(ReplicationObserver.class.getSimpleName()));
7600  }
7601
7602  /**
7603   * The same as HRegion class, the only difference is that instantiateHStore will
7604   * create a different HStore - HStoreForTesting. [HBASE-8518]
7605   */
7606  public static class HRegionForTesting extends HRegion {
7607
7608    public HRegionForTesting(final Path tableDir, final WAL wal, final FileSystem fs,
7609                             final Configuration confParam, final RegionInfo regionInfo,
7610                             final TableDescriptor htd, final RegionServerServices rsServices) {
7611      this(new HRegionFileSystem(confParam, fs, tableDir, regionInfo),
7612          wal, confParam, htd, rsServices);
7613    }
7614
7615    public HRegionForTesting(HRegionFileSystem fs, WAL wal,
7616                             Configuration confParam, TableDescriptor htd,
7617                             RegionServerServices rsServices) {
7618      super(fs, wal, confParam, htd, rsServices);
7619    }
7620
7621    /**
7622     * Create HStore instance.
7623     * @return If Mob is enabled, return HMobStore, otherwise return HStoreForTesting.
7624     */
7625    @Override
7626    protected HStore instantiateHStore(final ColumnFamilyDescriptor family, boolean warmup)
7627        throws IOException {
7628      if (family.isMobEnabled()) {
7629        if (HFile.getFormatVersion(this.conf) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) {
7630          throw new IOException("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS +
7631              " is required for MOB feature. Consider setting " + HFile.FORMAT_VERSION_KEY +
7632              " accordingly.");
7633        }
7634        return new HMobStore(this, family, this.conf, warmup);
7635      }
7636      return new HStoreForTesting(this, family, this.conf, warmup);
7637    }
7638  }
7639
7640  /**
7641   * HStoreForTesting is merely the same as HStore, the difference is in the doCompaction method
7642   * of HStoreForTesting there is a checkpoint "hbase.hstore.compaction.complete" which
7643   * doesn't let hstore compaction complete. In the former edition, this config is set in
7644   * HStore class inside compact method, though this is just for testing, otherwise it
7645   * doesn't do any help. In HBASE-8518, we try to get rid of all "hbase.hstore.compaction.complete"
7646   * config (except for testing code).
7647   */
7648  public static class HStoreForTesting extends HStore {
7649
7650    protected HStoreForTesting(final HRegion region,
7651        final ColumnFamilyDescriptor family,
7652        final Configuration confParam, boolean warmup) throws IOException {
7653      super(region, family, confParam, warmup);
7654    }
7655
7656    @Override
7657    protected List<HStoreFile> doCompaction(CompactionRequestImpl cr,
7658        Collection<HStoreFile> filesToCompact, User user, long compactionStartTime,
7659        List<Path> newFiles) throws IOException {
7660      // let compaction incomplete.
7661      if (!this.conf.getBoolean("hbase.hstore.compaction.complete", true)) {
7662        LOG.warn("hbase.hstore.compaction.complete is set to false");
7663        List<HStoreFile> sfs = new ArrayList<>(newFiles.size());
7664        final boolean evictOnClose =
7665            getCacheConfig() != null? getCacheConfig().shouldEvictOnClose(): true;
7666        for (Path newFile : newFiles) {
7667          // Create storefile around what we wrote with a reader on it.
7668          HStoreFile sf = createStoreFileAndReader(newFile);
7669          sf.closeStoreFile(evictOnClose);
7670          sfs.add(sf);
7671        }
7672        return sfs;
7673      }
7674      return super.doCompaction(cr, filesToCompact, user, compactionStartTime, newFiles);
7675    }
7676  }
7677
7678  @Test
7679  public void testCloseNoInterrupt() throws Exception {
7680    byte[] cf1 = Bytes.toBytes("CF1");
7681    byte[][] families = { cf1 };
7682    final int SLEEP_TIME = 10 * 1000;
7683
7684    Configuration conf = new Configuration(CONF);
7685    // Disable close thread interrupt and server abort behavior
7686    conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, false);
7687    conf.setInt(HRegion.CLOSE_WAIT_INTERVAL, 1000);
7688    region = initHRegion(tableName, method, conf, families);
7689
7690    final CountDownLatch latch = new CountDownLatch(1);
7691    final AtomicBoolean holderInterrupted = new AtomicBoolean();
7692    Thread holder = new Thread(new Runnable() {
7693      @Override
7694      public void run() {
7695        try {
7696          LOG.info("Starting region operation holder");
7697          region.startRegionOperation(Operation.SCAN);
7698          latch.countDown();
7699          try {
7700            Thread.sleep(SLEEP_TIME);
7701          } catch (InterruptedException e) {
7702            LOG.info("Interrupted");
7703            holderInterrupted.set(true);
7704          }
7705        } catch (Exception e) {
7706          throw new RuntimeException(e);
7707        } finally {
7708          try {
7709            region.closeRegionOperation();
7710          } catch (IOException e) {
7711          }
7712          LOG.info("Stopped region operation holder");
7713        }
7714      }
7715    });
7716
7717    holder.start();
7718    latch.await();
7719    region.close();
7720    region = null;
7721    holder.join();
7722
7723    assertFalse("Region lock holder should not have been interrupted", holderInterrupted.get());
7724  }
7725
7726  @Test
7727  public void testCloseInterrupt() throws Exception {
7728    byte[] cf1 = Bytes.toBytes("CF1");
7729    byte[][] families = { cf1 };
7730    final int SLEEP_TIME = 10 * 1000;
7731
7732    Configuration conf = new Configuration(CONF);
7733    // Enable close thread interrupt and server abort behavior
7734    conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, true);
7735    // Speed up the unit test, no need to wait default 10 seconds.
7736    conf.setInt(HRegion.CLOSE_WAIT_INTERVAL, 1000);
7737    region = initHRegion(tableName, method, conf, families);
7738
7739    final CountDownLatch latch = new CountDownLatch(1);
7740    final AtomicBoolean holderInterrupted = new AtomicBoolean();
7741    Thread holder = new Thread(new Runnable() {
7742      @Override
7743      public void run() {
7744        try {
7745          LOG.info("Starting region operation holder");
7746          region.startRegionOperation(Operation.SCAN);
7747          latch.countDown();
7748          try {
7749            Thread.sleep(SLEEP_TIME);
7750          } catch (InterruptedException e) {
7751            LOG.info("Interrupted");
7752            holderInterrupted.set(true);
7753          }
7754        } catch (Exception e) {
7755          throw new RuntimeException(e);
7756        } finally {
7757          try {
7758            region.closeRegionOperation();
7759          } catch (IOException e) {
7760          }
7761          LOG.info("Stopped region operation holder");
7762        }
7763      }
7764    });
7765
7766    holder.start();
7767    latch.await();
7768    region.close();
7769    region = null;
7770    holder.join();
7771
7772    assertTrue("Region lock holder was not interrupted", holderInterrupted.get());
7773  }
7774
7775  @Test
7776  public void testCloseAbort() throws Exception {
7777    byte[] cf1 = Bytes.toBytes("CF1");
7778    byte[][] families = { cf1 };
7779    final int SLEEP_TIME = 10 * 1000;
7780
7781    Configuration conf = new Configuration(CONF);
7782    // Enable close thread interrupt and server abort behavior.
7783    conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, true);
7784    // Set the abort interval to a fraction of sleep time so we are guaranteed to be aborted.
7785    conf.setInt(HRegion.CLOSE_WAIT_TIME, SLEEP_TIME / 2);
7786    // Set the wait interval to a fraction of sleep time so we are guaranteed to be interrupted.
7787    conf.setInt(HRegion.CLOSE_WAIT_INTERVAL, SLEEP_TIME / 4);
7788    region = initHRegion(tableName, method, conf, families);
7789    RegionServerServices rsServices = mock(RegionServerServices.class);
7790    when(rsServices.getServerName()).thenReturn(ServerName.valueOf("localhost", 1000, 1000));
7791    region.rsServices = rsServices;
7792
7793    final CountDownLatch latch = new CountDownLatch(1);
7794    Thread holder = new Thread(new Runnable() {
7795      @Override
7796      public void run() {
7797        try {
7798          LOG.info("Starting region operation holder");
7799          region.startRegionOperation(Operation.SCAN);
7800          latch.countDown();
7801          // Hold the lock for SLEEP_TIME seconds no matter how many times we are interrupted.
7802          int timeRemaining = SLEEP_TIME;
7803          while (timeRemaining > 0) {
7804            long start = EnvironmentEdgeManager.currentTime();
7805            try {
7806              Thread.sleep(timeRemaining);
7807            } catch (InterruptedException e) {
7808              LOG.info("Interrupted");
7809            }
7810            long end = EnvironmentEdgeManager.currentTime();
7811            timeRemaining -= end - start;
7812            if (timeRemaining < 0) {
7813              timeRemaining = 0;
7814            }
7815            if (timeRemaining > 0) {
7816              LOG.info("Sleeping again, remaining time " + timeRemaining + " ms");
7817            }
7818          }
7819        } catch (Exception e) {
7820          throw new RuntimeException(e);
7821        } finally {
7822          try {
7823            region.closeRegionOperation();
7824          } catch (IOException e) {
7825          }
7826          LOG.info("Stopped region operation holder");
7827        }
7828      }
7829    });
7830
7831    holder.start();
7832    latch.await();
7833    try {
7834      region.close();
7835    } catch (IOException e) {
7836      LOG.info("Caught expected exception", e);
7837    }
7838    region = null;
7839    holder.join();
7840
7841    // Verify the region tried to abort the server
7842    verify(rsServices, atLeast(1)).abort(anyString(),any());
7843  }
7844
7845  @Test
7846  public void testInterruptProtection() throws Exception {
7847    byte[] cf1 = Bytes.toBytes("CF1");
7848    byte[][] families = { cf1 };
7849    final int SLEEP_TIME = 10 * 1000;
7850
7851    Configuration conf = new Configuration(CONF);
7852    // Enable close thread interrupt and server abort behavior.
7853    conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, true);
7854    conf.setInt(HRegion.CLOSE_WAIT_INTERVAL, 1000);
7855    region = initHRegion(tableName, method, conf, families);
7856
7857    final CountDownLatch latch = new CountDownLatch(1);
7858    final AtomicBoolean holderInterrupted = new AtomicBoolean();
7859    Thread holder = new Thread(new Runnable() {
7860      @Override
7861      public void run() {
7862        try {
7863          LOG.info("Starting region operation holder");
7864          region.startRegionOperation(Operation.SCAN);
7865          LOG.info("Protecting against interrupts");
7866          region.disableInterrupts();
7867          try {
7868            latch.countDown();
7869            try {
7870              Thread.sleep(SLEEP_TIME);
7871            } catch (InterruptedException e) {
7872              LOG.info("Interrupted");
7873              holderInterrupted.set(true);
7874            }
7875          } finally {
7876            region.enableInterrupts();
7877          }
7878        } catch (Exception e) {
7879          throw new RuntimeException(e);
7880        } finally {
7881          try {
7882            region.closeRegionOperation();
7883          } catch (IOException e) {
7884          }
7885          LOG.info("Stopped region operation holder");
7886        }
7887      }
7888    });
7889
7890    holder.start();
7891    latch.await();
7892    region.close();
7893    region = null;
7894    holder.join();
7895
7896    assertFalse("Region lock holder should not have been interrupted", holderInterrupted.get());
7897  }
7898
7899}