Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
a41e6b3
Upgrade nebula.netflixoss to replace bintray publication and update T…
rpalcolea Mar 4, 2021
b74cdb0
Merge pull request #166 from Netflix/replace-bintray
rpalcolea Mar 9, 2021
3f2f850
Replace JCenter with Maven Central
rpalcolea Mar 18, 2021
1ff6bd7
Merge pull request #167 from Netflix/replace-jcenter
rpalcolea Mar 18, 2021
2dc4615
Rotate publishing credentials
rpalcolea Apr 21, 2021
0ab8541
Fix typo
Jun 8, 2021
1325b24
Corrected Error Message for Deprecated Gauge
Jun 24, 2021
2b382fa
rotate TravisCI secrets
rpalcolea Aug 17, 2021
e4de3eb
Remove TravisCI and use Github Actions
rpalcolea Aug 17, 2021
3e697ef
Merge pull request #172 from Netflix/use-github-actions
rpalcolea Aug 17, 2021
d4b6429
don't drop by half for periodically.
abersnaze Dec 22, 2021
2e59b07
Updaate gradle scopes
fedorka Aug 18, 2022
d76ccdb
Update gradle and Nebula
fedorka Aug 22, 2022
468f40e
fix flaky test
fedorka Aug 22, 2022
c0b6dbb
Merge pull request #177 from Netflix/feature/update-gradle
fedorka Aug 22, 2022
37c622e
update dependencies, add dependency locks, add junit 5 support
fedorka Aug 22, 2022
7da4bec
Allow custom HTTP status for throttled requests
fedorka Aug 22, 2022
ff90760
Merge pull request #178 from Netflix/feature/configurable-status
fedorka Aug 26, 2022
0017e8e
Fix race condition in LifoBlockingLimiter
coekie Aug 26, 2022
d217530
Make LifoBlockingLimiterTest.unblockWhenFullBeforeTimeout less flaky
coekie Sep 6, 2022
0e911af
Make ListenerHolder.listener non-nullable
coekie Sep 30, 2022
73d390a
Use a counting semaphore in SimpleLimiter
kilink Oct 10, 2022
3ce3644
Merge pull request #179 from coekie/lifo-limiter-fix-race
pandeyabhi1987 Oct 27, 2022
6e86611
Merge pull request #174 from abersnaze/limit-after-success
fedorka Dec 20, 2022
2e0e627
Add java 17 compatible jakarta servlet module
Feb 2, 2023
e6fe184
Rename package jakarta module
Feb 15, 2023
47169d3
Complete gradle wrapper upgrade
Feb 16, 2023
d5582b3
Merge pull request #180 from kilink/simple-limiter-semaphore
umairk79 Feb 23, 2023
a06c77a
Set source to Java 11 and test compile to Java 17
umairk79 Feb 23, 2023
e024253
Updated to spring test 6.+
umairk79 Feb 24, 2023
5522e3d
Merge pull request #183 from umairk79/java17-jakarta-servlet
umairk79 Mar 2, 2023
1c486a9
Ensure the inflight metrics are properly updated when rejecting
Jan 2, 2024
291d7bb
Merge pull request #191 from nicholashagen/update-inflight-metrics-wh…
fedorka Jan 17, 2024
e860c79
add bypass filter logic
umairk79 Feb 12, 2024
87c3f27
add bypass feature to limiters
umairk79 Feb 20, 2024
0af8186
add helper method for bypass resolvers
umairk79 Mar 18, 2024
084ce82
replace stream with chainable predicates
umairk79 Mar 21, 2024
8874fee
refactor builder helper methods
umairk79 Mar 21, 2024
8dac7db
updated tests
umairk79 Mar 21, 2024
1c68e5c
refactor default bypass predicate object
umairk79 Mar 21, 2024
628dde9
added more tests
umairk79 Mar 21, 2024
9808a48
Merge pull request #194 from Netflix/feature/bypass-filter
umairk79 Mar 21, 2024
d0cd732
upgrade nebula oss plugin and gradle to latest version
umairk79 Mar 21, 2024
7cd750a
Merge pull request #196 from Netflix/feature/update-oss-plugin
umairk79 Mar 25, 2024
5c86608
Restore bytecode compatibility for limiter builders (#197)
fedorka Mar 27, 2024
784c960
Add warning log when the initial AIMD limit is higher than the max AI…
May 8, 2024
3c050ab
Change var name to be all caps
May 8, 2024
81f9c1a
Add warning log for if the initial limit is less than minimum limit a…
May 9, 2024
59c0471
Merge pull request #199 from Netflix/aimd-limits-warn
ayangster May 15, 2024
7c587cf
Merge pull request #170 from jaketerrito/patch-1
umairk79 May 22, 2024
313f8ff
Merge pull request #169 from epot/epot-patch-1
umairk79 May 22, 2024
4946f60
Update default branch to main
umairk79 May 23, 2024
e0affd6
Merge pull request #200 from Netflix/update-default-branch
umairk79 May 23, 2024
799ae09
Prevent duplicate publishing on release
umairk79 May 23, 2024
36ec147
Remove unused imports
arouel Jun 19, 2024
fe7d9eb
switch busy flag to an AtomicInteger in AbstractPartitionedLimiter
jasonk000 Aug 26, 2024
07907b1
remove locking in AbstractPartitionedLimiter release
jasonk000 Aug 26, 2024
efbab75
explicitly unlock as early as possible
jasonk000 Aug 26, 2024
dffb413
move metric reporting outside the lock in AbstractPartitionedLimiter
jasonk000 Aug 26, 2024
7dada83
Merge pull request #203 from arouel/remove-unused-imports
umairk79 Aug 26, 2024
075bc76
Merge pull request #201 from Netflix/update-release-rules
umairk79 Aug 26, 2024
794dac3
fix: fix binary compatiblity with AbstractPartitionedLimiter metrics
jasonk000 Aug 26, 2024
de94354
make limit volatile in AbstractPartitionedLimiter
jasonk000 Aug 26, 2024
89616b3
add some concurrency tests
jasonk000 Aug 27, 2024
20aebd0
nit: whitespace fixup
jasonk000 Aug 27, 2024
729567b
move shouldBypass predicate check outside the lock state
jasonk000 Aug 27, 2024
4271a69
refactor to minimise lines of code holding lock
jasonk000 Aug 27, 2024
786f7e8
remove all locking in the not-at-limit pathway in AbstractPartitioned…
jasonk000 Aug 27, 2024
7dd2ae8
make behaviour around partition limits a little more crisp, and docum…
jasonk000 Aug 27, 2024
ef14c09
reorder resolvePartition call since we do not need it for bypass
jasonk000 Aug 27, 2024
06322db
tidy up flow in tryAcquire
jasonk000 Aug 28, 2024
09a1e6f
remove use of System.out.println in tests
jasonk000 Aug 30, 2024
58dab7f
Merge pull request #204 from jasonk000/jkoch/less-locking-partitioned…
umairk79 Sep 3, 2024
53eee34
perf: WindowedLimit allow non-updating threads to proceed when updati…
jasonk000 Sep 12, 2024
dd1ba7f
Use LongSupplier for clock in AbstractLimiter
kilink Sep 15, 2024
f2528de
Add methods to SampleListener to handle primitives
kilink Sep 15, 2024
65413bd
Return singleton no-op Listener in createBypassListener
kilink Sep 15, 2024
daebd31
Merge pull request #206 from kilink/limiter-long-supplier-clock
kilink Sep 18, 2024
26f23a0
Use IntUnaryOperator in Gradient2Limit for queueSize
kilink Sep 15, 2024
450aa7a
Merge pull request #209 from kilink/noop-listener-create-bypass-listener
kilink Sep 18, 2024
7989055
Merge pull request #208 from kilink/sample-listener-primitive
kilink Sep 18, 2024
74bb308
Merge pull request #207 from kilink/gradient2-limit-int-unary-operator
kilink Sep 19, 2024
a127eb4
Use IntUnaryOperator / DoubleUnaryOperator in VegasLimit
kilink Sep 19, 2024
414f64d
Merge pull request #211 from kilink/vegas-limit-int-unary-operator
enbnt Oct 25, 2024
1dc2b94
Use com.netflix.nebula.netflixoss 11.6.0 to move publishing to Sonaty…
OdysseusLives Jun 17, 2025
357e8ba
Github action: checkout v4
OdysseusLives Jun 17, 2025
caf92ce
Github action: cache v4
OdysseusLives Jun 17, 2025
26e9fca
Merge pull request #221 from Netflix/update-nebula.netflixoss-use-son…
OdysseusLives Jun 23, 2025
39c74f7
Expose configured backlog timeout in LifoBlockingLimiter (#223)
agshing Dec 8, 2025
e8df64d
Update Github Actions to use environments
rpalcolea Dec 16, 2025
554db58
Merge pull request #225 from Netflix/use-environments
rpalcolea Dec 17, 2025
b4e2167
fix: readme typos (#227)
ag-wnl Dec 18, 2025
78a74b9
Add time unit to Limit#onSample (#228)
Meijuh Jan 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/nebula-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: "CI"
on:
push:
branches:
- '*'
tags-ignore:
- '*'
pull_request:

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
# test against JDK 8
java: [ 8 ]
name: CI with Java ${{ matrix.java }}
steps:
- uses: actions/checkout@v6
- name: Setup git user
run: |
git config --global user.name "Netflix OSS Maintainers"
git config --global user.email "netflixoss@netflix.com"
- name: Setup jdk
uses: actions/setup-java@v5
with:
java-version: ${{ matrix.java }}
distribution: 'zulu'
- uses: actions/cache@v5
id: gradle-cache
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle/dependency-locks/*.lockfile') }}
restore-keys: |
- ${{ runner.os }}-gradle-
- uses: actions/cache@v5
id: gradle-wrapper-cache
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradlewrapper-${{ hashFiles('gradle/wrapper/*') }}
restore-keys: |
- ${{ runner.os }}-gradlewrapper-
- name: Build with Gradle
run: ./gradlew --info --stacktrace build
env:
CI_NAME: github_actions
CI_BUILD_NUMBER: ${{ github.sha }}
CI_BUILD_URL: 'https://github.com/${{ github.repository }}'
CI_BRANCH: ${{ github.ref }}
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54 changes: 54 additions & 0 deletions .github/workflows/nebula-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: "Publish candidate/release to NetflixOSS and Maven Central"
on:
push:
tags:
- v*.*.*
- v*.*.*-rc.*

jobs:
build:
runs-on: ubuntu-latest
environment: Publish
steps:
- uses: actions/checkout@v6
- name: Setup git user
run: |
git config --global user.name "Netflix OSS Maintainers"
git config --global user.email "netflixoss@netflix.com"
- name: Setup jdk 8
uses: actions/setup-java@v5
with:
java-version: 1.8
distribution: 'zulu'
- uses: actions/cache@v5
id: gradle-cache
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle/dependency-locks/*.lockfile') }}
restore-keys: |
- ${{ runner.os }}-gradle-
- uses: actions/cache@v5
id: gradle-wrapper-cache
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradlewrapper-${{ hashFiles('gradle/wrapper/*') }}
restore-keys: |
- ${{ runner.os }}-gradlewrapper-
- name: Publish candidate
if: contains(github.ref, '-rc.')
run: ./gradlew --info --stacktrace -Prelease.useLastTag=true candidate
env:
NETFLIX_OSS_SIGNING_KEY: ${{ secrets.ORG_SIGNING_KEY }}
NETFLIX_OSS_SIGNING_PASSWORD: ${{ secrets.ORG_SIGNING_PASSWORD }}
NETFLIX_OSS_REPO_USERNAME: ${{ secrets.ORG_NETFLIXOSS_USERNAME }}
NETFLIX_OSS_REPO_PASSWORD: ${{ secrets.ORG_NETFLIXOSS_PASSWORD }}
- name: Publish release
if: (!contains(github.ref, '-rc.'))
run: ./gradlew --info -Prelease.useLastTag=true final
env:
NETFLIX_OSS_SONATYPE_USERNAME: ${{ secrets.ORG_SONATYPE_USERNAME }}
NETFLIX_OSS_SONATYPE_PASSWORD: ${{ secrets.ORG_SONATYPE_PASSWORD }}
NETFLIX_OSS_SIGNING_KEY: ${{ secrets.ORG_SIGNING_KEY }}
NETFLIX_OSS_SIGNING_PASSWORD: ${{ secrets.ORG_SIGNING_PASSWORD }}
NETFLIX_OSS_REPO_USERNAME: ${{ secrets.ORG_NETFLIXOSS_USERNAME }}
NETFLIX_OSS_REPO_PASSWORD: ${{ secrets.ORG_NETFLIXOSS_PASSWORD }}
43 changes: 43 additions & 0 deletions .github/workflows/nebula-snapshot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: "Publish snapshot to NetflixOSS and Maven Central"

on:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
environment: Publish
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup git user
run: |
git config --global user.name "Netflix OSS Maintainers"
git config --global user.email "netflixoss@netflix.com"
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 8
distribution: 'zulu'
- uses: actions/cache@v5
id: gradle-cache
with:
path: |
~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
- uses: actions/cache@v5
id: gradle-wrapper-cache
with:
path: |
~/.gradle/wrapper
key: ${{ runner.os }}-gradlewrapper-${{ hashFiles('gradle/wrapper/*') }}
- name: Build
run: ./gradlew build snapshot
env:
NETFLIX_OSS_SIGNING_KEY: ${{ secrets.ORG_SIGNING_KEY }}
NETFLIX_OSS_SIGNING_PASSWORD: ${{ secrets.ORG_SIGNING_PASSWORD }}
NETFLIX_OSS_REPO_USERNAME: ${{ secrets.ORG_NETFLIXOSS_USERNAME }}
NETFLIX_OSS_REPO_PASSWORD: ${{ secrets.ORG_NETFLIXOSS_PASSWORD }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ atlassian-ide-plugin.xml
.settings
.metadata

# publishing secrets
secrets/signing-key
15 changes: 0 additions & 15 deletions .travis.yml

This file was deleted.

8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ Java Library that implements and integrates concepts from TCP congestion control

# Background

When thinking of service availability operators traditionally think in terms of RPS (requests per second). Stress tests are normally performed to determine the RPS at which point the service tips over. RPS limits are then set somewhere below this tipping point (say 75% of this value) and enforced via a token bucket. However, in large distributed systems that auto-scale this value quickly goes out of date and the service falls over by becoming non-responsive as it is unable to gracefully shed excess load. Instead of thinking in terms of RPS, we should be thinking in terms of concurrent request where we apply queuing theory to determine the number of concurrent requests a service can handle before a queue starts to build up, latencies increase and the service eventually exhausts a hard limit such as CPU, memory, disk or network. This relationship is covered very nicely with Little's Law where `Limit = Average RPS * Average Latency`.
When thinking of service availability operators traditionally think in terms of RPS (requests per second). Stress tests are normally performed to determine the RPS at which point the service tips over. RPS limits are then set somewhere below this tipping point (say 75% of this value) and enforced via a token bucket. However, in large distributed systems that auto-scale this value quickly goes out of date and the service falls over by becoming non-responsive as it is unable to gracefully shed excess load. Instead of thinking in terms of RPS, we should be thinking in terms of concurrent requests where we apply queuing theory to determine the number of concurrent requests a service can handle before a queue starts to build up, latencies increase and the service eventually exhausts a hard limit such as CPU, memory, disk or network. This relationship is covered very nicely with Little's Law where `Limit = Average RPS * Average Latency`.

Concurrency limits are very easy to enforce but difficult to determine as they would require operators to fully understand the hardware services run on and coordinate how they scale. Instead we'd prefer to measure or estimate the concurrency limits at each point in the network. As systems scale and hit limits each node will adjust and enforce its local view of the limit. To estimate the limit we borrow from common TCP congestion control algorithms by equating a system's concurrency limit to a TCP congestion window.

Before applying the algorithm we need to set some ground rules.
* We accept that every system has an inherent concurrency limit that is determined by a hard resources, such as number of CPU cores.
* We accept that every system has an inherent concurrency limit that is determined by a hard resource, such as number of CPU cores.
* We accept that this limit can change as a system auto-scales.
* For large and complex distributed systems it's impossible to know all the hard resources.
* We can use latency measurements to determine when queuing happens.
Expand All @@ -27,7 +27,7 @@ At the end of each sampling window the limit is increased by 1 if the queue is l

## Gradient2

This algorithm attempts to address bias and drift when using minimum latency measurements. To do this the algorithm tracks uses the measure of divergence between two exponential averages over a long and short time time window. Using averages the algorithm can smooth out the impact of outliers for bursty traffic. Divergence duration is used as a proxy to identify a queueing trend at which point the algorithm aggresively reduces the limit.
This algorithm attempts to address bias and drift when using minimum latency measurements. To do this the algorithm tracks the measure of divergence between two exponential averages over a long and short time window. Using averages the algorithm can smooth out the impact of outliers for bursty traffic. Divergence duration is used as a proxy to identify a queueing trend at which point the algorithm aggressively reduces the limit.

# Enforcement Strategies

Expand Down Expand Up @@ -55,7 +55,7 @@ In this example a GRPC server is configured with a single adaptive limiter that
// Create and configure a server builder
ServerBuilder builder = ...;

builder.addService(ServerInterceptor.intercept(service,
builder.addService(ServerInterceptors.intercept(service,
ConcurrencyLimitServerInterceptor.newBuilder(
new GrpcServerLimiterBuilder()
.partitionByHeader(GROUP_HEADER)
Expand Down
27 changes: 24 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
import org.gradle.api.tasks.testing.Test

buildscript {
repositories {
jcenter()
mavenCentral()
maven {
url = 'https://plugins.gradle.org/m2'
}
}
dependencies {
classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2'
}
}

plugins {
id 'nebula.netflixoss' version '6.1.0'
id 'nebula.netflixoss' version '11.6.0'
}

// Establish version and status
ext.githubProjectName = rootProject.name

subprojects {
apply plugin: 'nebula.netflixoss'
apply plugin: 'java-library'
group = "com.netflix.${githubProjectName}"

repositories {
jcenter()
mavenCentral()
maven { url 'https://jitpack.io' }
}


ext {
grpcVersion = "1.9.0"
jUnitVersion = "5.+"
jUnitLegacyVersion = "4.+"
mockitoVersion = "4.+"
slf4jVersion = "1.7.+"
spectatorVersion = "1.+"
springVersion = "5.+"
}

tasks.withType(Test) {
useJUnitPlatform()
}
}
23 changes: 0 additions & 23 deletions buildViaTravis.sh

This file was deleted.

10 changes: 5 additions & 5 deletions concurrency-limits-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ plugins {
sourceCompatibility = JavaVersion.VERSION_1_8

dependencies {
compile "org.slf4j:slf4j-api:1.7.+"

testCompile 'junit:junit-dep:4.10'
testCompile "org.slf4j:slf4j-log4j12:1.7.+"
implementation "org.slf4j:slf4j-api:${slf4jVersion}"

testCompile 'com.github.kevinmost:junit-retry-rule:cbdd972d7c'
testCompileOnly "junit:junit:${jUnitLegacyVersion}"
testImplementation "org.slf4j:slf4j-log4j12:${slf4jVersion}"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${jUnitVersion}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${jUnitVersion}"
}
37 changes: 37 additions & 0 deletions concurrency-limits-core/dependencies.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"compileClasspath": {
"org.slf4j:slf4j-api": {
"locked": "1.7.36"
}
},
"runtimeClasspath": {
"org.slf4j:slf4j-api": {
"locked": "1.7.36"
}
},
"testCompileClasspath": {
"junit:junit": {
"locked": "4.13.2"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.36"
},
"org.slf4j:slf4j-log4j12": {
"locked": "1.7.36"
}
},
"testRuntimeClasspath": {
"org.junit.jupiter:junit-jupiter-engine": {
"locked": "5.10.2"
},
"org.junit.vintage:junit-vintage-engine": {
"locked": "5.10.2"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.36"
},
"org.slf4j:slf4j-log4j12": {
"locked": "1.7.36"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public interface Limit {

/**
* Update the limiter with a sample
* @param startTime
* @param rtt
* @param startTime start time in nanoseconds
* @param rtt round trip time in nanoseconds
* @param inflight
* @param didDrop
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ public interface MetricRegistry {
*/
interface SampleListener {
void addSample(Number value);

default void addLongSample(long value) {
addSample(value);
}

default void addDoubleSample(double value) {
addSample(value);
}
}

interface Counter {
Expand Down Expand Up @@ -59,7 +67,7 @@ default SampleListener distribution(String id, String... tagNameValuePairs) {
*/
@Deprecated
default void registerGauge(String id, Supplier<Number> supplier, String... tagNameValuePairs) {
throw new UnsupportedOperationException("registerDistribution is deprecated");
throw new UnsupportedOperationException("registerGauge is deprecated");
}

/**
Expand Down
Loading