/* * 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.ingestion; import com.spotify.heroic.common.Statistics; import com.spotify.heroic.filter.Filter; import com.spotify.heroic.metadata.MetadataBackend; import com.spotify.heroic.metadata.MetadataManager; import com.spotify.heroic.metric.MetricBackend; import com.spotify.heroic.metric.MetricManager; import com.spotify.heroic.statistics.IngestionManagerReporter; import com.spotify.heroic.suggest.SuggestBackend; import com.spotify.heroic.suggest.SuggestManager; import eu.toolchain.async.AsyncFramework; import eu.toolchain.async.AsyncFuture; import javax.inject.Inject; import javax.inject.Named; import java.util.Optional; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.LongAdder; import java.util.function.Function; import java.util.function.Supplier; import static com.google.common.base.Preconditions.checkNotNull; public class IngestionManagerImpl implements IngestionManager { final AsyncFramework async; final MetadataManager metadata; final MetricManager metric; final SuggestManager suggest; final IngestionManagerReporter reporter; private final boolean updateMetrics; private final boolean updateMetadata; private final boolean updateSuggestions; private volatile Filter filter; private final Semaphore writePermits; private final LongAdder ingested = new LongAdder(); /** * @param updateMetrics Ingested metrics will update metric backends. * @param updateMetadata Ingested metrics will update metadata backends. * @param updateSuggestions Ingested metrics will update suggest backends. * @param maxConcurrentWrites Limit the number of concurrent writes, 0 means no limit at all */ @Inject public IngestionManagerImpl( final AsyncFramework async, final MetadataManager metadata, final MetricManager metric, final SuggestManager suggest, final IngestionManagerReporter reporter, @Named("updateMetrics") final boolean updateMetrics, @Named("updateMetadata") final boolean updateMetadata, @Named("updateSuggestions") final boolean updateSuggestions, @Named("maxConcurrentWrites") final int maxConcurrentWrites, final Filter filter ) { this.async = async; this.metadata = metadata; this.metric = metric; this.suggest = suggest; this.reporter = reporter; this.updateMetrics = updateMetrics; this.updateMetadata = updateMetadata; this.updateSuggestions = updateSuggestions; this.filter = filter; this.writePermits = new Semaphore(maxConcurrentWrites); } @Override public IngestionGroup useOptionalGroup(final Optional<String> group) { return buildGroup(group, metric::useOptionalGroup, metadata::useOptionalGroup, suggest::useOptionalGroup); } @Override public AsyncFuture<Void> setFilter(Filter filter) { this.filter = checkNotNull(filter, "filter"); return async.resolved(); } @Override public AsyncFuture<Filter> getFilter() { return async.resolved(filter); } @Override public Statistics getStatistics() { return Statistics.of(INGESTED, ingested.sum(), AVAILABLE_WRITE_PERMITS, writePermits.availablePermits()); } private <I> IngestionGroup buildGroup( final I input, Function<I, MetricBackend> metric, Function<I, MetadataBackend> metadata, Function<I, SuggestBackend> suggest ) { // @formatter:off return new CoreIngestionGroup( async, () -> filter, writePermits, reporter, ingested, optionally(updateMetrics, () -> metric.apply(input)), optionally(updateMetadata, () -> metadata.apply(input)), optionally(updateSuggestions, () -> suggest.apply(input)) ); // @formatter:on } private <T> Optional<T> optionally(final boolean shouldSupply, final Supplier<T> supplier) { if (!shouldSupply) { return Optional.empty(); } return Optional.of(supplier.get()); } }