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.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertTrue;
022
023import java.io.IOException;
024import java.util.Arrays;
025import java.util.stream.Stream;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.Cell;
028import org.apache.hadoop.hbase.CellUtil;
029import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate;
030import org.apache.hadoop.hbase.HBaseTestingUtil;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.TableName;
033import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
034import org.apache.hadoop.hbase.client.Connection;
035import org.apache.hadoop.hbase.client.ConnectionFactory;
036import org.apache.hadoop.hbase.client.Put;
037import org.apache.hadoop.hbase.client.Result;
038import org.apache.hadoop.hbase.client.ResultScanner;
039import org.apache.hadoop.hbase.client.Scan;
040import org.apache.hadoop.hbase.client.Table;
041import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
042import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
043import org.apache.hadoop.hbase.testclassification.LargeTests;
044import org.apache.hadoop.hbase.testclassification.RegionServerTests;
045import org.apache.hadoop.hbase.util.Bytes;
046import org.junit.jupiter.api.AfterAll;
047import org.junit.jupiter.api.BeforeAll;
048import org.junit.jupiter.api.Tag;
049import org.junit.jupiter.api.TestInfo;
050import org.junit.jupiter.api.TestTemplate;
051import org.junit.jupiter.params.provider.Arguments;
052import org.slf4j.Logger;
053import org.slf4j.LoggerFactory;
054
055@Tag(RegionServerTests.TAG)
056@Tag(LargeTests.TAG)
057@HBaseParameterizedTestTemplate(name = "{index}: DataBlockEncoding = {0}")
058public class TestTagsReverseScan {
059
060  private static final Logger LOG = LoggerFactory.getLogger(TestTagsReverseScan.class);
061
062  private final static HBaseTestingUtil UTIL = new HBaseTestingUtil();
063
064  private DataBlockEncoding encoding;
065
066  public TestTagsReverseScan(DataBlockEncoding encoding) {
067    this.encoding = encoding;
068  }
069
070  public static Stream<Arguments> parameters() {
071    return Arrays.stream(DataBlockEncoding.values()).map(Arguments::of);
072  }
073
074  @BeforeAll
075  public static void setUpBeforeClass() throws Exception {
076    UTIL.startMiniCluster(2);
077  }
078
079  @AfterAll
080  public static void tearDownAfterClass() throws Exception {
081    UTIL.shutdownMiniCluster();
082  }
083
084  /**
085   * Test that we can do reverse scans when writing tags and using DataBlockEncoding. Fails with an
086   * exception for PREFIX, DIFF, and FAST_DIFF prior to HBASE-27580
087   */
088  @TestTemplate
089  public void testReverseScanWithDBE(TestInfo testInfo) throws IOException {
090    byte[] family = Bytes.toBytes("0");
091
092    Configuration conf = new Configuration(UTIL.getConfiguration());
093    conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
094
095    try (Connection connection = ConnectionFactory.createConnection(conf)) {
096      testReverseScanWithDBE(testInfo, connection, family, HConstants.DEFAULT_BLOCKSIZE, 10);
097    }
098  }
099
100  /**
101   * Test that we can do reverse scans when writing tags and using DataBlockEncoding. Fails with an
102   * exception for PREFIX, DIFF, and FAST_DIFF
103   */
104  @TestTemplate
105  public void testReverseScanWithDBEWhenCurrentBlockUpdates(TestInfo testInfo) throws IOException {
106    byte[] family = Bytes.toBytes("0");
107
108    Configuration conf = new Configuration(UTIL.getConfiguration());
109    conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
110
111    try (Connection connection = ConnectionFactory.createConnection(conf)) {
112      testReverseScanWithDBE(testInfo, connection, family, 1024, 30000);
113    }
114  }
115
116  private void testReverseScanWithDBE(TestInfo testInfo, Connection conn, byte[] family,
117    int blockSize, int maxRows) throws IOException {
118    LOG.info("Running test with DBE={}", encoding);
119    TableName tableName =
120      TableName.valueOf(testInfo.getTestMethod().get().getName() + "-" + encoding);
121    UTIL.createTable(
122      TableDescriptorBuilder.newBuilder(tableName).setColumnFamily(ColumnFamilyDescriptorBuilder
123        .newBuilder(family).setDataBlockEncoding(encoding).setBlocksize(blockSize).build()).build(),
124      null);
125
126    Table table = conn.getTable(tableName);
127
128    byte[] val1 = new byte[10];
129    byte[] val2 = new byte[10];
130    Bytes.random(val1);
131    Bytes.random(val2);
132
133    for (int i = 0; i < maxRows; i++) {
134      table.put(new Put(Bytes.toBytes(i)).addColumn(family, Bytes.toBytes(1), val1)
135        .addColumn(family, Bytes.toBytes(2), val2).setTTL(600_000));
136    }
137
138    UTIL.flush(table.getName());
139
140    Scan scan = new Scan();
141    scan.setReversed(true);
142
143    try (ResultScanner scanner = table.getScanner(scan)) {
144      for (int i = maxRows - 1; i >= 0; i--) {
145        Result row = scanner.next();
146        assertEquals(2, row.size());
147
148        Cell cell1 = row.getColumnLatestCell(family, Bytes.toBytes(1));
149        assertTrue(CellUtil.matchingRows(cell1, Bytes.toBytes(i)));
150        assertTrue(CellUtil.matchingValue(cell1, val1));
151
152        Cell cell2 = row.getColumnLatestCell(family, Bytes.toBytes(2));
153        assertTrue(CellUtil.matchingRows(cell2, Bytes.toBytes(i)));
154        assertTrue(CellUtil.matchingValue(cell2, val2));
155      }
156    }
157  }
158}