/* * Copyright 2017 NAVER Corp. * * 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 com.navercorp.pinpoint.web.mapper.stat.sampling; import com.navercorp.pinpoint.common.server.bo.stat.AgentStatDataPoint; import com.navercorp.pinpoint.web.mapper.stat.sampling.sampler.AgentStatSampler; import com.navercorp.pinpoint.web.util.TimeWindow; import com.navercorp.pinpoint.web.vo.stat.SampledAgentStatDataPoint; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; /** * @author HyunGil Jeong */ public class EagerSamplingHandler<T extends AgentStatDataPoint, S extends SampledAgentStatDataPoint> implements AgentStatSamplingHandler<T, S> { private final TimeWindow timeWindow; private final AgentStatSampler<T, S> sampler; private final Map<Long, SamplingPartitionContext> samplingContexts = new HashMap<>(); private final Map<Long, SortedMap<Long, S>> sampledPointProjection = new TreeMap<>(); public EagerSamplingHandler(TimeWindow timeWindow, AgentStatSampler<T, S> sampler) { this.timeWindow = timeWindow; this.sampler = sampler; } public void addDataPoint(T dataPoint) { long startTimestamp = dataPoint.getStartTimestamp(); long timestamp = dataPoint.getTimestamp(); long timeslotTimestamp = timeWindow.refineTimestamp(timestamp); SamplingPartitionContext samplingContext = samplingContexts.get(startTimestamp); if (samplingContext == null) { samplingContext = new SamplingPartitionContext(timeslotTimestamp, dataPoint); samplingContexts.put(startTimestamp, samplingContext); } else { long timeslotTimestampToSample = samplingContext.getTimeslotTimestamp(); if (timeslotTimestampToSample == timeslotTimestamp) { samplingContext.addDataPoint(dataPoint); } else if (timeslotTimestampToSample > timeslotTimestamp){ S sampledPoint = samplingContext.sampleDataPoints(dataPoint); SortedMap<Long, S> sampledPoints = sampledPointProjection.get(timeslotTimestampToSample); if (sampledPoints == null) { sampledPoints = new TreeMap<>(); sampledPointProjection.put(timeslotTimestampToSample, sampledPoints); } sampledPoints.put(startTimestamp, sampledPoint); samplingContext = new SamplingPartitionContext(timeslotTimestamp, dataPoint); samplingContexts.put(startTimestamp, samplingContext); } else { // Results should be sorted in a descending order of their actual timestamp values // as they are stored using reverse timestamp. throw new IllegalStateException("Out of order AgentStatDataPoint"); } } } public List<S> getSampledDataPoints() { // sample remaining data point projections for (Map.Entry<Long, SamplingPartitionContext> e : samplingContexts.entrySet()) { long startTimestamp = e.getKey(); SamplingPartitionContext samplingPartitionContext = e.getValue(); long timeslotTimestamp = samplingPartitionContext.getTimeslotTimestamp(); S sampledDataPoint = samplingPartitionContext.sampleDataPoints(); SortedMap<Long, S> reduceCandidates = sampledPointProjection.get(timeslotTimestamp); if (reduceCandidates == null) { reduceCandidates = new TreeMap<>(); sampledPointProjection.put(timeslotTimestamp, reduceCandidates); } reduceCandidates.put(startTimestamp, sampledDataPoint); } // reduce projection if (sampledPointProjection.isEmpty()) { return Collections.emptyList(); } else { List<S> sampledDataPoints = new ArrayList<>(sampledPointProjection.size()); for (SortedMap<Long, S> sampledPointCandidates : sampledPointProjection.values()) { sampledDataPoints.add(reduceSampledPoints(sampledPointCandidates)); } return sampledDataPoints; } } private S reduceSampledPoints(SortedMap<Long, S> sampledPointCandidates) { Long lastKey = sampledPointCandidates.lastKey(); return sampledPointCandidates.get(lastKey); } private class SamplingPartitionContext { private final int timeslotIndex; private final long timeslotTimestamp; private final List<T> dataPoints = new ArrayList<>(); private SamplingPartitionContext(long timeslotTimestamp, T initialDataPoint) { this.timeslotTimestamp = timeslotTimestamp; this.dataPoints.add(initialDataPoint); this.timeslotIndex = timeWindow.getWindowIndex(this.timeslotTimestamp); } private void addDataPoint(T dataPoint) { this.dataPoints.add(dataPoint); } private long getTimeslotTimestamp() { return timeslotTimestamp; } private S sampleDataPoints() { return sampleDataPoints(null); } private S sampleDataPoints(T previousDataPoint) { return sampler.sampleDataPoints(timeslotIndex, timeslotTimestamp, dataPoints, previousDataPoint); } } }