Skip to content

Commit 74e0401

Browse files
bors[bot]antifuchs
andauthored
Merge #12
12: Fix off-by-one error in check_all's capacity check r=antifuchs a=antifuchs This fixes #11: I'd missed that the "weight" variable is _additional weight_ that gets added to the first cell, so check_all (when given one more element than fit in the burst capacity) would return a "denied" result instead of an "insufficient capacity" result - effectively preventing futures from ever completing. Now, we treat additional weight as what it is: * rename "weight" to "additional_weight" to make it clear what is going on * Add a comment over the capacity check, clarifying the off-by-one error potential * Add a test about for more capacity-excession scenarios, including the off-by-one, to prevent regressions. Thanks to @jean-airoldie for discovering the bug & reporting it! Co-authored-by: Andreas Fuchs <[email protected]>
2 parents 3d81da8 + 3bf4b4f commit 74e0401

File tree

4 files changed

+39
-24
lines changed

4 files changed

+39
-24
lines changed

.circleci/config.yml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# TemplateCIConfig { bench: BenchEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: None, commandline: "cargo bench" }), clippy: ClippyEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: Some("rustup component add clippy"), commandline: "cargo clippy -- -D warnings" }), rustfmt: RustfmtEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: Some("rustup component add rustfmt"), commandline: "cargo fmt -v -- --check" }), additional_matrix_entries: {"cargo_deny": CustomEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: Some("cargo install cargo-deny"), commandline: "cargo deny check all" }), "no_std_stable": CustomEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: None, commandline: "cargo test --no-default-features --features no_std" }), "no_std_nightly": CustomEntry(MatrixEntry { run: true, run_cron: false, version: "nightly", install_commandline: None, commandline: "cargo +nightly test --no-default-features --features no_std" })}, cache: "cargo", os: "linux", dist: "xenial", versions: ["stable", "nightly"], test_commandline: "cargo test --verbose --all", scheduled_test_branches: ["master"], test_schedule: "0 0 * * 0" }
1+
# TemplateCIConfig { bench: BenchEntry(MatrixEntry { run: true, run_cron: false, version: "nightly", install_commandline: None, commandline: "cargo bench", timeout: None }), clippy: ClippyEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: Some("rustup component add clippy"), commandline: "cargo clippy -- -D warnings", timeout: None }), rustfmt: RustfmtEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: Some("rustup component add rustfmt"), commandline: "cargo fmt -v -- --check", timeout: None }), additional_matrix_entries: {"cargo_deny": CustomEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: Some("cargo install cargo-deny"), commandline: "cargo deny check all", timeout: None }), "no_std_nightly": CustomEntry(MatrixEntry { run: true, run_cron: false, version: "nightly", install_commandline: None, commandline: "cargo +nightly test --no-default-features --features no_std", timeout: None }), "no_std_stable": CustomEntry(MatrixEntry { run: true, run_cron: false, version: "stable", install_commandline: None, commandline: "cargo test --no-default-features --features no_std", timeout: None })}, cache: "cargo", os: "linux", dist: "xenial", versions: ["stable", "nightly"], test_commandline: "cargo test --verbose --all", scheduled_test_branches: ["master"], test_schedule: "0 0 * * 0" }
22
version: "2.1"
33

44
executors:
@@ -95,7 +95,7 @@ jobs:
9595
- run:
9696
name: cargo deny check all
9797
command: cargo deny check all
98-
no_std_stable:
98+
no_std_nightly:
9999
parameters:
100100
version:
101101
type: executor
@@ -107,9 +107,9 @@ jobs:
107107
steps:
108108
- checkout
109109
- run:
110-
name: cargo test --no-default-features --features no_std
111-
command: cargo test --no-default-features --features no_std
112-
no_std_nightly:
110+
name: cargo +nightly test --no-default-features --features no_std
111+
command: cargo +nightly test --no-default-features --features no_std
112+
no_std_stable:
113113
parameters:
114114
version:
115115
type: executor
@@ -121,8 +121,8 @@ jobs:
121121
steps:
122122
- checkout
123123
- run:
124-
name: cargo +nightly test --no-default-features --features no_std
125-
command: cargo +nightly test --no-default-features --features no_std
124+
name: cargo test --no-default-features --features no_std
125+
command: cargo test --no-default-features --features no_std
126126

