All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jersey.server.internal.monitoring.core.AbstractSlidingWindowTimeReservoir Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.6
Show newest version
/*
 * Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
 * Copyright 2010, 2013 Coda Hale and Yammer, Inc.
 *
 * 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.glassfish.jersey.server.internal.monitoring.core;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import static org.glassfish.jersey.server.internal.monitoring.core.ReservoirConstants.COLLISION_BUFFER;
import static org.glassfish.jersey.server.internal.monitoring.core.ReservoirConstants.COLLISION_BUFFER_POWER;

/**
 * An abstract {@link TimeReservoir} implementation backed by a sliding window that stores only the measurements made in the last
 * {@code N} seconds (or other startTime unit) and allows an update with data that happened in past (which is what makes it
 * different from Dropwizard's Metrics SlidingTimeWindowReservoir.
 * 

* The snapshot this reservoir returns has limitations as mentioned in {@link TimeReservoir}. *

* This reservoir is capable to store up to 2^{@link ReservoirConstants#COLLISION_BUFFER_POWER}, that is 256, in a granularity of * nanoseconds. In other words, up to 256 values that occurred at the same nanosecond can be stored in this reservoir. For * particular nanosecond, if the collision buffer exceeds, newly added values are thrown away. * * @param The type of values to store in this sliding window reservoir * @author Stepan Vavra * @see

Dropwizard's
 * Metrics SlidingTimeWindowReservoir
*/ public abstract class AbstractSlidingWindowTimeReservoir implements TimeReservoir { private final ConcurrentNavigableMap measurements; private final long window; private final AtomicLong greatestTick; private final AtomicLong updateCount; private final AtomicLong startTick; private final AtomicInteger trimOff; private final SlidingWindowTrimmer trimmer; private final long interval; private final TimeUnit intervalUnit; /** * Creates a new {@link SlidingWindowTimeReservoir} with the start time and window of startTime. * * @param window The window of startTime * @param windowUnit The unit of {@code window} * @param startTime The start time from which this reservoir calculates measurements * @param startTimeUnit The start time unit */ public AbstractSlidingWindowTimeReservoir(final long window, final TimeUnit windowUnit, final long startTime, final TimeUnit startTimeUnit) { this(window, windowUnit, startTime, startTimeUnit, null); } /** * Creates a new base sliding time window reservoir with the start time and a specified time window. * * @param window The window of startTime. * @param windowUnit The unit of {@code window}. * @param startTime The start time from which this reservoir calculates measurements. * @param startTimeUnit The start time unit. * @param trimmer The trimmer to use for trimming, if {@code null}, default trimmer is used. */ @SuppressWarnings("unchecked") public AbstractSlidingWindowTimeReservoir(final long window, final TimeUnit windowUnit, final long startTime, final TimeUnit startTimeUnit, final SlidingWindowTrimmer trimmer) { this.trimmer = trimmer != null ? trimmer : (SlidingWindowTrimmer) DefaultSlidingWindowTrimmerHolder.INSTANCE; this.measurements = new ConcurrentSkipListMap<>(); this.interval = window; this.intervalUnit = windowUnit; this.window = windowUnit.toNanos(window) << COLLISION_BUFFER_POWER; this.startTick = new AtomicLong(tick(startTime, startTimeUnit)); this.greatestTick = new AtomicLong(startTick.get()); this.updateCount = new AtomicLong(0); this.trimOff = new AtomicInteger(0); this.trimmer.setTimeReservoir(this); } @Override public int size(long time, TimeUnit timeUnit) { conditionallyUpdateGreatestTick(tick(time, timeUnit)); trim(); return measurements.size(); } @Override public void update(V value, long time, TimeUnit timeUnit) { if (updateCount.incrementAndGet() % ReservoirConstants.TRIM_THRESHOLD == 0) { trim(); } long tick = tick(time, timeUnit); for (int i = 0; i < COLLISION_BUFFER; ++i) { if (measurements.putIfAbsent(tick, value) == null) { conditionallyUpdateGreatestTick(tick); return; } // increase the tick, there should be up to COLLISION_BUFFER empty slots // where to put the value for given 'time' // if empty slot is not found, throw it away as we're getting inaccurate statistics anyway tick++; } } @Override public long interval(final TimeUnit timeUnit) { return timeUnit.convert(interval, intervalUnit); } private long conditionallyUpdateGreatestTick(final long tick) { while (true) { final long currentGreatestTick = greatestTick.get(); if (tick <= currentGreatestTick) { // the tick is too small, return the greatest one return currentGreatestTick; } if (greatestTick.compareAndSet(currentGreatestTick, tick)) { // successfully updated greatestTick with the tick return tick; } } } /** * Updates the startTick in case that the sliding window was created AFTER the time of a value that updated this window. * * @param firstEntry The first entry of the windowed measurments */ private void conditionallyUpdateStartTick(final Map.Entry firstEntry) { final Long firstEntryKey = firstEntry != null ? firstEntry.getKey() : null; if (firstEntryKey != null && firstEntryKey < startTick.get()) { while (true) { final long expectedStartTick = startTick.get(); if (startTick.compareAndSet(expectedStartTick, firstEntryKey)) { return; } } } } /** * Subclasses are required to instantiate {@link UniformTimeSnapshot} on their own. * * @param values The values to create the snapshot from * @param timeInterval The time interval this snapshot conforms to * @param timeIntervalUnit The interval unit of the time interval * @param time The time of the request of the snapshot * @param timeUnit The unit of the time of the snapshot request * @return The snapshot */ protected abstract UniformTimeSnapshot snapshot(final Collection values, final long timeInterval, final TimeUnit timeIntervalUnit, final long time, final TimeUnit timeUnit); @Override public UniformTimeSnapshot getSnapshot(long time, TimeUnit timeUnit) { trimOff.incrementAndGet(); final long baselineTick = conditionallyUpdateGreatestTick(tick(time, timeUnit)); try { // now, with the 'baselineTick' we can be sure that no trim will be performed // we just cannot guarantee that 'time' will correspond with the 'baselineTick' which is what the API warns about final ConcurrentNavigableMap windowMap = measurements .subMap((roundTick(baselineTick)) - window, true, baselineTick, true); // if the first update came with value lower that the 'startTick' we need to extend the window size so that the // calculation depending on the actual measured interval is not unnecessary boosted conditionallyUpdateStartTick(windowMap.firstEntry()); // calculate the actual measured interval final long measuredTickInterval = Math.min(baselineTick - startTick.get(), window); return snapshot(windowMap.values(), measuredTickInterval >> COLLISION_BUFFER_POWER, TimeUnit.NANOSECONDS, time, timeUnit); } finally { trimOff.decrementAndGet(); trim(baselineTick); } } private long tick(long time, TimeUnit timeUnit) { return timeUnit.toNanos(time) << COLLISION_BUFFER_POWER; } private void trim() { trim(greatestTick.get()); } private void trim(final long baselineTick) { if (trimEnabled()) { final long key = roundTick(baselineTick) - window; trimmer.trim(measurements, key); } } private boolean trimEnabled() { return trimOff.get() == 0; } /** * The purpose of this method is to deal with the fact that data for the same nanosecond can be distributed in an interval * [0,256). By rounding the tick, we get the tick to which all the other ticks from the same interval belong. * * @param tick The tick * @return The rounded tick */ private long roundTick(final long tick) { // tick / COLLISION_BUFFER * COLLISION_BUFFER return tick >> COLLISION_BUFFER_POWER << COLLISION_BUFFER_POWER; } /** * The holder of the lazy loaded instance of the default trimmer. */ private static final class DefaultSlidingWindowTrimmerHolder { /** * The default instance of sliding window trimmer. */ static final SlidingWindowTrimmer INSTANCE = new SlidingWindowTrimmer() { @Override public void trim(final ConcurrentNavigableMap map, final long key) { map.headMap(key).clear(); } @Override public void setTimeReservoir(final TimeReservoir reservoir) { // not used } }; } }