/* * Copyright (c) 2015 Spotify AB. * * 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 com.spotify.heroic.metric; import static com.spotify.heroic.common.Optionals.mergeOptionalList; import static com.spotify.heroic.common.Optionals.pickOptional; import static java.util.Optional.empty; import static java.util.Optional.of; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.spotify.heroic.analytics.MetricAnalytics; import com.spotify.heroic.common.GroupSet; import com.spotify.heroic.common.ModuleIdBuilder; import com.spotify.heroic.common.OptionalLimit; import com.spotify.heroic.dagger.CorePrimaryComponent; import com.spotify.heroic.lifecycle.LifeCycle; import com.spotify.heroic.statistics.HeroicReporter; import com.spotify.heroic.statistics.MetricBackendReporter; import dagger.Module; import dagger.Provides; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; import javax.inject.Named; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @Module public class MetricManagerModule { public static final int DEFAULT_FETCH_PARALLELISM = 100; public static final boolean DEFAULT_FAIL_ON_LIMITS = false; public static final long DEFAULT_SMALL_QUERY_THRESHOLD = 200000; private final List<MetricModule> backends; private final Optional<List<String>> defaultBackends; /** * Limit in how many groups we are allowed to return. */ private final OptionalLimit groupLimit; /** * Limit in the number of series we may fetch from the metadata backend. */ private final OptionalLimit seriesLimit; /** * Limit in how many datapoints a single aggregation is allowed to output. */ private final OptionalLimit aggregationLimit; /** * Limit in how many datapoints a session is allowed to fetch in total. */ private final OptionalLimit dataLimit; /** * How many data fetches are performed in parallel. */ private final int fetchParallelism; /** * If {@code true}, will cause any limits applied to be reported as a failure. */ private final boolean failOnLimits; /** * Threshold for defining a "small" query, measured in pre-aggregation sample size */ private final long smallQueryThreshold; @Provides @MetricScope public MetricBackendReporter reporter(HeroicReporter reporter) { return reporter.newMetricBackend(); } @Provides @MetricScope public GroupSet<MetricBackend> defaultBackends( Set<MetricBackend> configured, MetricAnalytics analytics ) { return GroupSet.build( ImmutableSet.copyOf(configured.stream().map(analytics::wrap).iterator()), defaultBackends); } @Provides @MetricScope public List<MetricModule.Exposed> components( final CorePrimaryComponent primary, final MetricBackendReporter reporter ) { final List<MetricModule.Exposed> exposed = new ArrayList<>(); final ModuleIdBuilder idBuilder = new ModuleIdBuilder(); for (final MetricModule m : backends) { final String id = idBuilder.buildId(m); final MetricModule.Depends depends = new MetricModule.Depends(reporter); exposed.add(m.module(primary, depends, id)); } return exposed; } @Provides @MetricScope public Set<MetricBackend> backends( List<MetricModule.Exposed> components, MetricBackendReporter reporter ) { return ImmutableSet.copyOf(components .stream() .map(MetricModule.Exposed::backend) .map(reporter::decorate) .iterator()); } @Provides @MetricScope @Named("metric") public LifeCycle metricLife(List<MetricModule.Exposed> components) { return LifeCycle.combined(components.stream().map(MetricModule.Exposed::life)); } @Provides @MetricScope @Named("groupLimit") public OptionalLimit groupLimit() { return groupLimit; } @Provides @MetricScope @Named("seriesLimit") public OptionalLimit seriesLimit() { return seriesLimit; } @Provides @MetricScope @Named("aggregationLimit") public OptionalLimit aggregationLimit() { return aggregationLimit; } @Provides @MetricScope @Named("dataLimit") public OptionalLimit dataLimit() { return dataLimit; } @Provides @MetricScope @Named("fetchParallelism") public int fetchParallelism() { return fetchParallelism; } @Provides @MetricScope @Named("failOnLimits") public boolean failOnLimits() { return failOnLimits; } @Provides @MetricScope @Named("smallQueryThreshold") public long smallQueryThreshold() { return smallQueryThreshold; } public static Builder builder() { return new Builder(); } @NoArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor public static class Builder { private Optional<List<MetricModule>> backends = empty(); private Optional<List<String>> defaultBackends = empty(); private OptionalLimit groupLimit = OptionalLimit.empty(); private OptionalLimit seriesLimit = OptionalLimit.empty(); private OptionalLimit aggregationLimit = OptionalLimit.empty(); private OptionalLimit dataLimit = OptionalLimit.empty(); private Optional<Integer> fetchParallelism = empty(); private Optional<Boolean> failOnLimits = empty(); private Optional<Long> smallQueryThreshold = empty(); public Builder backends(List<MetricModule> backends) { this.backends = of(backends); return this; } public Builder defaultBackends(List<String> defaultBackends) { this.defaultBackends = of(defaultBackends); return this; } public Builder groupLimit(long groupLimit) { this.groupLimit = OptionalLimit.of(groupLimit); return this; } public Builder seriesLimit(long seriesLimit) { this.seriesLimit = OptionalLimit.of(seriesLimit); return this; } public Builder aggregationLimit(long aggregationLimit) { this.aggregationLimit = OptionalLimit.of(aggregationLimit); return this; } public Builder dataLimit(long dataLimit) { this.dataLimit = OptionalLimit.of(dataLimit); return this; } public Builder fetchParallelism(Integer fetchParallelism) { this.fetchParallelism = of(fetchParallelism); return this; } public Builder failOnLimits(boolean failOnLimits) { this.failOnLimits = of(failOnLimits); return this; } public Builder smallQueryThreshold(long smallQueryThreshold) { this.smallQueryThreshold = of(smallQueryThreshold); return this; } public Builder merge(final Builder o) { // @formatter:off return new Builder( mergeOptionalList(o.backends, backends), mergeOptionalList(o.defaultBackends, defaultBackends), groupLimit.orElse(o.groupLimit), seriesLimit.orElse(o.seriesLimit), aggregationLimit.orElse(o.aggregationLimit), dataLimit.orElse(o.dataLimit), pickOptional(fetchParallelism, o.fetchParallelism), pickOptional(failOnLimits, o.failOnLimits), pickOptional(smallQueryThreshold, o.smallQueryThreshold) ); // @formatter:on } public MetricManagerModule build() { // @formatter:off return new MetricManagerModule( backends.orElseGet(ImmutableList::of), defaultBackends, groupLimit, seriesLimit, aggregationLimit, dataLimit, fetchParallelism.orElse(DEFAULT_FETCH_PARALLELISM), failOnLimits.orElse(DEFAULT_FAIL_ON_LIMITS), smallQueryThreshold.orElse(DEFAULT_SMALL_QUERY_THRESHOLD) ); // @formatter:on } } }