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