com.spotify.heroic.aggregation.ChainInstance.java Source code

Java tutorial

Introduction

Here is the source code for com.spotify.heroic.aggregation.ChainInstance.java

Source

/*
 * 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.aggregation;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.spotify.heroic.ObjectHasher;
import com.spotify.heroic.common.DateRange;
import com.spotify.heroic.common.Series;
import com.spotify.heroic.common.Statistics;
import com.spotify.heroic.metric.Event;
import com.spotify.heroic.metric.MetricGroup;
import com.spotify.heroic.metric.Payload;
import com.spotify.heroic.metric.Point;
import com.spotify.heroic.metric.Spread;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.Data;
import lombok.RequiredArgsConstructor;

/**
 * A special aggregation method that is a chain of other aggregation methods.
 *
 * @author udoprog
 */
@Data
public class ChainInstance implements AggregationInstance {
    private final List<AggregationInstance> chain;

    /**
     * The last aggregation in the chain determines the estimated number of samples.
     */
    @Override
    public long estimate(DateRange range) {
        return chain.get(chain.size() - 1).estimate(range);
    }

    /**
     * The last aggregation in the chain determines the cadence.
     */
    @Override
    public long cadence() {
        return chain.stream().map(AggregationInstance::cadence).filter(c -> c >= 0).reduce((a, b) -> b).orElse(-1L);
    }

    @Override
    public AggregationInstance distributed() {
        final Iterator<AggregationInstance> it = chain.iterator();

        final ImmutableList.Builder<AggregationInstance> chain = ImmutableList.builder();
        AggregationInstance last = it.next();

        if (!last.distributable()) {
            return EmptyInstance.INSTANCE;
        }

        while (it.hasNext()) {
            final AggregationInstance next = it.next();

            if (!next.distributable()) {
                chain.add(last.distributed());
                return fromList(chain.build());
            }

            chain.add(last);
            last = next;
        }

        chain.add(last.distributed());
        return fromList(chain.build());
    }

    @Override
    public AggregationInstance reducer() {
        final Iterator<AggregationInstance> it = chain.iterator();
        AggregationInstance last = it.next();

        final ImmutableList.Builder<AggregationInstance> chain = ImmutableList.builder();

        if (!last.distributable()) {
            chain.add(EmptyInstance.INSTANCE);
            chain.add(last);

            while (it.hasNext()) {
                chain.add(it.next());
            }

            return fromList(chain.build());
        }

        while (it.hasNext()) {
            final AggregationInstance next = it.next();

            if (!next.distributable()) {
                chain.add(last.reducer());
                chain.add(next);

                while (it.hasNext()) {
                    chain.add(it.next());
                }

                return fromList(chain.build());
            }

            last = next;
        }

        return last.reducer();
    }

    @Override
    public AggregationSession session(final DateRange range, final RetainQuotaWatcher watcher,
            final BucketStrategy bucketStrategy) {
        final Iterator<AggregationInstance> it = chain.iterator();

        final AggregationInstance first = it.next();
        final AggregationSession head = first.session(range, watcher, bucketStrategy);

        final List<AggregationSession> tail = new ArrayList<>();

        while (it.hasNext()) {
            final AggregationSession s = it.next().session(range, watcher, bucketStrategy);
            tail.add(s);
        }

        return new Session(head, tail);
    }

    @Override
    public Set<String> requiredTags() {
        return chain.iterator().next().requiredTags();
    }

    @Override
    public void hashTo(final ObjectHasher hasher) {
        hasher.putObject(getClass(), () -> {
            hasher.putField("chain", chain, hasher.list(hasher.with(AggregationInstance::hashTo)));
        });
    }

    private static final Joiner CHAIN_JOINER = Joiner.on(" -> ");

    @Override
    public String toString() {
        return "[" + CHAIN_JOINER.join(chain.stream().map(Object::toString).iterator()) + "]";
    }

    public static AggregationInstance of(final AggregationInstance... chain) {
        return fromList(ImmutableList.copyOf(chain));
    }

    public static AggregationInstance fromList(final List<AggregationInstance> chain) {
        final List<AggregationInstance> c = flattenChain(chain);

        if (c.size() == 1) {
            return c.iterator().next();
        }

        return new ChainInstance(c);
    }

    private static List<AggregationInstance> flattenChain(final List<AggregationInstance> chain) {
        final ImmutableList.Builder<AggregationInstance> child = ImmutableList.builder();

        for (final AggregationInstance i : chain) {
            if (i instanceof ChainInstance) {
                child.addAll(flattenChain(ChainInstance.class.cast(i).getChain()));
            } else {
                child.add(i);
            }
        }

        return child.build();
    }

    @RequiredArgsConstructor
    private static final class Session implements AggregationSession {
        private final AggregationSession first;
        private final Iterable<AggregationSession> rest;

        @Override
        public void updatePoints(Map<String, String> key, Set<Series> series, List<Point> values) {
            first.updatePoints(key, series, values);
        }

        @Override
        public void updateEvents(Map<String, String> key, Set<Series> series, List<Event> values) {
            first.updateEvents(key, series, values);
        }

        @Override
        public void updateSpreads(Map<String, String> key, Set<Series> series, List<Spread> values) {
            first.updateSpreads(key, series, values);
        }

        @Override
        public void updateGroup(Map<String, String> key, Set<Series> series, List<MetricGroup> values) {
            first.updateGroup(key, series, values);
        }

        @Override
        public void updatePayload(Map<String, String> key, Set<Series> series, List<Payload> values) {
            first.updatePayload(key, series, values);
        }

        @Override
        public AggregationResult result() {
            final AggregationResult firstResult = first.result();
            List<AggregationOutput> current = firstResult.getResult();
            Statistics statistics = firstResult.getStatistics();

            for (final AggregationSession session : rest) {
                for (final AggregationOutput u : current) {
                    u.getMetrics().updateAggregation(session, u.getKey(), u.getSeries());
                }

                final AggregationResult next = session.result();
                current = next.getResult();
                statistics = statistics.merge(next.getStatistics());
            }

            return new AggregationResult(current, statistics);
        }

        @Override
        public String toString() {
            if (rest.iterator().hasNext()) {
                return "[" + first + ", " + Joiner.on(", ").join(rest) + "]";
            }

            return "[" + first + "]";
        }
    }
}