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