blob: 6a82dde4c697be7eb6d5ba936f5aeb36d8c68b64 [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.core.spi.balancer.util;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.accumulo.core.data.TabletId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Throttles logging of balancer problems by storing the last log time for each problem and limiting
* reports to once per minute. The logger is generated internally from the supplied class in order
* to adhere to the requirements for the SPI package (which prohibits having the Logger class in the
* API).
*/
public class ThrottledBalancerProblemReporter {
public interface Problem {
void report();
}
public interface OutstandingMigrationsProblem extends Problem {
void setMigrations(Set<TabletId> migrations);
}
private static final long TIME_BETWEEN_WARNINGS = TimeUnit.SECONDS.toMillis(60);
private final WeakHashMap<Problem,Long> problemReportTimes = new WeakHashMap<>();
private final Logger log;
public ThrottledBalancerProblemReporter(Class<?> loggerClass) {
log = LoggerFactory.getLogger(loggerClass);
}
/**
* Create a new problem reporter to indicate there are no tablet servers available and balancing
* could not be performed. Balancers should only create a single instance of this problem, and
* reuse each time the problem is reported.
*/
public Problem createNoTabletServersProblem() {
return () -> log.warn("Not balancing because we don't have any tservers.");
}
/**
* Create a new problem reporter to indicate that balancing could not be performed due to the
* existence of outstanding migrations. Balancers should only create a single instance of this
* problem and update its migrations list before each report.
*/
public OutstandingMigrationsProblem createOutstandingMigrationsProblem() {
return new OutstandingMigrationsProblem() {
private Set<TabletId> migrations = Collections.emptySet();
@Override
public void setMigrations(Set<TabletId> migrations) {
this.migrations = migrations;
}
@Override
public void report() {
log.warn("Not balancing due to {} outstanding migrations.", migrations.size());
/*
* TODO ACCUMULO-2938 redact key extents in this output to avoid leaking protected
* information.
*/
if (log.isDebugEnabled()) {
log.debug("Sample up to 10 outstanding migrations: {}",
migrations.stream().limit(10).map(String::valueOf).collect(Collectors.joining(", ")));
}
// Now that we've reported, clear out the migrations list so we don't hold it in memory.
migrations = Collections.emptySet();
}
};
}
/**
* Reports a balance problem. The {@link Problem#report()} will only be called up to once a minute
* for each problem that is reported repeatedly.
*/
public void reportProblem(Problem problem) {
long reportTime = problemReportTimes.getOrDefault(problem, -1L);
if ((System.currentTimeMillis() - reportTime) > TIME_BETWEEN_WARNINGS) {
problem.report();
problemReportTimes.put(problem, System.currentTimeMillis());
}
}
/**
* Clears reported problems so that a problem report will be logged immediately the next time
* {@link #reportProblem(Problem)} is invoked.
*/
public void clearProblemReportTimes() {
problemReportTimes.clear();
}
}