public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Christian Ebner <c.ebner@proxmox.com>
To: Proxmox Backup Server development discussion
	<pbs-devel@lists.proxmox.com>,
	Hannes Laimer <h.laimer@proxmox.com>
Subject: Re: [pbs-devel] [PATCH proxmox v3 2/3] http: track user tag updates on rate-limited streams
Date: Wed, 12 Nov 2025 10:46:17 +0100	[thread overview]
Message-ID: <65cbff3b-56d0-4528-bc89-fad80e3029f0@proxmox.com> (raw)
In-Reply-To: <20251110134255.69132-3-h.laimer@proxmox.com>

nit: needs reformatting via cargo fmt

On 11/10/25 2:42 PM, Hannes Laimer wrote:
> Introduce rate-limit tags with a user variant and let rate-limited
> streams hold a shared handle so callbacks can refresh limits
> whenever the tag set changes.
> 
> If we decide to implement something like [1] in the future this could
> potentially include group rate-limits for example.
> 
> [1] https://bugzilla.proxmox.com/show_bug.cgi?id=5867
> 
> Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
> ---
>   proxmox-http/src/lib.rs                 |  2 +-
>   proxmox-http/src/rate_limited_stream.rs | 40 +++++++++++++++++++++----
>   2 files changed, 36 insertions(+), 6 deletions(-)
> 
> diff --git a/proxmox-http/src/lib.rs b/proxmox-http/src/lib.rs
> index 8b6953b0..990f8f36 100644
> --- a/proxmox-http/src/lib.rs
> +++ b/proxmox-http/src/lib.rs
> @@ -34,7 +34,7 @@ pub use rate_limiter::{RateLimit, RateLimiter, RateLimiterVec, ShareableRateLimi
>   #[cfg(feature = "rate-limited-stream")]
>   mod rate_limited_stream;
>   #[cfg(feature = "rate-limited-stream")]
> -pub use rate_limited_stream::RateLimitedStream;
> +pub use rate_limited_stream::{RateLimitedStream, RateLimiterTag, RateLimiterTags};
>   
>   #[cfg(feature = "body")]
>   mod body;
> diff --git a/proxmox-http/src/rate_limited_stream.rs b/proxmox-http/src/rate_limited_stream.rs
> index e24df7af..6b525591 100644
> --- a/proxmox-http/src/rate_limited_stream.rs
> +++ b/proxmox-http/src/rate_limited_stream.rs
> @@ -15,8 +15,15 @@ use super::{RateLimiter, ShareableRateLimit};
>   
>   type SharedRateLimit = Arc<dyn ShareableRateLimit>;
>   
> +#[derive(Clone, Debug, PartialEq, Eq)]
> +pub enum RateLimiterTag {
> +    User(String),
> +}
> +
> +pub type RateLimiterTags = Vec<RateLimiterTag>;
> +
>   pub type RateLimiterCallback =
> -    dyn Fn() -> (Option<SharedRateLimit>, Option<SharedRateLimit>) + Send;
> +    dyn Fn(&[RateLimiterTag]) -> (Option<SharedRateLimit>, Option<SharedRateLimit>) + Send;
>   
>   /// A rate limited stream using [RateLimiter]
>   pub struct RateLimitedStream<S> {
> @@ -26,6 +33,8 @@ pub struct RateLimitedStream<S> {
>       write_delay: Option<Pin<Box<Sleep>>>,
>       update_limiter_cb: Option<Box<RateLimiterCallback>>,
>       last_limiter_update: Instant,
> +    tag_handle: Option<Arc<Mutex<RateLimiterTags>>>,
> +    last_tags: Option<RateLimiterTags>,
>       stream: S,
>   }
>   
> @@ -53,6 +62,8 @@ impl<S> RateLimitedStream<S> {
>               write_delay: None,
>               update_limiter_cb: None,
>               last_limiter_update: Instant::now(),
> +            tag_handle: None,
> +            last_tags: None,
>               stream,
>           }
>       }
> @@ -64,12 +75,13 @@ impl<S> RateLimitedStream<S> {
>       /// Note: This function is called within an async context, so it
>       /// should be fast and must not block.
>       pub fn with_limiter_update_cb<
> -        F: Fn() -> (Option<SharedRateLimit>, Option<SharedRateLimit>) + Send + 'static,
> +        F: Fn(&[RateLimiterTag]) -> (Option<SharedRateLimit>, Option<SharedRateLimit>) + Send + 'static,
>       >(
>           stream: S,
>           update_limiter_cb: F,
>       ) -> Self {
> -        let (read_limiter, write_limiter) = update_limiter_cb();
> +        let tag_handle = Some(Arc::new(Mutex::new(Vec::new())));
> +        let (read_limiter, write_limiter) = update_limiter_cb(&[]);
>           Self {
>               read_limiter,
>               read_delay: None,
> @@ -77,15 +89,29 @@ impl<S> RateLimitedStream<S> {
>               write_delay: None,
>               update_limiter_cb: Some(Box::new(update_limiter_cb)),
>               last_limiter_update: Instant::now(),
> +            tag_handle,
> +            last_tags: None,
>               stream,
>           }
>       }
>   
>       fn update_limiters(&mut self) {
>           if let Some(ref update_limiter_cb) = self.update_limiter_cb {
> -            if self.last_limiter_update.elapsed().as_secs() >= 5 {
> +            let mut force_update = false;
> +            let current_tags = self
> +                .tag_handle
> +                .as_ref()
> +                .map(|handle| handle.lock().unwrap().clone());
> +
> +            if self.last_tags != current_tags {
> +                self.last_tags = current_tags.clone();
> +                force_update = true;
> +            }
> +
> +            if force_update || self.last_limiter_update.elapsed().as_secs() >= 5 {
>                   self.last_limiter_update = Instant::now();
> -                let (read_limiter, write_limiter) = update_limiter_cb();
> +                let tags = self.last_tags.as_ref().map(|tags| tags.as_slice()).unwrap_or(&[]);
> +                let (read_limiter, write_limiter) = update_limiter_cb(tags);
>                   self.read_limiter = read_limiter;
>                   self.write_limiter = write_limiter;
>               }
> @@ -99,6 +125,10 @@ impl<S> RateLimitedStream<S> {
>       pub fn inner_mut(&mut self) -> &mut S {
>           &mut self.stream
>       }
> +
> +    pub fn tag_handle(&self) -> Option<Arc<Mutex<RateLimiterTags>>> {
> +        self.tag_handle.as_ref().map(Arc::clone)
> +    }
>   }
>   
>   fn register_traffic(limiter: &dyn ShareableRateLimit, count: usize) -> Option<Pin<Box<Sleep>>> {



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel


  reply	other threads:[~2025-11-12  9:46 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-10 13:42 [pbs-devel] [PATCH proxmox{, -backup} v3 0/6] add user specific rate-limits Hannes Laimer
2025-11-10 13:42 ` [pbs-devel] [PATCH proxmox v3 1/3] pbs-api-types: allow traffic-control rules to match users Hannes Laimer
2025-11-12  9:46   ` Christian Ebner
2025-11-10 13:42 ` [pbs-devel] [PATCH proxmox v3 2/3] http: track user tag updates on rate-limited streams Hannes Laimer
2025-11-12  9:46   ` Christian Ebner [this message]
2025-11-10 13:42 ` [pbs-devel] [PATCH proxmox v3 3/3] rest-server: propagate rate-limit tags from authenticated users Hannes Laimer
2025-11-12  9:55   ` Christian Ebner
2025-11-10 13:42 ` [pbs-devel] [PATCH proxmox-backup v3 1/3] api: taffic-control: update/delete users on rule correctly Hannes Laimer
2025-11-10 13:42 ` [pbs-devel] [PATCH proxmox-backup v3 2/3] traffic-control: handle users specified in a " Hannes Laimer
2025-11-12  9:55   ` Christian Ebner
2025-11-10 13:42 ` [pbs-devel] [PATCH proxmox-backup v3 3/3] ui: traffic-control: add users field in edit form and list Hannes Laimer
2025-11-12  9:55   ` Christian Ebner
2025-11-12 10:08 ` [pbs-devel] [PATCH proxmox{, -backup} v3 0/6] add user specific rate-limits Christian Ebner
2025-11-12 10:36 ` [pbs-devel] superseded: " Hannes Laimer

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=65cbff3b-56d0-4528-bc89-fad80e3029f0@proxmox.com \
    --to=c.ebner@proxmox.com \
    --cc=h.laimer@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
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal