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