-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Josh Gordineer
committed
Nov 24, 2023
1 parent
a634f1d
commit 4048d8e
Showing
13 changed files
with
1,269 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
ribbon-loadbalancer/src/main/java/com/netflix/stats/distribution/DataAccumulator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
* | ||
* Copyright 2013 Netflix, 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 com.netflix.stats.distribution; | ||
|
||
import java.util.concurrent.locks.Lock; | ||
|
||
|
||
/** | ||
* A double-buffer of {@link DataBuffer} objects. | ||
* One is the "current" buffer, and new data is added to it. | ||
* The other is the "previous" buffer, and is used as a sorce | ||
* of computed statistics. | ||
* | ||
* @see DataPublisher | ||
* | ||
* @author $Author: netflixoss $ | ||
*/ | ||
public abstract class DataAccumulator implements DataCollector { | ||
|
||
private DataBuffer current; | ||
private DataBuffer previous; | ||
private final Object swapLock = new Object(); | ||
|
||
/* | ||
* Constructor(s) | ||
*/ | ||
|
||
/** | ||
* Creates a new initially empty DataAccumulator. | ||
* | ||
* @param bufferSize the size of the buffers to use | ||
*/ | ||
public DataAccumulator(int bufferSize) { | ||
this.current = new DataBuffer(bufferSize); | ||
this.previous = new DataBuffer(bufferSize); | ||
} | ||
|
||
/* | ||
* Accumulating new values | ||
*/ | ||
|
||
/** {@inheritDoc} */ | ||
public void noteValue(double val) { | ||
synchronized (swapLock) { | ||
Lock l = current.getLock(); | ||
l.lock(); | ||
try { | ||
current.noteValue(val); | ||
} finally { | ||
l.unlock(); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Swaps the data collection buffers, and computes statistics | ||
* about the data collected up til now. | ||
*/ | ||
public void publish() { | ||
/* | ||
* Some care is required here to correctly swap the DataBuffers, | ||
* but not hold the synchronization object while compiling stats | ||
* (a potentially long computation). This ensures that continued | ||
* data collection (calls to noteValue()) will not be blocked for any | ||
* significant period. | ||
*/ | ||
DataBuffer tmp = null; | ||
Lock l = null; | ||
synchronized (swapLock) { | ||
// Swap buffers | ||
tmp = current; | ||
current = previous; | ||
previous = tmp; | ||
// Start collection in the new "current" buffer | ||
l = current.getLock(); | ||
l.lock(); | ||
try { | ||
current.startCollection(); | ||
} finally { | ||
l.unlock(); | ||
} | ||
// Grab lock on new "previous" buffer | ||
l = tmp.getLock(); | ||
l.lock(); | ||
} | ||
// Release synchronizaton *before* publishing data | ||
try { | ||
tmp.endCollection(); | ||
publish(tmp); | ||
} finally { | ||
l.unlock(); | ||
} | ||
} | ||
|
||
/** | ||
* Called to publish recently collected data. | ||
* When called, the {@link Lock} associated with the "previous" | ||
* buffer is held, so the data will not be changed. | ||
* Other locks have been released, and so new data can be | ||
* collected in the "current" buffer. | ||
* The data in the buffer has also been sorted in increasing order. | ||
* | ||
* @param buf the {@code DataBuffer} that is now "previous". | ||
*/ | ||
protected abstract void publish(DataBuffer buf); | ||
|
||
} // DataAccumulator |
202 changes: 202 additions & 0 deletions
202
ribbon-loadbalancer/src/main/java/com/netflix/stats/distribution/DataBuffer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
/* | ||
* | ||
* Copyright 2013 Netflix, 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 com.netflix.stats.distribution; | ||
|
||
import java.util.Arrays; | ||
import java.util.concurrent.locks.Lock; | ||
import java.util.concurrent.locks.ReentrantLock; | ||
|
||
|
||
/** | ||
* A fixed-size data collection buffer that holds a sliding window | ||
* of the most recent values added. | ||
* The {@code DataBuffer} is also a {@link Distribution} and so collects | ||
* basic statistics about the data added to the buffer. | ||
* This statistical data is managed on-the-fly, and reflects all the data | ||
* added, if those values that may have been dropped due to buffer overflow. | ||
* <p> | ||
* This class is <em>not</em> synchronized, but can instead managed by a | ||
* {@link Lock} attached to the {@code DataBuffer} (see {@link #getLock}). | ||
* @author netflixoss | ||
*/ | ||
public class DataBuffer extends Distribution { | ||
|
||
private final Lock lock; | ||
private final double[] buf; | ||
private long startMillis; | ||
private long endMillis; | ||
private int size; | ||
private int insertPos; | ||
|
||
/* | ||
* Constructors | ||
*/ | ||
|
||
/** | ||
* Creates a new {@code DataBuffer} with a given capacity. | ||
*/ | ||
public DataBuffer(int capacity) { | ||
lock = new ReentrantLock(); | ||
buf = new double[capacity]; | ||
startMillis = 0; | ||
size = 0; | ||
insertPos = 0; | ||
} | ||
|
||
/* | ||
* Accessors | ||
*/ | ||
|
||
/** | ||
* Gets the {@link Lock} to use to manage access to the | ||
* contents of the {@code DataBuffer}. | ||
*/ | ||
public Lock getLock() { | ||
return lock; | ||
} | ||
|
||
/** | ||
* Gets the capacity of the {@code DataBuffer}; that is, | ||
* the maximum number of values that the {@code DataBuffer} can hold. | ||
*/ | ||
public int getCapacity() { | ||
return buf.length; | ||
} | ||
|
||
/** | ||
* Gets the length of time over which the data was collected, | ||
* in milliseconds. | ||
* The value is only valid after {@link #endCollection} | ||
* has been called (and before a subsequent call to {@link #startCollection}). | ||
*/ | ||
public long getSampleIntervalMillis() { | ||
return (endMillis - startMillis); | ||
} | ||
|
||
/** | ||
* Gets the number of values currently held in the buffer. | ||
* This value may be smaller than the value of {@link #getNumValues} | ||
* depending on how the percentile values were computed. | ||
*/ | ||
public int getSampleSize() { | ||
return size; | ||
} | ||
|
||
/* | ||
* Managing the data | ||
*/ | ||
|
||
/** {@inheritDoc} */ | ||
@Override | ||
public void clear() { | ||
super.clear(); | ||
startMillis = 0; | ||
size = 0; | ||
insertPos = 0; | ||
} | ||
|
||
/** | ||
* Notifies the buffer that data is collection is now enabled. | ||
*/ | ||
public void startCollection() { | ||
clear(); | ||
startMillis = System.currentTimeMillis(); | ||
} | ||
|
||
/** | ||
* Notifies the buffer that data has just ended. | ||
* <p> | ||
* <b>Performance Note:</b> | ||
* <br>This method sorts the underlying data buffer, | ||
* and so may be slow. It is best to call this at most once | ||
* and fetch all percentile values desired, instead of making | ||
* a number of repeated calls. | ||
*/ | ||
public void endCollection() { | ||
endMillis = System.currentTimeMillis(); | ||
Arrays.sort(buf, 0, size); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* <p> | ||
* The buffer wraps-around if it is full, overwriting the oldest | ||
* entry with the new value. | ||
*/ | ||
@Override | ||
public void noteValue(double val) { | ||
super.noteValue(val); | ||
buf[insertPos++] = val; | ||
if (insertPos >= buf.length) { | ||
insertPos = 0; | ||
size = buf.length; | ||
} else if (insertPos > size) { | ||
size = insertPos; | ||
} | ||
} | ||
|
||
/** | ||
* Gets the requested percentile statistics. | ||
* | ||
* @param percents array of percentile values to compute, | ||
* which must be in the range {@code [0 .. 100]} | ||
* @param percentiles array to fill in with the percentile values; | ||
* must be the same length as {@code percents} | ||
* @return the {@code percentiles} array | ||
* @see <a href="http://en.wikipedia.org/wiki/Percentile">Percentile (Wikipedia)</a> | ||
* @see <a href="http://cnx.org/content/m10805/latest/">Percentile</a> | ||
*/ | ||
public double[] getPercentiles(double[] percents, double[] percentiles) { | ||
for (int i = 0; i < percents.length; i++) { | ||
percentiles[i] = computePercentile(percents[i]); | ||
} | ||
return percentiles; | ||
} | ||
|
||
private double computePercentile(double percent) { | ||
// Some just-in-case edge cases | ||
if (size <= 0) { | ||
return 0.0; | ||
} else if (percent <= 0.0) { | ||
return buf[0]; | ||
} else if (percent >= 100.0) { // SUPPRESS CHECKSTYLE MagicNumber | ||
return buf[size - 1]; | ||
} | ||
/* | ||
* Note: | ||
* Documents like http://cnx.org/content/m10805/latest | ||
* use a one-based ranking, while this code uses a zero-based index, | ||
* so the code may not look exactly like the formulas. | ||
*/ | ||
double index = (percent / 100.0) * size; // SUPPRESS CHECKSTYLE MagicNumber | ||
int iLow = (int) Math.floor(index); | ||
int iHigh = (int) Math.ceil(index); | ||
assert 0 <= iLow && iLow <= index && index <= iHigh && iHigh <= size; | ||
assert (iHigh - iLow) <= 1; | ||
if (iHigh >= size) { | ||
// Another edge case | ||
return buf[size - 1]; | ||
} else if (iLow == iHigh) { | ||
return buf[iLow]; | ||
} else { | ||
// Interpolate between the two bounding values | ||
return buf[iLow] + (index - iLow) * (buf[iHigh] - buf[iLow]); | ||
} | ||
} | ||
|
||
} // DataBuffer |
36 changes: 36 additions & 0 deletions
36
ribbon-loadbalancer/src/main/java/com/netflix/stats/distribution/DataCollector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* | ||
* Copyright 2013 Netflix, 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 com.netflix.stats.distribution; | ||
|
||
|
||
/** | ||
* An object that collects new values incrementally. | ||
* | ||
* @author netflixoss | ||
* @version $Revision: $ | ||
*/ | ||
public interface DataCollector { | ||
|
||
/** | ||
* Adds a value to the collected data. | ||
* This must run very quickly, and so can safely | ||
* be called in time-critical code. | ||
*/ | ||
void noteValue(double val); | ||
|
||
} // DataCollector |
Oops, something went wrong.