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