From: Hannes Laimer <h.laimer@proxmox.com>
To: Proxmox Backup Server development discussion
<pbs-devel@lists.proxmox.com>,
Christian Ebner <c.ebner@proxmox.com>
Subject: Re: [pbs-devel] [PATCH proxmox v2 2/4] http: drop factored out rate limiter implementation
Date: Tue, 11 Nov 2025 11:36:48 +0100 [thread overview]
Message-ID: <ab94fb25-1b2c-4482-b709-b5bb32f60228@proxmox.com> (raw)
In-Reply-To: <20250916124147.513342-3-c.ebner@proxmox.com>
Reviewed-by: Hannes Laimer <h.laimer@proxmox.com>
Tested-by: Hannes Laimer <h.laimer@proxmox.com>
On 9/16/25 14:42, Christian Ebner wrote:
> The rate limiter implementation has been moved together with the shared
> rate limiter into a dedicated crate. Depend on that and drop the now
> dead code from the proxmox-http crate.
>
> Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
> ---
> Changes since version 1:
> - not present in previous version
>
> Cargo.toml | 1 +
> proxmox-http/Cargo.toml | 4 +-
> proxmox-http/debian/control | 19 +--
> proxmox-http/src/client/connector.rs | 3 +-
> proxmox-http/src/lib.rs | 5 -
> proxmox-http/src/rate_limited_stream.rs | 2 +-
> proxmox-http/src/rate_limiter.rs | 214 ------------------------
> 7 files changed, 8 insertions(+), 240 deletions(-)
> delete mode 100644 proxmox-http/src/rate_limiter.rs
>
> diff --git a/Cargo.toml b/Cargo.toml
> index bde32b17..f62b9882 100644
> --- a/Cargo.toml
> +++ b/Cargo.toml
> @@ -153,6 +153,7 @@ proxmox-login = { version = "1.0.0", path = "proxmox-login" }
> proxmox-network-types = { version = "0.1.0", path = "proxmox-network-types" }
> proxmox-product-config = { version = "1.0.0", path = "proxmox-product-config" }
> proxmox-config-digest = { version = "1.0.0", path = "proxmox-config-digest" }
> +proxmox-rate-limiter = { version = "1.0.0", path = "proxmox-rate-limiter" }
> proxmox-rest-server = { version = "1.0.0", path = "proxmox-rest-server" }
> proxmox-router = { version = "3.2.2", path = "proxmox-router" }
> proxmox-s3-client = { version = "1.2", path = "proxmox-s3-client" }
> diff --git a/proxmox-http/Cargo.toml b/proxmox-http/Cargo.toml
> index 2f83cf5f..0b362bef 100644
> --- a/proxmox-http/Cargo.toml
> +++ b/proxmox-http/Cargo.toml
> @@ -32,6 +32,7 @@ url = { workspace = true, optional = true }
>
> proxmox-async = { workspace = true, optional = true }
> proxmox-base64 = { workspace = true, optional = true }
> +proxmox-rate-limiter = { workspace = true, optional = true, features = [ "rate-limiter" ] }
> proxmox-sys = { workspace = true, optional = true }
> proxmox-io = { workspace = true, optional = true }
> proxmox-lang = { workspace = true, optional = true }
> @@ -53,7 +54,6 @@ body = [
> "dep:sync_wrapper",
> "sync_wrapper?/futures",
> ]
> -rate-limiter = ["dep:hyper"]
> rate-limited-stream = [
> "dep:tokio",
> "dep:hyper-util",
> @@ -61,7 +61,7 @@ rate-limited-stream = [
> "hyper-util?/client-legacy",
> "hyper-util?/http1",
> "tokio?/time",
> - "rate-limiter",
> + "dep:proxmox-rate-limiter",
> ]
> client = [
> "dep:bytes",
> diff --git a/proxmox-http/debian/control b/proxmox-http/debian/control
> index f86e58d1..ae3af111 100644
> --- a/proxmox-http/debian/control
> +++ b/proxmox-http/debian/control
> @@ -29,7 +29,6 @@ Suggests:
> librust-proxmox-http+http-helpers-dev (= ${binary:Version}),
> librust-proxmox-http+proxmox-async-dev (= ${binary:Version}),
> librust-proxmox-http+rate-limited-stream-dev (= ${binary:Version}),
> - librust-proxmox-http+rate-limiter-dev (= ${binary:Version}),
> librust-proxmox-http+websocket-dev (= ${binary:Version})
> Provides:
> librust-proxmox-http+default-dev (= ${binary:Version}),
> @@ -172,12 +171,13 @@ Multi-Arch: same
> Depends:
> ${misc:Depends},
> librust-proxmox-http-dev (= ${binary:Version}),
> - librust-proxmox-http+rate-limiter-dev (= ${binary:Version}),
> librust-hyper-util-0.1+client-dev (>= 0.1.12-~~),
> librust-hyper-util-0.1+client-legacy-dev (>= 0.1.12-~~),
> librust-hyper-util-0.1+default-dev (>= 0.1.12-~~),
> librust-hyper-util-0.1+http1-dev (>= 0.1.12-~~),
> librust-hyper-util-0.1+http2-dev (>= 0.1.12-~~),
> + librust-proxmox-rate-limiter-1+default-dev,
> + librust-proxmox-rate-limiter-1+rate-limiter-dev,
> librust-tokio-1+default-dev (>= 1.6-~~),
> librust-tokio-1+time-dev (>= 1.6-~~)
> Provides:
> @@ -188,21 +188,6 @@ Description: Proxmox HTTP library - feature "rate-limited-stream"
> This metapackage enables feature "rate-limited-stream" for the Rust proxmox-
> http crate, by pulling in any additional dependencies needed by that feature.
>
> -Package: librust-proxmox-http+rate-limiter-dev
> -Architecture: any
> -Multi-Arch: same
> -Depends:
> - ${misc:Depends},
> - librust-proxmox-http-dev (= ${binary:Version}),
> - librust-hyper-1+default-dev
> -Provides:
> - librust-proxmox-http-1+rate-limiter-dev (= ${binary:Version}),
> - librust-proxmox-http-1.0+rate-limiter-dev (= ${binary:Version}),
> - librust-proxmox-http-1.0.3+rate-limiter-dev (= ${binary:Version})
> -Description: Proxmox HTTP library - feature "rate-limiter"
> - This metapackage enables feature "rate-limiter" for the Rust proxmox-http
> - crate, by pulling in any additional dependencies needed by that feature.
> -
> Package: librust-proxmox-http+websocket-dev
> Architecture: any
> Multi-Arch: same
> diff --git a/proxmox-http/src/client/connector.rs b/proxmox-http/src/client/connector.rs
> index 1600d47c..d5d85cb9 100644
> --- a/proxmox-http/src/client/connector.rs
> +++ b/proxmox-http/src/client/connector.rs
> @@ -13,13 +13,14 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
> use tokio::net::TcpStream;
> use tokio_openssl::SslStream;
>
> +use proxmox_rate_limiter::ShareableRateLimit;
> use proxmox_sys::linux::socket::set_tcp_keepalive;
>
> use crate::proxy_config::ProxyConfig;
> use crate::uri::build_authority;
>
> use super::tls::MaybeTlsStream;
> -use crate::{RateLimitedStream, ShareableRateLimit};
> +use crate::RateLimitedStream;
>
> type SharedRateLimit = Arc<dyn ShareableRateLimit>;
>
> diff --git a/proxmox-http/src/lib.rs b/proxmox-http/src/lib.rs
> index 8b6953b0..7f6f67f8 100644
> --- a/proxmox-http/src/lib.rs
> +++ b/proxmox-http/src/lib.rs
> @@ -26,11 +26,6 @@ mod client_trait;
> #[cfg(feature = "client-trait")]
> pub use client_trait::HttpClient;
>
> -#[cfg(feature = "rate-limiter")]
> -mod rate_limiter;
> -#[cfg(feature = "rate-limiter")]
> -pub use rate_limiter::{RateLimit, RateLimiter, RateLimiterVec, ShareableRateLimit};
> -
> #[cfg(feature = "rate-limited-stream")]
> mod rate_limited_stream;
> #[cfg(feature = "rate-limited-stream")]
> diff --git a/proxmox-http/src/rate_limited_stream.rs b/proxmox-http/src/rate_limited_stream.rs
> index e9308a47..2253bef8 100644
> --- a/proxmox-http/src/rate_limited_stream.rs
> +++ b/proxmox-http/src/rate_limited_stream.rs
> @@ -11,7 +11,7 @@ use tokio::time::Sleep;
>
> use std::task::{Context, Poll};
>
> -use super::{RateLimiter, ShareableRateLimit};
> +use proxmox_rate_limiter::{RateLimiter, ShareableRateLimit};
>
> type SharedRateLimit = Arc<dyn ShareableRateLimit>;
>
> diff --git a/proxmox-http/src/rate_limiter.rs b/proxmox-http/src/rate_limiter.rs
> deleted file mode 100644
> index 945c77a6..00000000
> --- a/proxmox-http/src/rate_limiter.rs
> +++ /dev/null
> @@ -1,214 +0,0 @@
> -use std::convert::TryInto;
> -use std::time::{Duration, Instant};
> -
> -use anyhow::{bail, Error};
> -
> -/// Rate limiter interface.
> -pub trait RateLimit {
> - /// Update rate and bucket size
> - fn update_rate(&mut self, rate: u64, bucket_size: u64);
> -
> - /// Returns the overall traffic (since started)
> - fn traffic(&self) -> u64;
> -
> - /// Register traffic, returning a proposed delay to reach the
> - /// expected rate.
> - fn register_traffic(&mut self, current_time: Instant, data_len: u64) -> Duration;
> -}
> -
> -/// Like [`RateLimit`], but does not require self to be mutable.
> -///
> -/// This is useful for types providing internal mutability (Mutex).
> -pub trait ShareableRateLimit: Send + Sync {
> - fn update_rate(&self, rate: u64, bucket_size: u64);
> - fn traffic(&self) -> u64;
> - fn register_traffic(&self, current_time: Instant, data_len: u64) -> Duration;
> -}
> -
> -/// IMPORTANT: We use this struct in shared memory, so please do not
> -/// change/modify the layout (do not add fields)
> -#[derive(Clone)]
> -#[repr(C)]
> -struct TbfState {
> - traffic: u64, // overall traffic
> - last_update: Instant,
> - consumed_tokens: u64,
> -}
> -
> -impl TbfState {
> - const NO_DELAY: Duration = Duration::from_millis(0);
> -
> - fn refill_bucket(&mut self, rate: u64, current_time: Instant) {
> - let time_diff = match current_time.checked_duration_since(self.last_update) {
> - Some(duration) => duration.as_nanos(),
> - None => return,
> - };
> -
> - if time_diff == 0 {
> - return;
> - }
> -
> - self.last_update = current_time;
> -
> - let allowed_traffic = ((time_diff.saturating_mul(rate as u128)) / 1_000_000_000)
> - .try_into()
> - .unwrap_or(u64::MAX);
> -
> - self.consumed_tokens = self.consumed_tokens.saturating_sub(allowed_traffic);
> - }
> -
> - fn register_traffic(
> - &mut self,
> - rate: u64,
> - bucket_size: u64,
> - current_time: Instant,
> - data_len: u64,
> - ) -> Duration {
> - self.refill_bucket(rate, current_time);
> -
> - self.traffic += data_len;
> - self.consumed_tokens += data_len;
> -
> - if self.consumed_tokens <= bucket_size {
> - return Self::NO_DELAY;
> - }
> - Duration::from_nanos(
> - (self.consumed_tokens - bucket_size).saturating_mul(1_000_000_000) / rate,
> - )
> - }
> -}
> -
> -/// Token bucket based rate limiter
> -///
> -/// IMPORTANT: We use this struct in shared memory, so please do not
> -/// change/modify the layout (do not add fields)
> -#[repr(C)]
> -pub struct RateLimiter {
> - rate: u64, // tokens/second
> - bucket_size: u64, // TBF bucket size
> - state: TbfState,
> -}
> -
> -impl RateLimiter {
> - /// Creates a new instance, using [Instant::now] as start time.
> - pub fn new(rate: u64, bucket_size: u64) -> Self {
> - let start_time = Instant::now();
> - Self::with_start_time(rate, bucket_size, start_time)
> - }
> -
> - /// Creates a new instance with specified `rate`, `bucket_size` and `start_time`.
> - pub fn with_start_time(rate: u64, bucket_size: u64, start_time: Instant) -> Self {
> - Self {
> - rate,
> - bucket_size,
> - state: TbfState {
> - traffic: 0,
> - last_update: start_time,
> - // start with empty bucket (all tokens consumed)
> - consumed_tokens: bucket_size,
> - },
> - }
> - }
> -}
> -
> -impl RateLimit for RateLimiter {
> - fn update_rate(&mut self, rate: u64, bucket_size: u64) {
> - self.rate = rate;
> -
> - if bucket_size < self.bucket_size && self.state.consumed_tokens > bucket_size {
> - self.state.consumed_tokens = bucket_size; // start again
> - }
> -
> - self.bucket_size = bucket_size;
> - }
> -
> - fn traffic(&self) -> u64 {
> - self.state.traffic
> - }
> -
> - fn register_traffic(&mut self, current_time: Instant, data_len: u64) -> Duration {
> - self.state
> - .register_traffic(self.rate, self.bucket_size, current_time, data_len)
> - }
> -}
> -
> -impl<R: RateLimit + Send> ShareableRateLimit for std::sync::Mutex<R> {
> - fn update_rate(&self, rate: u64, bucket_size: u64) {
> - self.lock().unwrap().update_rate(rate, bucket_size);
> - }
> -
> - fn traffic(&self) -> u64 {
> - self.lock().unwrap().traffic()
> - }
> -
> - fn register_traffic(&self, current_time: Instant, data_len: u64) -> Duration {
> - self.lock()
> - .unwrap()
> - .register_traffic(current_time, data_len)
> - }
> -}
> -
> -/// Array of rate limiters.
> -///
> -/// A group of rate limiters with same configuration.
> -pub struct RateLimiterVec {
> - rate: u64, // tokens/second
> - bucket_size: u64, // TBF bucket size
> - state: Vec<TbfState>,
> -}
> -
> -impl RateLimiterVec {
> - /// Creates a new instance, using [Instant::now] as start time.
> - pub fn new(group_size: usize, rate: u64, bucket_size: u64) -> Self {
> - let start_time = Instant::now();
> - Self::with_start_time(group_size, rate, bucket_size, start_time)
> - }
> -
> - /// Creates a new instance with specified `rate`, `bucket_size` and `start_time`.
> - pub fn with_start_time(
> - group_size: usize,
> - rate: u64,
> - bucket_size: u64,
> - start_time: Instant,
> - ) -> Self {
> - let state = TbfState {
> - traffic: 0,
> - last_update: start_time,
> - // start with empty bucket (all tokens consumed)
> - consumed_tokens: bucket_size,
> - };
> - Self {
> - rate,
> - bucket_size,
> - state: vec![state; group_size],
> - }
> - }
> -
> - #[allow(clippy::len_without_is_empty)]
> - /// Return the number of TBF entries (group_size)
> - pub fn len(&self) -> usize {
> - self.state.len()
> - }
> -
> - /// Traffic for the specified index
> - pub fn traffic(&self, index: usize) -> Result<u64, Error> {
> - if index >= self.state.len() {
> - bail!("RateLimiterVec::traffic - index out of range");
> - }
> - Ok(self.state[index].traffic)
> - }
> -
> - /// Register traffic at the specified index
> - pub fn register_traffic(
> - &mut self,
> - index: usize,
> - current_time: Instant,
> - data_len: u64,
> - ) -> Result<Duration, Error> {
> - if index >= self.state.len() {
> - bail!("RateLimiterVec::register_traffic - index out of range");
> - }
> -
> - Ok(self.state[index].register_traffic(self.rate, self.bucket_size, current_time, data_len))
> - }
> -}
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
next prev parent reply other threads:[~2025-11-11 10:36 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-16 12:41 [pbs-devel] [PATCH proxmox{, -backup} v2 0/8] shared rate limiter for s3 client instances Christian Ebner
2025-09-16 12:41 ` [pbs-devel] [PATCH proxmox v2 1/4] rate-limiter: add crate for traffic rate limiter implementations Christian Ebner
2025-11-11 10:34 ` Hannes Laimer
2025-11-11 15:06 ` Christian Ebner
2025-09-16 12:41 ` [pbs-devel] [PATCH proxmox v2 2/4] http: drop factored out rate limiter implementation Christian Ebner
2025-11-11 10:36 ` Hannes Laimer [this message]
2025-09-16 12:41 ` [pbs-devel] [PATCH proxmox v2 3/4] rest-server: optionally depend on factored out shared rate limiter Christian Ebner
2025-11-11 10:42 ` Hannes Laimer
2025-09-16 12:41 ` [pbs-devel] [PATCH proxmox v2 4/4] s3-client: add shared rate limiter via https connector Christian Ebner
2025-11-11 10:41 ` Hannes Laimer
2025-09-16 12:41 ` [pbs-devel] [PATCH proxmox-backup v2 1/4] traffic control: use factored out shared rate limiter Christian Ebner
2025-11-11 10:45 ` Hannes Laimer
2025-09-16 12:41 ` [pbs-devel] [PATCH proxmox-backup v2 2/4] api: config: update s3 endpoint rate limits in config Christian Ebner
2025-11-11 10:45 ` Hannes Laimer
2025-09-16 12:41 ` [pbs-devel] [PATCH proxmox-backup v2 3/4] datastore: s3: set rate limiter options for s3 client Christian Ebner
2025-11-11 10:46 ` Hannes Laimer
2025-09-16 12:41 ` [pbs-devel] [PATCH proxmox-backup v2 4/4] ui: expose rate and burst limits for s3 endpoints Christian Ebner
2025-11-11 10:46 ` Hannes Laimer
2025-11-11 10:49 ` [pbs-devel] [PATCH proxmox{, -backup} v2 0/8] shared rate limiter for s3 client instances Hannes Laimer
2025-11-11 15:10 ` Christian Ebner
2025-11-12 11:55 ` [pbs-devel] superseded: " Christian Ebner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ab94fb25-1b2c-4482-b709-b5bb32f60228@proxmox.com \
--to=h.laimer@proxmox.com \
--cc=c.ebner@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox