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.compactions;
019
020import static org.junit.Assert.assertEquals;
021
022import java.text.SimpleDateFormat;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.ExtendedCell;
025import org.apache.hadoop.hbase.HBaseClassTestRule;
026import org.apache.hadoop.hbase.PrivateCellUtil;
027import org.apache.hadoop.hbase.testclassification.RegionServerTests;
028import org.apache.hadoop.hbase.testclassification.SmallTests;
029import org.apache.hadoop.hbase.util.Bytes;
030import org.junit.After;
031import org.junit.Before;
032import org.junit.ClassRule;
033import org.junit.Test;
034import org.junit.experimental.categories.Category;
035
036@Category({ RegionServerTests.class, SmallTests.class })
037public class TestRowKeyDateTieringValueProvider {
038  @ClassRule
039  public static final HBaseClassTestRule CLASS_RULE =
040    HBaseClassTestRule.forClass(TestRowKeyDateTieringValueProvider.class);
041
042  private RowKeyDateTieringValueProvider provider;
043  private Configuration conf;
044
045  @Before
046  public void setUp() {
047    conf = new Configuration();
048    provider = new RowKeyDateTieringValueProvider();
049  }
050
051  @After
052  public void tearDown() {
053    provider = null;
054    conf = null;
055  }
056
057  @Test
058  public void testInitWithValidConfig() throws Exception {
059    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "(\\d{4}-\\d{2}-\\d{2})");
060    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "yyyy-MM-dd");
061    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_GROUP, "1");
062    provider.init(conf);
063    assertEquals(provider.getRowKeyPattern().pattern(), "(\\d{4}-\\d{2}-\\d{2})");
064    assertEquals(provider.getDateFormat().toPattern(), "yyyy-MM-dd");
065    assertEquals(Integer.valueOf(1), provider.getRowKeyRegexExtractGroup());
066  }
067
068  @Test(expected = IllegalArgumentException.class)
069  public void testInitWithMissingRegexPattern() throws Exception {
070    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "yyyy-MM-dd");
071    provider.init(conf);
072  }
073
074  @Test(expected = IllegalArgumentException.class)
075  public void testInitWithMissingDateFormat() throws Exception {
076    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "(\\d{4}-\\d{2}-\\d{2})");
077    provider.init(conf);
078  }
079
080  @Test(expected = IllegalArgumentException.class)
081  public void testInitWithInvalidDateFormat() throws Exception {
082    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "(\\d{4}-\\d{2}-\\d{2})");
083    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "invalid-format");
084    provider.init(conf);
085  }
086
087  @Test(expected = IllegalArgumentException.class)
088  public void testInitWithInvalidExtractGroup() throws Exception {
089    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "(\\d{4}-\\d{2}-\\d{2})");
090    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "yyyy-MM-dd");
091    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_GROUP, "-1");
092    provider.init(conf);
093  }
094
095  @Test(expected = IllegalArgumentException.class)
096  public void testInitWithExtractGroupExceedingPatternGroups() throws Exception {
097    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "(\\d{4}-\\d{2}-\\d{2})");
098    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "yyyy-MM-dd");
099    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_GROUP, "2"); // Only 1 group in
100                                                                          // pattern
101    provider.init(conf);
102  }
103
104  @Test
105  public void testGetTieringValue() throws Exception {
106    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "_(\\d{4}-\\d{2}-\\d{2})_");
107    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "yyyy-MM-dd");
108    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_GROUP, "1");
109    provider.init(conf);
110
111    String dateStr = "2023-10-15";
112    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
113    long expectedTimestamp = sdf.parse(dateStr).getTime();
114
115    String rowKeyStr = "order_" + dateStr + "_details";
116    byte[] rowKey = Bytes.toBytes(rowKeyStr);
117    ExtendedCell cell = PrivateCellUtil.createFirstOnRow(rowKey);
118    long actualTimestamp = provider.getTieringValue(cell);
119
120    assertEquals(expectedTimestamp, actualTimestamp);
121  }
122
123  @Test
124  public void testGetTieringValueWithNonMatchingRowKey() throws Exception {
125    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "_(\\d{4}-\\d{2}-\\d{2})_");
126    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "yyyy-MM-dd");
127    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_GROUP, "1");
128    provider.init(conf);
129
130    String rowKeyStr = "order_details_no_date";
131    byte[] rowKey = Bytes.toBytes(rowKeyStr);
132    ExtendedCell cell = PrivateCellUtil.createFirstOnRow(rowKey);
133    long actualTimestamp = provider.getTieringValue(cell);
134
135    assertEquals(Long.MAX_VALUE, actualTimestamp);
136  }
137
138  @Test
139  public void testGetTieringValueWithInvalidDateInRowKey() throws Exception {
140    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "_(\\d{14})_");
141    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "yyyyMMddHHmmss");
142    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_GROUP, "1");
143    provider.init(conf);
144
145    // Invalid Month (14)
146    String rowKeyStr = "order_20151412124556_date";
147    byte[] rowKey = Bytes.toBytes(rowKeyStr);
148    ExtendedCell cell = PrivateCellUtil.createFirstOnRow(rowKey);
149    long actualTimestamp = provider.getTieringValue(cell);
150
151    assertEquals(Long.MAX_VALUE, actualTimestamp);
152  }
153
154  @Test
155  public void testGetTieringValueWithNonUTF8RowKey() throws Exception {
156    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_PATTERN, "_(\\d{8})_");
157    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_FORMAT, "yyyyMMdd");
158    conf.set(RowKeyDateTieringValueProvider.TIERING_KEY_DATE_GROUP, "1");
159    provider.init(conf);
160
161    // Row key with non-UTF-8 bytes (invalid UTF-8 sequence)
162    byte[] rowKey =
163      new byte[] { 0x6F, 0x72, 0x64, 0x65, 0x72, 0x5F, (byte) 0xFF, (byte) 0xFE, 0x5F };
164    ExtendedCell cell = PrivateCellUtil.createFirstOnRow(rowKey);
165    long timestamp = provider.getTieringValue(cell);
166
167    assertEquals(Long.MAX_VALUE, timestamp);
168  }
169
170  @Test(expected = IllegalStateException.class)
171  public void testGetTieringValueWithoutInitialization() {
172    String rowKeyStr = "order_9999999999999999_date";
173    byte[] rowKey = Bytes.toBytes(rowKeyStr);
174    ExtendedCell cell = PrivateCellUtil.createFirstOnRow(rowKey);
175    provider.getTieringValue(cell);
176  }
177}