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.hadoop.hbase.util.EnvironmentEdgeManager; 039import org.apache.yetus.audience.InterfaceAudience; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 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)).collect(Collectors.toList())); 090 fields = Collections.unmodifiableList(tmp); 091 } else { 092 fields = Collections.unmodifiableList( 093 currentMode.getFieldInfos().stream().map(FieldInfo::getField).collect(Collectors.toList())); 094 } 095 096 if (keepSortFieldAndSortOrderIfPossible) { 097 boolean match = fields.stream().anyMatch(f -> f == currentSortField); 098 if (!match) { 099 if (initialSortField != null && initialAscendingSort != null) { 100 currentSortField = initialSortField; 101 ascendingSort = initialAscendingSort; 102 } else { 103 currentSortField = nextMode.getDefaultSortField(); 104 ascendingSort = false; 105 } 106 } 107 } else { 108 if (initialSortField != null && initialAscendingSort != null) { 109 currentSortField = initialSortField; 110 ascendingSort = initialAscendingSort; 111 } else { 112 currentSortField = nextMode.getDefaultSortField(); 113 ascendingSort = false; 114 } 115 } 116 117 clearFilters(); 118 if (initialFilters != null) { 119 filters.addAll(initialFilters); 120 } 121 decomposePushDownFilter(); 122 } 123 124 public void setSortFieldAndFields(Field sortField, List<Field> fields) { 125 this.currentSortField = sortField; 126 this.fields = Collections.unmodifiableList(new ArrayList<>(fields)); 127 } 128 129 /* 130 * HBTop only calls this from a single thread, and if that ever changes, this needs 131 * synchronization 132 */ 133 public void refreshMetricsData() { 134 ClusterMetrics clusterMetrics; 135 try { 136 clusterMetrics = admin.getClusterMetrics(); 137 } catch (Exception e) { 138 LOGGER.error("Unable to get cluster metrics", e); 139 return; 140 } 141 142 refreshSummary(clusterMetrics); 143 refreshRecords(clusterMetrics); 144 } 145 146 private void refreshSummary(ClusterMetrics clusterMetrics) { 147 String currentTime = ISO_8601_EXTENDED_TIME_FORMAT.format(EnvironmentEdgeManager.currentTime()); 148 String version = clusterMetrics.getHBaseVersion(); 149 String clusterId = clusterMetrics.getClusterId(); 150 int liveServers = clusterMetrics.getLiveServerMetrics().size(); 151 int deadServers = clusterMetrics.getDeadServerNames().size(); 152 int regionCount = clusterMetrics.getRegionCount(); 153 int ritCount = clusterMetrics.getRegionStatesInTransition().size(); 154 double averageLoad = clusterMetrics.getAverageLoad(); 155 long aggregateRequestPerSecond = clusterMetrics.getLiveServerMetrics().entrySet().stream() 156 .mapToLong(e -> e.getValue().getRequestCountPerSecond()).sum(); 157 158 summary = new Summary(currentTime, version, clusterId, liveServers + deadServers, liveServers, 159 deadServers, regionCount, ritCount, averageLoad, aggregateRequestPerSecond); 160 } 161 162 private void refreshRecords(ClusterMetrics clusterMetrics) { 163 List<Record> records = currentMode.getRecords(clusterMetrics, pushDownFilters); 164 165 // Filter and sort 166 records = records.stream().filter(r -> filters.stream().allMatch(f -> f.execute(r))) 167 .sorted((recordLeft, recordRight) -> { 168 FieldValue left = recordLeft.get(currentSortField); 169 FieldValue right = recordRight.get(currentSortField); 170 return (ascendingSort ? 1 : -1) * left.compareTo(right); 171 }).collect(Collectors.toList()); 172 173 this.records = Collections.unmodifiableList(records); 174 } 175 176 public void switchSortOrder() { 177 ascendingSort = !ascendingSort; 178 } 179 180 public boolean addFilter(String filterString, boolean ignoreCase) { 181 RecordFilter filter = RecordFilter.parse(filterString, fields, ignoreCase); 182 if (filter == null) { 183 return false; 184 } 185 filters.add(filter); 186 filterHistories.add(filterString); 187 return true; 188 } 189 190 public void clearFilters() { 191 pushDownFilters.clear(); 192 filters.clear(); 193 } 194 195 public boolean drillDown(Record selectedRecord) { 196 DrillDownInfo drillDownInfo = currentMode.drillDown(selectedRecord); 197 if (drillDownInfo == null) { 198 return false; 199 } 200 switchMode(drillDownInfo.getNextMode(), true, drillDownInfo.getInitialFilters()); 201 return true; 202 } 203 204 public Mode getCurrentMode() { 205 return currentMode; 206 } 207 208 public Field getCurrentSortField() { 209 return currentSortField; 210 } 211 212 public List<FieldInfo> getFieldInfos() { 213 return fieldInfos; 214 } 215 216 public List<Field> getFields() { 217 return fields; 218 } 219 220 public Summary getSummary() { 221 return summary; 222 } 223 224 public List<Record> getRecords() { 225 return records; 226 } 227 228 public List<RecordFilter> getFilters() { 229 return Collections.unmodifiableList(filters); 230 } 231 232 public List<String> getFilterHistories() { 233 return Collections.unmodifiableList(filterHistories); 234 } 235 236 private void decomposePushDownFilter() { 237 pushDownFilters.clear(); 238 for (RecordFilter filter : filters) { 239 if (!fields.contains(filter.getField())) { 240 pushDownFilters.add(filter); 241 } 242 } 243 filters.removeAll(pushDownFilters); 244 } 245 246 public Collection<? extends RecordFilter> getPushDownFilters() { 247 return pushDownFilters; 248 } 249}