Replace leaky_bucket with governor crate

This commit is contained in:
Igor Katson 2024-11-16 10:55:33 +00:00
parent 3924197461
commit 1dbdeb5bbe
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
4 changed files with 117 additions and 49 deletions

102
Cargo.lock generated
View file

@ -1497,9 +1497,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
@ -1507,9 +1507,9 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
@ -1535,15 +1535,15 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
@ -1552,21 +1552,27 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-channel",
"futures-core",
@ -1854,6 +1860,27 @@ dependencies = [
"system-deps",
]
[[package]]
name = "governor"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0746aa765db78b521451ef74221663b57ba595bf83f75d0ce23cc09447c8139f"
dependencies = [
"cfg-if",
"dashmap 6.1.0",
"futures-sink",
"futures-timer",
"futures-util",
"no-std-compat",
"nonzero_ext",
"parking_lot",
"portable-atomic",
"quanta",
"rand 0.8.5",
"smallvec",
"spinning_top",
]
[[package]]
name = "gtk"
version = "0.18.1"
@ -2590,10 +2617,10 @@ dependencies = [
"console-subscriber",
"dashmap 6.1.0",
"futures",
"governor",
"hex 0.4.3",
"http",
"itertools 0.13.0",
"leaky-bucket",
"librqbit-bencode",
"librqbit-buffers",
"librqbit-clone-to-owned",
@ -3094,6 +3121,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "no-std-compat"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "nodrop"
version = "0.1.14"
@ -3110,6 +3143,12 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nonzero_ext"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
[[package]]
name = "notify"
version = "6.1.1"
@ -3944,6 +3983,21 @@ dependencies = [
"prost",
]
[[package]]
name = "quanta"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5"
dependencies = [
"crossbeam-utils",
"libc",
"once_cell",
"raw-cpuid",
"wasi 0.11.0+wasi-snapshot-preview1",
"web-sys",
"winapi",
]
[[package]]
name = "quick-xml"
version = "0.32.0"
@ -4107,6 +4161,15 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "raw-cpuid"
version = "11.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0"
dependencies = [
"bitflags 2.6.0",
]
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@ -4861,6 +4924,15 @@ dependencies = [
"lock_api",
]
[[package]]
name = "spinning_top"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
dependencies = [
"lock_api",
]
[[package]]
name = "spki"
version = "0.7.3"

View file

@ -61,7 +61,7 @@ tokio = { version = "1", features = [
"fs",
"io-util",
] }
leaky-bucket = "1.1"
governor = "0.7"
console-subscriber = { version = "0.4", optional = true }
axum = { version = "0.7", optional = true }
tower-http = { version = "0.5", features = ["cors", "trace"], optional = true }

View file

@ -1,49 +1,42 @@
use std::sync::Arc;
use std::time::Duration;
use leaky_bucket::RateLimiter;
use governor::DefaultDirectRateLimiter as RateLimiter;
use governor::Quota;
use parking_lot::RwLock;
use peer_binary_protocol::PIECE_MESSAGE_DEFAULT_LEN;
use serde::Deserialize;
use serde::Serialize;
use std::num::NonZero;
use std::num::NonZeroU32;
use std::sync::Arc;
#[derive(Default, Serialize, Deserialize, Clone, Copy)]
pub struct LimitsConfig {
pub upload_bps: Option<usize>,
pub download_bps: Option<usize>,
pub upload_bps: Option<NonZero<u32>>,
pub download_bps: Option<NonZero<u32>>,
}
struct Limit(RwLock<Arc<Option<leaky_bucket::RateLimiter>>>);
struct Limit(RwLock<Arc<Option<RateLimiter>>>);
impl Limit {
fn new_inner(bps: Option<usize>) -> Arc<Option<leaky_bucket::RateLimiter>> {
fn new_inner(bps: Option<NonZero<u32>>) -> Arc<Option<RateLimiter>> {
let bps = match bps {
Some(bps) => bps,
None => return Arc::new(None),
};
let b_per_100_ms = bps.div_ceil(10);
Arc::new(Some(
RateLimiter::builder()
.interval(Duration::from_millis(100))
.refill(b_per_100_ms)
// whatever the limit is, we need to be able to download / upload a chunk
.max(PIECE_MESSAGE_DEFAULT_LEN.max(bps))
.build(),
))
Arc::new(Some(RateLimiter::direct(Quota::per_second(bps))))
}
fn new(bps: Option<usize>) -> Self {
fn new(bps: Option<NonZero<u32>>) -> Self {
Self(RwLock::new(Self::new_inner(bps)))
}
async fn acquire(&self, size: usize) {
async fn acquire(&self, size: NonZero<u32>) -> anyhow::Result<()> {
let lim = self.0.read().clone();
if let Some(rl) = lim.as_ref() {
rl.acquire(size).await
rl.until_n_ready(size).await?;
}
Ok(())
}
fn set(&self, limit: Option<usize>) {
fn set(&self, limit: Option<NonZero<u32>>) {
let new = Self::new_inner(limit);
*self.0.write() = new;
}
@ -62,19 +55,19 @@ impl Limits {
}
}
pub async fn prepare_for_upload(&self, len: usize) {
pub async fn prepare_for_upload(&self, len: NonZero<u32>) -> anyhow::Result<()> {
self.up.acquire(len).await
}
pub async fn prepare_for_download(&self, len: usize) {
pub async fn prepare_for_download(&self, len: NonZero<u32>) -> anyhow::Result<()> {
self.down.acquire(len).await
}
pub fn set_upload_bps(&self, bps: Option<usize>) {
pub fn set_upload_bps(&self, bps: Option<NonZero<u32>>) {
self.up.set(bps);
}
pub fn set_download_bps(&self, bps: Option<usize>) {
pub fn set_download_bps(&self, bps: Option<NonZeroU32>) {
self.down.set(bps);
}
}

View file

@ -46,6 +46,7 @@ pub mod stats;
use std::{
collections::{HashMap, HashSet},
net::SocketAddr,
num::NonZero,
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc,
@ -416,12 +417,14 @@ impl TorrentStateLive {
)>,
) -> anyhow::Result<()> {
while let Some((tx, ci)) = rx.recv().await {
self.ratelimits.prepare_for_upload(ci.size as usize).await;
self.ratelimits
.prepare_for_upload(NonZero::new(ci.size).unwrap())
.await?;
if let Some(session) = self.torrent.session.upgrade() {
session
.ratelimits
.prepare_for_upload(ci.size as usize)
.await;
.prepare_for_upload(NonZero::new(ci.size).unwrap())
.await?;
}
let _ = tx.send(WriterRequest::ReadChunkRequest(ci));
}
@ -1446,14 +1449,14 @@ impl PeerHandler {
self.state
.ratelimits
.prepare_for_download(request.length as usize)
.await;
.prepare_for_download(NonZero::new(request.length).unwrap())
.await?;
if let Some(session) = self.state.torrent().session.upgrade() {
session
.ratelimits
.prepare_for_download(request.length as usize)
.await;
.prepare_for_download(NonZero::new(request.length).unwrap())
.await?;
}
loop {