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