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