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