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.screen.top; 019 020import static org.apache.commons.lang3.time.DateFormatUtils.ISO_8601_EXTENDED_TIME_FORMAT; 021 022import edu.umd.cs.findbugs.annotations.Nullable; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.List; 027import java.util.Objects; 028import java.util.stream.Collectors; 029import org.apache.hadoop.hbase.ClusterMetrics; 030import org.apache.hadoop.hbase.client.Admin; 031import org.apache.hadoop.hbase.hbtop.Record; 032import org.apache.hadoop.hbase.hbtop.RecordFilter; 033import org.apache.hadoop.hbase.hbtop.field.Field; 034import org.apache.hadoop.hbase.hbtop.field.FieldInfo; 035import org.apache.hadoop.hbase.hbtop.field.FieldValue; 036import org.apache.hadoop.hbase.hbtop.mode.DrillDownInfo; 037import org.apache.hadoop.hbase.hbtop.mode.Mode; 038import org.apache.yetus.audience.InterfaceAudience; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041 042 043/** 044 * The data and business logic for the top screen. 045 */ 046@InterfaceAudience.Private 047public class TopScreenModel { 048 049 private static final Logger LOGGER = LoggerFactory.getLogger(TopScreenModel.class); 050 051 private final Admin admin; 052 053 private Mode currentMode; 054 private Field currentSortField; 055 private List<FieldInfo> fieldInfos; 056 private List<Field> fields; 057 058 private Summary summary; 059 private List<Record> records; 060 061 private final List<RecordFilter> filters = new ArrayList<>(); 062 private final List<RecordFilter> pushDownFilters = new ArrayList<>(); 063 private final List<String> filterHistories = new ArrayList<>(); 064 065 private boolean ascendingSort; 066 067 public TopScreenModel(Admin admin, Mode initialMode, @Nullable List<Field> initialFields, 068 @Nullable Field initialSortField, @Nullable Boolean initialAscendingSort, 069 @Nullable List<RecordFilter> initialFilters) { 070 this.admin = Objects.requireNonNull(admin); 071 switchMode(Objects.requireNonNull(initialMode), initialSortField, false, initialFields, 072 initialAscendingSort, initialFilters); 073 } 074 075 public void switchMode(Mode nextMode, boolean keepSortFieldAndSortOrderIfPossible, 076 List<RecordFilter> initialFilters) { 077 switchMode(nextMode, null, keepSortFieldAndSortOrderIfPossible, null, null, initialFilters); 078 } 079 080 public void switchMode(Mode nextMode, Field initialSortField, 081 boolean keepSortFieldAndSortOrderIfPossible, @Nullable List<Field> initialFields, 082 @Nullable Boolean initialAscendingSort, @Nullable List<RecordFilter> initialFilters) { 083 currentMode = nextMode; 084 fieldInfos = Collections.unmodifiableList(new ArrayList<>(currentMode.getFieldInfos())); 085 086 if (initialFields != null) { 087 List<Field> tmp = new ArrayList<>(initialFields); 088 tmp.addAll(currentMode.getFieldInfos().stream().map(FieldInfo::getField) 089 .filter(f -> !initialFields.contains(f)) 090 .collect(Collectors.toList())); 091 fields = Collections.unmodifiableList(tmp); 092 } else { 093 fields = Collections.unmodifiableList(currentMode.getFieldInfos().stream() 094 .map(FieldInfo::getField).collect(Collectors.toList())); 095 } 096 097 if (keepSortFieldAndSortOrderIfPossible) { 098 boolean match = fields.stream().anyMatch(f -> f == currentSortField); 099 if (!match) { 100 if (initialSortField != null && initialAscendingSort != null) { 101 currentSortField = initialSortField; 102 ascendingSort = initialAscendingSort; 103 } else { 104 currentSortField = nextMode.getDefaultSortField(); 105 ascendingSort = false; 106 } 107 } 108 } else { 109 if (initialSortField != null && initialAscendingSort != null) { 110 currentSortField = initialSortField; 111 ascendingSort = initialAscendingSort; 112 } else { 113 currentSortField = nextMode.getDefaultSortField(); 114 ascendingSort = false; 115 } 116 } 117 118 clearFilters(); 119 if (initialFilters != null) { 120 filters.addAll(initialFilters); 121 } 122 decomposePushDownFilter(); 123 } 124 125 public void setSortFieldAndFields(Field sortField, List<Field> fields) { 126 this.currentSortField = sortField; 127 this.fields = Collections.unmodifiableList(new ArrayList<>(fields)); 128 } 129 130 /* 131 * HBTop only calls this from a single thread, and if that ever changes, this needs 132 * synchronization 133 */ 134 public void refreshMetricsData() { 135 ClusterMetrics clusterMetrics; 136 try { 137 clusterMetrics = admin.getClusterMetrics(); 138 } catch (Exception e) { 139 LOGGER.error("Unable to get cluster metrics", e); 140 return; 141 } 142 143 refreshSummary(clusterMetrics); 144 refreshRecords(clusterMetrics); 145 } 146 147 private void refreshSummary(ClusterMetrics clusterMetrics) { 148 String currentTime = ISO_8601_EXTENDED_TIME_FORMAT 149 .format(System.currentTimeMillis()); 150 String version = clusterMetrics.getHBaseVersion(); 151 String clusterId = clusterMetrics.getClusterId(); 152 int liveServers = clusterMetrics.getLiveServerMetrics().size(); 153 int deadServers = clusterMetrics.getDeadServerNames().size(); 154 int regionCount = clusterMetrics.getRegionCount(); 155 int ritCount = clusterMetrics.getRegionStatesInTransition().size(); 156 double averageLoad = clusterMetrics.getAverageLoad(); 157 long aggregateRequestPerSecond = clusterMetrics.getLiveServerMetrics().entrySet().stream() 158 .mapToLong(e -> e.getValue().getRequestCountPerSecond()).sum(); 159 160 summary = new Summary(currentTime, version, clusterId, liveServers + deadServers, 161 liveServers, deadServers, regionCount, ritCount, averageLoad, aggregateRequestPerSecond); 162 } 163 164 private void refreshRecords(ClusterMetrics clusterMetrics) { 165 List<Record> records = currentMode.getRecords(clusterMetrics, pushDownFilters); 166 167 // Filter and sort 168 records = records.stream() 169 .filter(r -> filters.stream().allMatch(f -> f.execute(r))) 170 .sorted((recordLeft, recordRight) -> { 171 FieldValue left = recordLeft.get(currentSortField); 172 FieldValue right = recordRight.get(currentSortField); 173 return (ascendingSort ? 1 : -1) * left.compareTo(right); 174 }).collect(Collectors.toList()); 175 176 this.records = Collections.unmodifiableList(records); 177 } 178 179 public void switchSortOrder() { 180 ascendingSort = !ascendingSort; 181 } 182 183 public boolean addFilter(String filterString, boolean ignoreCase) { 184 RecordFilter filter = RecordFilter.parse(filterString, fields, ignoreCase); 185 if (filter == null) { 186 return false; 187 } 188 filters.add(filter); 189 filterHistories.add(filterString); 190 return true; 191 } 192 193 public void clearFilters() { 194 pushDownFilters.clear(); 195 filters.clear(); 196 } 197 198 public boolean drillDown(Record selectedRecord) { 199 DrillDownInfo drillDownInfo = currentMode.drillDown(selectedRecord); 200 if (drillDownInfo == null) { 201 return false; 202 } 203 switchMode(drillDownInfo.getNextMode(), true, drillDownInfo.getInitialFilters()); 204 return true; 205 } 206 207 public Mode getCurrentMode() { 208 return currentMode; 209 } 210 211 public Field getCurrentSortField() { 212 return currentSortField; 213 } 214 215 public List<FieldInfo> getFieldInfos() { 216 return fieldInfos; 217 } 218 219 public List<Field> getFields() { 220 return fields; 221 } 222 223 public Summary getSummary() { 224 return summary; 225 } 226 227 public List<Record> getRecords() { 228 return records; 229 } 230 231 public List<RecordFilter> getFilters() { 232 return Collections.unmodifiableList(filters); 233 } 234 235 public List<String> getFilterHistories() { 236 return Collections.unmodifiableList(filterHistories); 237 } 238 239 private void decomposePushDownFilter() { 240 pushDownFilters.clear(); 241 for (RecordFilter filter : filters) { 242 if (!fields.contains(filter.getField())) { 243 pushDownFilters.add(filter); 244 } 245 } 246 filters.removeAll(pushDownFilters); 247 } 248 249 public Collection<? extends RecordFilter> getPushDownFilters() { 250 return pushDownFilters; 251 } 252}