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