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.hbtop;
019
020import static org.hamcrest.CoreMatchers.hasItems;
021import static org.hamcrest.CoreMatchers.is;
022import static org.hamcrest.CoreMatchers.notNullValue;
023import static org.hamcrest.CoreMatchers.nullValue;
024import static org.junit.Assert.assertThat;
025
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.Collections;
029import java.util.List;
030import java.util.stream.Collectors;
031import org.apache.hadoop.hbase.HBaseClassTestRule;
032import org.apache.hadoop.hbase.Size;
033import org.apache.hadoop.hbase.hbtop.field.Field;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.junit.ClassRule;
036import org.junit.Test;
037import org.junit.experimental.categories.Category;
038
039
040@Category(SmallTests.class)
041public class TestRecordFilter {
042
043  @ClassRule
044  public static final HBaseClassTestRule CLASS_RULE =
045    HBaseClassTestRule.forClass(TestRecordFilter.class);
046
047  @Test
048  public void testParseAndBuilder() {
049    testParseAndBuilder("REGION=region1", false,
050      RecordFilter.newBuilder(Field.REGION).equal("region1"));
051
052    testParseAndBuilder("REGION=", false,
053      RecordFilter.newBuilder(Field.REGION).equal(""));
054
055    testParseAndBuilder("!REGION=region1", false,
056      RecordFilter.newBuilder(Field.REGION).notEqual("region1"));
057
058    testParseAndBuilder("REGION==region2", true,
059      RecordFilter.newBuilder(Field.REGION, true).doubleEquals("region2"));
060
061    testParseAndBuilder("!REGION==region2", true,
062      RecordFilter.newBuilder(Field.REGION, true).notDoubleEquals("region2"));
063
064    testParseAndBuilder("#REQ/S>100", false,
065      RecordFilter.newBuilder(Field.REQUEST_COUNT_PER_SECOND).greater(100L));
066
067    testParseAndBuilder("!#REQ/S>100", false,
068      RecordFilter.newBuilder(Field.REQUEST_COUNT_PER_SECOND).notGreater(100L));
069
070    testParseAndBuilder("SF>=50MB", true,
071      RecordFilter.newBuilder(Field.STORE_FILE_SIZE, true).greaterOrEqual("50MB"));
072
073    testParseAndBuilder("!SF>=50MB", true,
074      RecordFilter.newBuilder(Field.STORE_FILE_SIZE, true).notGreaterOrEqual("50MB"));
075
076    testParseAndBuilder("#REQ/S<20", false,
077      RecordFilter.newBuilder(Field.REQUEST_COUNT_PER_SECOND).less(20L));
078
079    testParseAndBuilder("!#REQ/S<20", false,
080      RecordFilter.newBuilder(Field.REQUEST_COUNT_PER_SECOND).notLess(20L));
081
082    testParseAndBuilder("%COMP<=50%", true,
083      RecordFilter.newBuilder(Field.COMPACTION_PROGRESS, true).lessOrEqual("50%"));
084
085    testParseAndBuilder("!%COMP<=50%", true,
086      RecordFilter.newBuilder(Field.COMPACTION_PROGRESS, true).notLessOrEqual("50%"));
087  }
088
089  private void testParseAndBuilder(String filterString, boolean ignoreCase, RecordFilter expected) {
090    RecordFilter actual = RecordFilter.parse(filterString, ignoreCase);
091    assertThat(expected, is(actual));
092  }
093
094  @Test
095  public void testParseFailure() {
096    RecordFilter filter = RecordFilter.parse("REGIO=region1", false);
097    assertThat(filter, is(nullValue()));
098
099    filter = RecordFilter.parse("", false);
100    assertThat(filter, is(nullValue()));
101
102    filter = RecordFilter.parse("#REQ/S==aaa", false);
103    assertThat(filter, is(nullValue()));
104
105    filter = RecordFilter.parse("SF>=50", false);
106    assertThat(filter, is(nullValue()));
107  }
108
109  @Test
110  public void testToString() {
111    testToString("REGION=region1");
112    testToString("!REGION=region1");
113    testToString("REGION==region2");
114    testToString("!REGION==region2");
115    testToString("#REQ/S>100");
116    testToString("!#REQ/S>100");
117    testToString("SF>=50.0MB");
118    testToString("!SF>=50.0MB");
119    testToString("#REQ/S<20");
120    testToString("!#REQ/S<20");
121    testToString("%COMP<=50.00%");
122    testToString("!%COMP<=50.00%");
123  }
124
125  private void testToString(String filterString) {
126    RecordFilter filter = RecordFilter.parse(filterString, false);
127    assertThat(filter, is(notNullValue()));
128    assertThat(filterString, is(filter.toString()));
129  }
130
131  @Test
132  public void testFilters() {
133    List<Record> records = createTestRecords();
134
135    testFilter(records, "REGION=region", false,
136      "region1", "region2", "region3", "region4", "region5");
137    testFilter(records, "!REGION=region", false);
138    testFilter(records, "REGION=Region", false);
139
140    testFilter(records, "REGION==region", false);
141    testFilter(records, "REGION==region1", false, "region1");
142    testFilter(records, "!REGION==region1", false, "region2", "region3", "region4", "region5");
143
144    testFilter(records, "#REQ/S==100", false, "region1");
145    testFilter(records, "#REQ/S>100", false, "region2", "region5");
146    testFilter(records, "SF>=100MB", false, "region1", "region2", "region4", "region5");
147    testFilter(records, "!#SF>=10", false, "region1", "region4");
148    testFilter(records, "LOCALITY<0.5", false, "region5");
149    testFilter(records, "%COMP<=50%", false, "region2", "region3", "region4", "region5");
150
151    testFilters(records, Arrays.asList("SF>=100MB", "#REQ/S>100"), false,
152      "region2", "region5");
153    testFilters(records, Arrays.asList("%COMP<=50%", "!#SF>=10"), false, "region4");
154    testFilters(records, Arrays.asList("!REGION==region1", "LOCALITY<0.5", "#REQ/S>100"), false,
155      "region5");
156  }
157
158  @Test
159  public void testFiltersIgnoreCase() {
160    List<Record> records = createTestRecords();
161
162    testFilter(records, "REGION=Region", true,
163      "region1", "region2", "region3", "region4", "region5");
164    testFilter(records, "REGION=REGION", true,
165      "region1", "region2", "region3", "region4", "region5");
166  }
167
168  private List<Record> createTestRecords() {
169    List<Record> ret = new ArrayList<>();
170    ret.add(createTestRecord("region1", 100L, new Size(100, Size.Unit.MEGABYTE), 2, 1.0f, 80f));
171    ret.add(createTestRecord("region2", 120L, new Size(100, Size.Unit.GIGABYTE), 10, 0.5f, 20f));
172    ret.add(createTestRecord("region3", 50L, new Size(500, Size.Unit.KILOBYTE), 15, 0.8f, 50f));
173    ret.add(createTestRecord("region4", 90L, new Size(10, Size.Unit.TERABYTE), 5, 0.9f, 30f));
174    ret.add(createTestRecord("region5", 200L, new Size(1, Size.Unit.PETABYTE), 13, 0.1f, 40f));
175    return ret;
176  }
177
178  private Record createTestRecord(String region, long requestCountPerSecond,
179    Size storeFileSize, int numStoreFiles, float locality, float compactionProgress) {
180    Record.Builder builder = Record.builder();
181    builder.put(Field.REGION, region);
182    builder.put(Field.REQUEST_COUNT_PER_SECOND, requestCountPerSecond);
183    builder.put(Field.STORE_FILE_SIZE, storeFileSize);
184    builder.put(Field.NUM_STORE_FILES, numStoreFiles);
185    builder.put(Field.LOCALITY, locality);
186    builder.put(Field.COMPACTION_PROGRESS, compactionProgress);
187    return builder.build();
188  }
189
190  private void testFilter(List<Record> records, String filterString, boolean ignoreCase,
191    String... expectedRegions) {
192    testFilters(records, Collections.singletonList(filterString), ignoreCase, expectedRegions);
193  }
194
195  private void testFilters(List<Record> records, List<String> filterStrings, boolean ignoreCase,
196    String... expectedRegions) {
197    List<String> actual =
198      records.stream().filter(r -> filterStrings.stream()
199        .map(f -> RecordFilter.parse(f, ignoreCase))
200        .allMatch(f -> f.execute(r)))
201        .map(r -> r.get(Field.REGION).asString())
202        .collect(Collectors.toList());
203    assertThat(actual, hasItems(expectedRegions));
204    assertThat(actual.size(), is(expectedRegions.length));
205  }
206}