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.io.encoding;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.List;
025import org.apache.hadoop.fs.Path;
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.HBaseTestingUtil;
028import org.apache.hadoop.hbase.HConstants;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
031import org.apache.hadoop.hbase.client.Put;
032import org.apache.hadoop.hbase.client.RegionInfo;
033import org.apache.hadoop.hbase.client.RegionInfoBuilder;
034import org.apache.hadoop.hbase.client.Scan;
035import org.apache.hadoop.hbase.client.TableDescriptor;
036import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
037import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
038import org.apache.hadoop.hbase.regionserver.HRegion;
039import org.apache.hadoop.hbase.regionserver.RegionScanner;
040import org.apache.hadoop.hbase.testclassification.IOTests;
041import org.apache.hadoop.hbase.testclassification.SmallTests;
042import org.apache.hadoop.hbase.util.Bytes;
043import org.junit.jupiter.api.AfterEach;
044import org.junit.jupiter.api.BeforeEach;
045import org.junit.jupiter.api.Tag;
046import org.junit.jupiter.api.Test;
047
048@Tag(IOTests.TAG)
049@Tag(SmallTests.TAG)
050public class TestSeekBeforeWithReverseScan {
051
052  private final HBaseTestingUtil testUtil = new HBaseTestingUtil();
053
054  private HRegion region;
055
056  private byte[] cfName = Bytes.toBytes("a");
057  private byte[] cqName = Bytes.toBytes("b");
058
059  @BeforeEach
060  public void setUp() throws Exception {
061    TableName tableName = TableName.valueOf(getClass().getSimpleName());
062    TableDescriptor tableDescriptor =
063      TableDescriptorBuilder.newBuilder(tableName).setColumnFamily(ColumnFamilyDescriptorBuilder
064        .newBuilder(cfName).setDataBlockEncoding(DataBlockEncoding.FAST_DIFF).build()).build();
065    RegionInfo info = RegionInfoBuilder.newBuilder(tableName).build();
066    Path path = testUtil.getDataTestDir(getClass().getSimpleName());
067    region =
068      HBaseTestingUtil.createRegionAndWAL(info, path, testUtil.getConfiguration(), tableDescriptor);
069  }
070
071  @AfterEach
072  public void tearDown() throws Exception {
073    HBaseTestingUtil.closeRegionAndWAL(region);
074    testUtil.cleanupTestDir();
075  }
076
077  @Test
078  public void testReverseScanWithoutPadding() throws Exception {
079    byte[] row1 = Bytes.toBytes("a");
080    byte[] row2 = Bytes.toBytes("ab");
081    byte[] row3 = Bytes.toBytes("b");
082
083    Put put1 = new Put(row1);
084    put1.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
085    Put put2 = new Put(row2);
086    put2.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
087    Put put3 = new Put(row3);
088    put3.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
089
090    region.put(put1);
091    region.put(put2);
092    region.put(put3);
093    region.flush(true);
094    Scan scan = new Scan();
095    scan.setCacheBlocks(false);
096    scan.setReversed(true);
097    scan.setFilter(new FirstKeyOnlyFilter());
098    scan.addFamily(cfName);
099    RegionScanner scanner = region.getScanner(scan);
100    List<Cell> res = new ArrayList<>();
101    int count = 1;
102    while (scanner.next(res)) {
103      count++;
104    }
105    assertEquals("b", Bytes.toString(res.get(0).getRowArray(), res.get(0).getRowOffset(),
106      res.get(0).getRowLength()));
107    assertEquals("ab", Bytes.toString(res.get(1).getRowArray(), res.get(1).getRowOffset(),
108      res.get(1).getRowLength()));
109    assertEquals("a", Bytes.toString(res.get(2).getRowArray(), res.get(2).getRowOffset(),
110      res.get(2).getRowLength()));
111    assertEquals(3, count);
112  }
113
114  @Test
115  public void testReverseScanWithPadding() throws Exception {
116    byte[] terminator = new byte[] { -1 };
117    byte[] row1 = Bytes.add(invert(Bytes.toBytes("a")), terminator);
118    byte[] row2 = Bytes.add(invert(Bytes.toBytes("ab")), terminator);
119    byte[] row3 = Bytes.add(invert(Bytes.toBytes("b")), terminator);
120
121    Put put1 = new Put(row1);
122    put1.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
123    Put put2 = new Put(row2);
124    put2.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
125    Put put3 = new Put(row3);
126    put3.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
127
128    region.put(put1);
129    region.put(put2);
130    region.put(put3);
131    region.flush(true);
132    Scan scan = new Scan();
133    scan.setCacheBlocks(false);
134    scan.setReversed(true);
135    scan.setFilter(new FirstKeyOnlyFilter());
136    scan.addFamily(cfName);
137    RegionScanner scanner = region.getScanner(scan);
138    List<Cell> res = new ArrayList<>();
139    int count = 1;
140    while (scanner.next(res)) {
141      count++;
142    }
143    assertEquals(3, count);
144  }
145
146  private byte[] invert(byte[] bytes) {
147    byte[] newBytes = Arrays.copyOf(bytes, bytes.length);
148    for (int i = 0; i < newBytes.length; i++) {
149      newBytes[i] = (byte) (newBytes[i] ^ 0xFF);
150    }
151    return newBytes;
152  }
153}