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