HboDeleteStaleStatisticsCommand.java
// 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.doris.nereids.trees.plans.commands;
import org.apache.doris.catalog.Env;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.nereids.trees.plans.PlanType;
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.StmtExecutor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Manual hbo statistics clean up:
* <pre>
* HBO DELETE STALE STATISTICS; -- entries whose data moved beyond the tolerance
* HBO DELETE STALE STATISTICS OLDER_THAN 3600; -- ... and unjudgeable ones older than 1h
* </pre>
* A hbo key does not contain any data state, so an entry stays matchable while a table grows; what
* makes it useless is the read side verdict on the baseline the entry recorded
* (see {@link org.apache.doris.nereids.stats.HboStructFreshness}): an entry whose data moved by more
* than {@code Config.hbo_row_count_change_ratio} is not applied any more and is removed here,
* together with - when {@code OLDER_THAN} is given - the entries which cannot be judged at all
* (their recorded state does not resolve to existing tables, they prune partitions so their rows
* cannot be compared with a whole table, or they record no state). Entries which are merely
* {@code drifted} are kept: they are still applied. Pinned entries only: learned entries are keyed
* by an internally generated fingerprint and carry no recorded state of their own.
*/
public class HboDeleteStaleStatisticsCommand extends Command {
private static final Logger LOG = LogManager.getLogger(HboDeleteStaleStatisticsCommand.class);
private final Long olderThanSeconds;
/**
* HboDeleteStaleStatisticsCommand
* @param olderThanSeconds when non null, also remove entries which cannot be resolved as soon as
* they are older than this many seconds
*/
public HboDeleteStaleStatisticsCommand(Long olderThanSeconds) {
super(PlanType.HBO_STATISTICS_COMMAND);
this.olderThanSeconds = olderThanSeconds;
}
@Override
public void run(ConnectContext ctx, StmtExecutor executor) throws Exception {
if (!Env.getCurrentEnv().getAccessManager()
.checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN)) {
throw new AnalysisException("Access denied: HBO statistics management requires ADMIN privilege");
}
if (olderThanSeconds != null
&& (olderThanSeconds < 0 || olderThanSeconds > Long.MAX_VALUE / 1000)) {
// a negative value would disable the clause instead of applying it, and a value whose
// millisecond conversion overflows would silently become negative
throw new AnalysisException("hbo delete stale statistics OLDER_THAN must be a non-negative"
+ " number of seconds no greater than " + (Long.MAX_VALUE / 1000)
+ ": " + olderThanSeconds);
}
long olderThanMillis = olderThanSeconds == null ? -1 : olderThanSeconds * 1000;
int removed = Env.getCurrentEnv().getHboPlanStatisticsManager()
.deleteStalePinnedPlanStatistics(olderThanMillis);
LOG.info("HBO DELETE STALE STATISTICS removed {} pinned entries (olderThanSeconds={})",
removed, olderThanSeconds);
}
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitCommand(this, context);
}
public Long getOlderThanSeconds() {
return olderThanSeconds;
}
}