/** * Licensed 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.aurora.scheduler.pruning; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import javax.inject.Inject; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.util.concurrent.AbstractIdleService; import org.apache.aurora.common.quantity.Amount; import org.apache.aurora.common.quantity.Time; import org.apache.aurora.common.stats.StatsProvider; import org.apache.aurora.common.util.Clock; import org.apache.aurora.scheduler.storage.Storage; import org.apache.aurora.scheduler.storage.Storage.MutateWork.NoResult; import org.apache.aurora.scheduler.storage.entities.IJobUpdateKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.Objects.requireNonNull; /** * Prunes per-job update history on a periodic basis. */ class JobUpdateHistoryPruner extends AbstractIdleService { private static final Logger LOG = LoggerFactory.getLogger(JobUpdateHistoryPruner.class); @VisibleForTesting static final String JOB_UPDATES_PRUNED = "job_updates_pruned"; private final Clock clock; private final ScheduledExecutorService executor; private final Storage storage; private final HistoryPrunerSettings settings; private final AtomicLong prunedUpdatesCount; static class HistoryPrunerSettings { private final Amount<Long, Time> pruneInterval; private final Amount<Long, Time> maxHistorySize; private final int maxUpdatesPerJob; HistoryPrunerSettings( Amount<Long, Time> pruneInterval, Amount<Long, Time> maxHistorySize, int maxUpdatesPerJob) { this.pruneInterval = requireNonNull(pruneInterval); this.maxHistorySize = requireNonNull(maxHistorySize); this.maxUpdatesPerJob = maxUpdatesPerJob; } } @Inject JobUpdateHistoryPruner( Clock clock, ScheduledExecutorService executor, Storage storage, HistoryPrunerSettings settings, StatsProvider statsProvider) { this.clock = requireNonNull(clock); this.executor = requireNonNull(executor); this.storage = requireNonNull(storage); this.settings = requireNonNull(settings); this.prunedUpdatesCount = statsProvider.makeCounter(JOB_UPDATES_PRUNED); } @Override protected void startUp() { executor.scheduleAtFixedRate( () -> storage.write((NoResult.Quiet) storeProvider -> { Set<IJobUpdateKey> prunedUpdates = storeProvider.getJobUpdateStore().pruneHistory( settings.maxUpdatesPerJob, clock.nowMillis() - settings.maxHistorySize.as(Time.MILLISECONDS)); prunedUpdatesCount.addAndGet(prunedUpdates.size()); LOG.info(prunedUpdates.isEmpty() ? "No job update history to prune." : "Pruned job update history: " + Joiner.on(",").join(prunedUpdates)); }), settings.pruneInterval.as(Time.MILLISECONDS), settings.pruneInterval.as(Time.MILLISECONDS), TimeUnit.MILLISECONDS); } @Override protected void shutDown() { // Nothing to do - await VM shutdown. } }