blob: 0cb87b61b4ec2d3e4636bade3aff9a9eaa6312de [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.accumulo.monitor.rest.tservers;
import static org.apache.accumulo.monitor.util.ParameterValidator.HOSTNAME_PORT_REGEX;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.apache.accumulo.core.clientImpl.ClientContext;
import org.apache.accumulo.core.clientImpl.Tables;
import org.apache.accumulo.core.data.TableId;
import org.apache.accumulo.core.dataImpl.KeyExtent;
import org.apache.accumulo.core.master.thrift.ManagerMonitorInfo;
import org.apache.accumulo.core.master.thrift.RecoveryStatus;
import org.apache.accumulo.core.master.thrift.TabletServerStatus;
import org.apache.accumulo.core.rpc.ThriftUtil;
import org.apache.accumulo.core.tabletserver.thrift.ActionStats;
import org.apache.accumulo.core.tabletserver.thrift.TabletClientService;
import org.apache.accumulo.core.tabletserver.thrift.TabletStats;
import org.apache.accumulo.core.trace.TraceUtil;
import org.apache.accumulo.core.util.AddressUtil;
import org.apache.accumulo.core.util.HostAndPort;
import org.apache.accumulo.monitor.Monitor;
import org.apache.accumulo.monitor.rest.manager.ManagerResource;
import org.apache.accumulo.server.manager.state.DeadServerList;
import org.apache.accumulo.server.util.ActionStatsUpdator;
/**
* Generates tserver lists as JSON objects
*
* @since 2.0.0
*/
@Path("/tservers")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class TabletServerResource {
@Inject
private Monitor monitor;
// Variable names become JSON keys
private TabletStats total;
private TabletStats historical;
/**
* Generates tserver summary
*
* @return tserver summary
*/
@GET
public TabletServers getTserverSummary() {
ManagerMonitorInfo mmi = monitor.getMmi();
if (mmi == null) {
return new TabletServers();
}
TabletServers tserverInfo = new TabletServers(mmi.tServerInfo.size());
for (TabletServerStatus status : mmi.tServerInfo) {
tserverInfo.addTablet(new TabletServer(monitor, status));
}
tserverInfo.addBadTabletServer(ManagerResource.getTables(monitor));
return tserverInfo;
}
/**
* REST call to clear dead servers from list
*
* @param server
* Dead server to clear
*/
@POST
@Consumes(MediaType.TEXT_PLAIN)
public void clearDeadServer(
@QueryParam("server") @NotNull @Pattern(regexp = HOSTNAME_PORT_REGEX) String server) {
DeadServerList obit = new DeadServerList(monitor.getContext());
obit.delete(server);
}
/**
* Generates a recovery tserver list
*
* @return Recovery tserver list
*/
@Path("recovery")
@GET
public TabletServersRecovery getTserverRecovery() {
TabletServersRecovery recoveryList = new TabletServersRecovery();
ManagerMonitorInfo mmi = monitor.getMmi();
if (mmi == null) {
return new TabletServersRecovery();
}
for (TabletServerStatus server : mmi.tServerInfo) {
if (server.logSorts != null) {
for (RecoveryStatus recovery : server.logSorts) {
String serv = AddressUtil.parseAddress(server.name, false).getHost();
String log = recovery.name;
int time = recovery.runtime;
double progress = recovery.progress;
recoveryList.addRecovery(new TabletServerRecoveryInformation(serv, log, time, progress));
}
}
}
return recoveryList;
}
/**
* Generates details for the selected tserver
*
* @param tserverAddress
* TServer name
* @return TServer details
*/
@Path("{address}")
@GET
public TabletServerSummary getTserverDetails(
@PathParam("address") @NotNull @Pattern(regexp = HOSTNAME_PORT_REGEX) String tserverAddress)
throws Exception {
ManagerMonitorInfo mmi = monitor.getMmi();
if (mmi == null)
return new TabletServerSummary();
boolean tserverExists = false;
for (TabletServerStatus ts : mmi.getTServerInfo()) {
if (tserverAddress.equals(ts.getName())) {
tserverExists = true;
break;
}
}
if (!tserverExists) {
return null;
}
double splitStdDev = 0;
double minorStdDev = 0;
double minorQueueStdDev = 0;
double majorStdDev = 0;
double majorQueueStdDev = 0;
double currentMinorAvg = 0;
double currentMajorAvg = 0;
double currentMinorStdDev = 0;
double currentMajorStdDev = 0;
total =
new TabletStats(null, new ActionStats(), new ActionStats(), new ActionStats(), 0, 0, 0, 0);
HostAndPort address = HostAndPort.fromString(tserverAddress);
historical =
new TabletStats(null, new ActionStats(), new ActionStats(), new ActionStats(), 0, 0, 0, 0);
List<TabletStats> tsStats = new ArrayList<>();
try {
ClientContext context = monitor.getContext();
TabletClientService.Client client =
ThriftUtil.getClient(new TabletClientService.Client.Factory(), address, context);
try {
for (String tableId : mmi.tableMap.keySet()) {
tsStats.addAll(client.getTabletStats(TraceUtil.traceInfo(), context.rpcCreds(), tableId));
}
historical = client.getHistoricalStats(TraceUtil.traceInfo(), context.rpcCreds());
} finally {
ThriftUtil.returnClient(client);
}
} catch (Exception e) {
return null;
}
List<CurrentOperations> currentOps = doCurrentOperations(tsStats);
if (total.minors.num != 0) {
currentMinorAvg = (long) (total.minors.elapsed / total.minors.num);
}
if (total.minors.elapsed != 0 && total.minors.num != 0) {
currentMinorStdDev = stddev(total.minors.elapsed, total.minors.num, total.minors.sumDev);
}
if (total.majors.num != 0) {
currentMajorAvg = total.majors.elapsed / total.majors.num;
}
if (total.majors.elapsed != 0 && total.majors.num != 0
&& total.majors.elapsed > total.majors.num) {
currentMajorStdDev = stddev(total.majors.elapsed, total.majors.num, total.majors.sumDev);
}
ActionStatsUpdator.update(total.minors, historical.minors);
ActionStatsUpdator.update(total.majors, historical.majors);
minorStdDev = stddev(total.minors.elapsed, total.minors.num, total.minors.sumDev);
minorQueueStdDev = stddev(total.minors.queueTime, total.minors.num, total.minors.queueSumDev);
majorStdDev = stddev(total.majors.elapsed, total.majors.num, total.majors.sumDev);
majorQueueStdDev = stddev(total.majors.queueTime, total.majors.num, total.majors.queueSumDev);
splitStdDev =
stddev(historical.splits.elapsed, historical.splits.num, historical.splits.sumDev);
TabletServerDetailInformation details = doDetails(tsStats.size());
List<AllTimeTabletResults> allTime =
doAllTimeResults(majorQueueStdDev, minorQueueStdDev, splitStdDev, majorStdDev, minorStdDev);
CurrentTabletResults currentRes = doCurrentTabletResults(currentMinorAvg, currentMinorStdDev,
currentMajorAvg, currentMajorStdDev);
return new TabletServerSummary(details, allTime, currentRes, currentOps);
}
/**
* Generates the server stats
*
* @return Server stat list
*/
@Path("serverStats")
@GET
public ServerStats getServerStats() {
final int concurrentScans = monitor.getContext().getConfiguration().getScanExecutors().stream()
.mapToInt(sec -> sec.maxThreads).sum();
ServerStats stats = new ServerStats();
stats.addStats(
new ServerStat(ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors(), true,
100, "OS Load", "osload"));
stats.addStats(new ServerStat(1000, true, 1, "Ingest Entries", "ingest"));
stats.addStats(new ServerStat(10000, true, 1, "Scan Entries", "query"));
stats.addStats(new ServerStat(10, true, 10, "Ingest MB", "ingestMB"));
stats.addStats(new ServerStat(5, true, 10, "Scan MB", "queryMB"));
stats.addStats(new ServerStat(concurrentScans * 2, false, 1, "Running Scans", "scans"));
stats.addStats(new ServerStat(50, true, 10, "Scan Sessions", "scansessions"));
stats.addStats(new ServerStat(60000, false, 1, "Hold Time", "holdtime"));
stats.addStats(new ServerStat(1, false, 100, "Overall Avg", true, "allavg"));
stats.addStats(new ServerStat(1, false, 100, "Overall Max", true, "allmax"));
return stats;
}
private TabletServerDetailInformation doDetails(int numTablets) {
return new TabletServerDetailInformation(numTablets, total.numEntries, total.minors.status,
total.majors.status, historical.splits.status);
}
private List<AllTimeTabletResults> doAllTimeResults(double majorQueueStdDev,
double minorQueueStdDev, double splitStdDev, double majorStdDev, double minorStdDev) {
List<AllTimeTabletResults> allTime = new ArrayList<>();
// Minor Compaction Operation
allTime.add(new AllTimeTabletResults("Minor&nbsp;Compaction", total.minors.num,
total.minors.fail,
total.minors.num != 0 ? (total.minors.queueTime / total.minors.num) : null,
minorQueueStdDev, total.minors.num != 0 ? (total.minors.elapsed / total.minors.num) : null,
minorStdDev, total.minors.elapsed));
// Major Compaction Operation
allTime.add(new AllTimeTabletResults("Major&nbsp;Compaction", total.majors.num,
total.majors.fail,
total.majors.num != 0 ? (total.majors.queueTime / total.majors.num) : null,
majorQueueStdDev, total.majors.num != 0 ? (total.majors.elapsed / total.majors.num) : null,
majorStdDev, total.majors.elapsed));
// Split Operation
allTime.add(
new AllTimeTabletResults("Split", historical.splits.num, historical.splits.fail, null, null,
historical.splits.num != 0 ? (historical.splits.elapsed / historical.splits.num) : null,
splitStdDev, historical.splits.elapsed));
return allTime;
}
private CurrentTabletResults doCurrentTabletResults(double currentMinorAvg,
double currentMinorStdDev, double currentMajorAvg, double currentMajorStdDev) {
return new CurrentTabletResults(currentMinorAvg, currentMinorStdDev, currentMajorAvg,
currentMajorStdDev);
}
private List<CurrentOperations> doCurrentOperations(List<TabletStats> tsStats) throws Exception {
List<CurrentOperations> currentOperations = new ArrayList<>();
for (TabletStats info : tsStats) {
if (info.extent == null) {
historical = info;
continue;
}
total.numEntries += info.numEntries;
ActionStatsUpdator.update(total.minors, info.minors);
ActionStatsUpdator.update(total.majors, info.majors);
KeyExtent extent = KeyExtent.fromThrift(info.extent);
TableId tableId = extent.tableId();
String displayExtent = String.format("[%s]", extent.obscured());
String tableName = Tables.getPrintableTableInfoFromId(monitor.getContext(), tableId);
currentOperations.add(
new CurrentOperations(tableName, tableId, displayExtent, info.numEntries, info.ingestRate,
info.queryRate, info.minors.num != 0 ? info.minors.elapsed / info.minors.num : null,
stddev(info.minors.elapsed, info.minors.num, info.minors.sumDev),
info.minors.elapsed != 0 ? info.minors.count / info.minors.elapsed : null,
info.majors.num != 0 ? info.majors.elapsed / info.majors.num : null,
stddev(info.majors.elapsed, info.majors.num, info.majors.sumDev),
info.majors.elapsed != 0 ? info.majors.count / info.majors.elapsed : null));
}
return currentOperations;
}
private static double stddev(double elapsed, double num, double sumDev) {
if (num != 0) {
double average = elapsed / num;
return Math.sqrt((sumDev / num) - (average * average));
}
return 0;
}
}