/** * 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.sla; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Qualifier; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.AbstractIdleService; import com.google.inject.AbstractModule; import com.google.inject.Singleton; import org.apache.aurora.common.args.Arg; import org.apache.aurora.common.args.CmdLine; import org.apache.aurora.common.args.constraints.Positive; import org.apache.aurora.common.quantity.Amount; import org.apache.aurora.common.quantity.Time; import org.apache.aurora.scheduler.SchedulerServicesModule; import org.apache.aurora.scheduler.base.AsyncUtil; import org.apache.aurora.scheduler.sla.MetricCalculator.MetricCalculatorSettings; import org.apache.aurora.scheduler.sla.MetricCalculator.MetricCategory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.util.Objects.requireNonNull; import static org.apache.aurora.scheduler.sla.MetricCalculator.MetricCategory.JOB_UPTIMES; import static org.apache.aurora.scheduler.sla.MetricCalculator.MetricCategory.MEDIANS; import static org.apache.aurora.scheduler.sla.MetricCalculator.MetricCategory.PLATFORM_UPTIME; /** * Binding module for the sla processor. */ public class SlaModule extends AbstractModule { private static final Logger LOG = LoggerFactory.getLogger(SlaModule.class); @Positive @CmdLine(name = "sla_stat_refresh_interval", help = "The SLA stat refresh interval.") private static final Arg<Amount<Long, Time>> SLA_REFRESH_INTERVAL = Arg.create(Amount.of(1L, Time.MINUTES)); @CmdLine(name = "sla_prod_metrics", help = "Metric categories collected for production tasks.") private static final Arg<Set<MetricCategory>> SLA_PROD_METRICS = Arg.<Set<MetricCategory>>create(ImmutableSet.of(JOB_UPTIMES, PLATFORM_UPTIME, MEDIANS)); @CmdLine(name = "sla_non_prod_metrics", help = "Metric categories collected for non production tasks.") private static final Arg<Set<MetricCategory>> SLA_NON_PROD_METRICS = Arg.<Set<MetricCategory>>create(ImmutableSet.of()); @VisibleForTesting @Qualifier @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) @interface SlaExecutor { } private final Amount<Long, Time> refreshInterval; private final Set<MetricCategory> prodMetrics; private final Set<MetricCategory> nonProdMetrics; @VisibleForTesting SlaModule( Amount<Long, Time> refreshInterval, Set<MetricCategory> prodMetrics, Set<MetricCategory> nonProdMetrics) { this.refreshInterval = refreshInterval; this.prodMetrics = prodMetrics; this.nonProdMetrics = nonProdMetrics; } public SlaModule() { this(SLA_REFRESH_INTERVAL.get(), SLA_PROD_METRICS.get(), SLA_NON_PROD_METRICS.get()); } @Override protected void configure() { bind(MetricCalculatorSettings.class) .toInstance(new MetricCalculatorSettings( refreshInterval.as(Time.MILLISECONDS), prodMetrics, nonProdMetrics)); bind(MetricCalculator.class).in(Singleton.class); bind(ScheduledExecutorService.class) .annotatedWith(SlaExecutor.class) .toInstance(AsyncUtil.singleThreadLoggingScheduledExecutor("SlaStat-%d", LOG)); bind(SlaUpdater.class).in(Singleton.class); SchedulerServicesModule.addSchedulerActiveServiceBinding(binder()).to(SlaUpdater.class); } // TODO(ksweeney): This should use AbstractScheduledService. static class SlaUpdater extends AbstractIdleService { private final ScheduledExecutorService executor; private final MetricCalculator calculator; private final MetricCalculatorSettings settings; @Inject SlaUpdater( @SlaExecutor ScheduledExecutorService executor, MetricCalculator calculator, MetricCalculatorSettings settings) { this.executor = requireNonNull(executor); this.calculator = requireNonNull(calculator); this.settings = requireNonNull(settings); } @Override protected void startUp() { long interval = settings.getRefreshRateMs(); executor.scheduleAtFixedRate(calculator, interval, interval, TimeUnit.MILLISECONDS); LOG.debug("Scheduled SLA calculation with {} msec interval.", interval); } @Override protected void shutDown() { // Ignored. VM shutdown is required to stop computing SLAs. } } }