Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit be8b133

Browse files
Marek PotociarGerrit Code Review
authored andcommitted
Merge "JERSEY-2854: Jersey monitoring fixes."
2 parents 6e87d76 + a716e61 commit be8b133

File tree

8 files changed

+1038
-232
lines changed

8 files changed

+1038
-232
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*
40+
* This file incorporates work covered by the following copyright and
41+
* permission notice:
42+
*
43+
* Copyright 2010-2013 Coda Hale and Yammer, Inc., 2014-2015 Dropwizard Team
44+
*
45+
* Licensed under the Apache License, Version 2.0 (the "License");
46+
* you may not use this file except in compliance with the License.
47+
* You may obtain a copy of the License at
48+
*
49+
* http://www.apache.org/licenses/LICENSE-2.0
50+
*
51+
* Unless required by applicable law or agreed to in writing, software
52+
* distributed under the License is distributed on an "AS IS" BASIS,
53+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
54+
* See the License for the specific language governing permissions and
55+
* limitations under the License.
56+
*/
57+
58+
package org.glassfish.jersey.server.internal.monitoring;
59+
60+
import java.util.Collection;
61+
import java.util.concurrent.ConcurrentNavigableMap;
62+
import java.util.concurrent.ConcurrentSkipListMap;
63+
import java.util.concurrent.TimeUnit;
64+
import java.util.concurrent.atomic.AtomicInteger;
65+
import java.util.concurrent.atomic.AtomicLong;
66+
67+
/**
68+
* A {@link TimeReservoir} implementation backed by a sliding window that stores only the measurements made in the last {@code N}
69+
* seconds (or other startTime unit) and allows an update with data that happened in past (which is what makes it different from
70+
* Dropwizard's Metrics SlidingTimeWindowReservoir.
71+
* <p/>
72+
* The snapshot this reservoir returns has limitations as mentioned in {@link TimeReservoir}.
73+
* <p/>
74+
* This reservoir is capable to store up to 2^{@link #COLLISION_BUFFER_POWER}, that is 256, in a granularity of nanoseconds. In
75+
* other words, up to 256 values that occurred at the same nanosecond can be stored in this reservoir. For particular nanosecond,
76+
* if the collision buffer exceeds, newly added values are thrown away.
77+
*
78+
* @author Stepan Vavra (stepan.vavra at oracle.com)
79+
* @see <pre><a href="https://github.com/dropwizard/metrics/blob/master/metrics-core/src/main/java/io/dropwizard/metrics/SlidingTimeWindowReservoir.java">Dropwizrd's Metrics SlidingTimeWindowReservoir</a></pre>
80+
*/
81+
public class SlidingWindowTimeReservoir implements TimeReservoir {
82+
83+
// allow for 2^that many duplicate ticks before throwing away measurements
84+
private static final int COLLISION_BUFFER_POWER = 8;
85+
private static final int COLLISION_BUFFER = 1 << COLLISION_BUFFER_POWER; // 256
86+
// only trim on updating once every N
87+
private static final int TRIM_THRESHOLD = 256;
88+
89+
private final ConcurrentNavigableMap<Long, Long> measurements;
90+
private final long window;
91+
private final AtomicLong greatestTick;
92+
private final AtomicLong updateCount;
93+
private final long startTick;
94+
private final AtomicInteger trimOff;
95+
96+
/**
97+
* Creates a new {@link SlidingWindowTimeReservoir} with the start time and window of startTime.
98+
*
99+
* @param window the window of startTime
100+
* @param windowUnit the unit of {@code window}
101+
*/
102+
public SlidingWindowTimeReservoir(long window, TimeUnit windowUnit, long startTime, TimeUnit startTimeUnit) {
103+
this.measurements = new ConcurrentSkipListMap<>();
104+
this.window = windowUnit.toNanos(window) << COLLISION_BUFFER_POWER;
105+
this.startTick = tick(startTime, startTimeUnit);
106+
this.greatestTick = new AtomicLong(startTick);
107+
this.updateCount = new AtomicLong(0);
108+
this.trimOff = new AtomicInteger(0);
109+
}
110+
111+
@Override
112+
public int size(long time, TimeUnit timeUnit) {
113+
conditionallyUpdateGreatestTick(tick(time, timeUnit));
114+
trim();
115+
return measurements.size();
116+
}
117+
118+
@Override
119+
public void update(long value, long time, TimeUnit timeUnit) {
120+
if (updateCount.incrementAndGet() % TRIM_THRESHOLD == 0) {
121+
trim();
122+
}
123+
124+
long tick = tick(time, timeUnit);
125+
if (greatestTick.get() - window > tick) {
126+
// the value is too old that it doesn't even fit into the window
127+
return;
128+
}
129+
for (int i = 0; i < COLLISION_BUFFER; ++i) {
130+
if (measurements.putIfAbsent(tick, value) == null) {
131+
conditionallyUpdateGreatestTick(tick);
132+
break;
133+
}
134+
// increase the tick, there should be up to COLLISION_BUFFER empty slots
135+
// where to put the value for given 'time'
136+
// if empty slot is not found, throw it away as we're getting inaccurate statistics anyway
137+
tick++;
138+
}
139+
}
140+
141+
private long conditionallyUpdateGreatestTick(final long tick) {
142+
for (;;) {
143+
final long currentGreatestTick = greatestTick.get();
144+
if (tick <= currentGreatestTick) {
145+
// the tick is too small, return the greatest one
146+
return currentGreatestTick;
147+
}
148+
if (greatestTick.compareAndSet(currentGreatestTick, tick)) {
149+
// successfully updated greatestTick with the tick
150+
return tick;
151+
}
152+
}
153+
}
154+
155+
@Override
156+
public UniformTimeSnapshot getSnapshot(long time, TimeUnit timeUnit) {
157+
trimOff.incrementAndGet();
158+
final long baselineTick = conditionallyUpdateGreatestTick(tick(time, timeUnit));
159+
try {
160+
// now, with the 'baselineTick' we can be sure that no trim will be performed
161+
// we just cannot guarantee that 'time' will correspond with the 'baselineTick' which is what the API warns about
162+
final long measuredTickInterval = Math.min(baselineTick - startTick, window);
163+
final Collection<Long> values = measurements
164+
.subMap((roundTick(baselineTick)) - measuredTickInterval, true, baselineTick, true)
165+
.values();
166+
return new UniformTimeSnapshot(values, measuredTickInterval >> COLLISION_BUFFER_POWER, TimeUnit.NANOSECONDS);
167+
} finally {
168+
trimOff.decrementAndGet();
169+
trim(baselineTick);
170+
}
171+
}
172+
173+
private long tick(long time, TimeUnit timeUnit) {
174+
return timeUnit.toNanos(time) << COLLISION_BUFFER_POWER;
175+
}
176+
177+
private void trim() {
178+
trim(greatestTick.get());
179+
}
180+
181+
private void trim(final long baselineTick) {
182+
if (trimEnabled()) {
183+
measurements.headMap(roundTick(baselineTick) - window).clear();
184+
}
185+
}
186+
187+
private boolean trimEnabled() {
188+
return trimOff.get() == 0;
189+
}
190+
191+
/**
192+
* The purpose of this method is to deal with the fact that data for the same nanosecond can be distributed in an interval
193+
* [0,256). By rounding the tick, we get the tick to which all the other ticks from the same interval belong.
194+
*
195+
* @param tick The tick
196+
* @return The rounded tick
197+
*/
198+
private long roundTick(final long tick) {
199+
// tick / COLLISION_BUFFER * COLLISION_BUFFER
200+
return tick >> COLLISION_BUFFER_POWER << COLLISION_BUFFER_POWER;
201+
}
202+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*
40+
* This file incorporates work covered by the following copyright and
41+
* permission notice:
42+
*
43+
* Copyright 2010-2013 Coda Hale and Yammer, Inc., 2014-2015 Dropwizard Team
44+
*
45+
* Licensed under the Apache License, Version 2.0 (the "License");
46+
* you may not use this file except in compliance with the License.
47+
* You may obtain a copy of the License at
48+
*
49+
* http://www.apache.org/licenses/LICENSE-2.0
50+
*
51+
* Unless required by applicable law or agreed to in writing, software
52+
* distributed under the License is distributed on an "AS IS" BASIS,
53+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
54+
* See the License for the specific language governing permissions and
55+
* limitations under the License.
56+
*/
57+
58+
package org.glassfish.jersey.server.internal.monitoring;
59+
60+
import java.util.concurrent.TimeUnit;
61+
62+
/**
63+
* A statistically representative reservoir of a data stream in time.
64+
* <p/>
65+
* Comparing to Dropwizard's Reservoir, this interface adds a possibility to work with data that is associated with a specific
66+
* time. It may not be possible; however, to obtain a snapshot or size at some moment in past due to performance optimizations.
67+
*
68+
* @author Stepan Vavra (stepan.vavra at oracle.com)
69+
* @see <a href="https://github.com/dropwizard/metrics">https://github.com/dropwizard/metrics</a>
70+
*/
71+
public interface TimeReservoir {
72+
73+
/**
74+
* Returns the number of values recorded at given time or newer. It may not be supported to return a size in past due to
75+
* performance optimizations.
76+
*
77+
* @param time The time to get the size for
78+
* @param timeUnit Time unit of the provided time
79+
* @return the number of values recorded for given time or newer
80+
*/
81+
int size(long time, TimeUnit timeUnit);
82+
83+
/**
84+
* Adds a new recorded value to the reservoir bound to a given time.
85+
*
86+
* @param value a new recorded value
87+
* @param time The time the recorded value occurred at
88+
* @param timeUnit Time unit of the provided time
89+
*/
90+
void update(long value, long time, TimeUnit timeUnit);
91+
92+
/**
93+
* Returns a snapshot of the reservoir's values at given time or newer. It may not be supported to return a snapshot in past
94+
* due to performance optimizations.
95+
*
96+
* @param time The time for which to get the snapshot
97+
* @param timeUnit Time unit of the provided time
98+
* @return a snapshot of the reservoir's values for given time or newer
99+
*/
100+
UniformTimeSnapshot getSnapshot(long time, TimeUnit timeUnit);
101+
}

0 commit comments

Comments
 (0)