127127
ci_success:
128128
docker:
@@ -196,7 +196,7 @@ workflows:
196196
}
197197
}
198198
- bench:
199-
version: stable
199+
version: nightly
200200
filters: {
201201
"branches": {
202202
"ignore": [
@@ -213,14 +213,14 @@ workflows:
213213
name: "cargo_deny"
214214
version: stable
215215
version_name: stable
216-
- no_std_stable:
217-
name: "no_std_stable"
218-
version: stable
219-
version_name: stable
220216
- no_std_nightly:
221217
name: "no_std_nightly"
222218
version: nightly
223219
version_name: nightly
220+
- no_std_stable:
221+
name: "no_std_stable"
222+
version: stable
223+
version_name: stable
224224
- ci_success:
225225
requires:
226226
- test-stable
@@ -229,8 +229,8 @@ workflows:
229229
- clippy
230230
- bench
231231
- cargo_deny
232-
- no_std_stable
233232
- no_std_nightly
233+
- no_std_stable
234234
scheduled_tests:
235235
jobs:
236236
- test:

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ dev-version-ext = "dev"
2626

2727
[package.metadata.template_ci.bench]
2828
run = true
29-
version = "stable"
29+
version = "nightly"
3030

3131
[package.metadata.template_ci.additional_matrix_entries]
3232

src/gcra.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,18 @@ impl GCRA {
113113
let t0 = t0.duration_since(start);
114114
let tau = self.tau;
115115
let t = self.t;
116-
let weight = t * (n.get() - 1) as u64;
117-
if weight > tau {
116+
let additional_weight = t * (n.get() - 1) as u64;
117+
118+
// check that we can allow enough cells through. Note that `additional_weight` is the
119+
// value of the cells *in addition* to the first cell - so add that first cell back.
120+
if additional_weight + t > tau {
118121
return Err(NegativeMultiDecision::InsufficientCapacity(
119122
(tau.as_u64() / t.as_u64()) as u32,
120123
));
121124
}
122125
state.measure_and_replace(key, |tat| {
123126
let tat = tat.unwrap_or_else(|| self.starting_state(t0));
124-
let earliest_time = (tat + weight).saturating_sub(tau);
127+
let earliest_time = (tat + additional_weight).saturating_sub(tau);
125128
if t0 < earliest_time {
126129
Err(NegativeMultiDecision::BatchNonConforming(
127130
n.get(),
@@ -132,7 +135,7 @@ impl GCRA {
132135
},
133136
))
134137
} else {
135-
Ok(((), cmp::max(tat, t0) + t + weight))
138+
Ok(((), cmp::max(tat, t0) + t + additional_weight))
136139
}
137140
})
138141
}

tests/direct.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,25 @@ fn rejects_too_many_all() {
116116
// After 3 and 20 seconds, it should not allow 15 on that bucket either:
117117
clock.advance(ms * 3 * 1000);
118118
assert_ne!(Ok(()), lb.check_all(nonzero!(15u32)));
119+
}
119120

120-
clock.advance(ms * 17 * 1000);
121-
let result = lb.check_all(nonzero!(15u32));
122-
match result {
123-
Err(NegativeMultiDecision::InsufficientCapacity(n)) => assert_eq!(n, 5),
124-
_ => panic!("Did not expect {:?}", result),
125-
}
121+
#[test]
122+
fn all_capacity_check_rejects_excess() {
123+
let clock = FakeRelativeClock::default();
124+
let lb = RateLimiter::direct_with_clock(Quota::per_second(nonzero!(5u32)), &clock);
125+
126+
assert_eq!(
127+
Err(NegativeMultiDecision::InsufficientCapacity(5)),
128+
lb.check_all(nonzero!(15u32))
129+
);
130+
assert_eq!(
131+
Err(NegativeMultiDecision::InsufficientCapacity(5)),
132+
lb.check_all(nonzero!(6u32))
133+
);
134+
assert_eq!(
135+
Err(NegativeMultiDecision::InsufficientCapacity(5)),
136+
lb.check_all(nonzero!(7u32))
137+
);
126138
}
127139

128140
#[test]

0 commit comments

Comments
 (0)