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