From 146d42de37c4b26cc1ec40d69834b43fc5cf9c6e Mon Sep 17 00:00:00 2001 From: Ilya Bizyaev Date: Fri, 6 Dec 2024 22:52:27 +0100 Subject: [PATCH] Fix calculation of per-second used counts in throttling This is not the cause of throttling issues that I was having, but something I think is a bug from reading the code. `history` is a deque that is kept sorted by the timestamp: old entries are popped from the front, and new entries are pushed to the back. To calculate `used` and `requests_sent.per_sec[chat]`, we want to count entries from the past second, so the newest ones, so from the back. For `take_while` to work as expected, it needs to be called on the reverse iterator, as the regular iterator is front-to-back. Otherwise `take_while` can finish early due to entries that are up to a minute old. --- CHANGELOG.md | 1 + crates/teloxide-core/src/adaptors/throttle/worker.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e56d7a..dbd5b4d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Now `InlineQueryResultsButton` serializes properly ([issue 1181](https://github.com/teloxide/teloxide/issues/1181)) - Now `ThreadId` is able to serialize in multipart requests ([PR 1179](https://github.com/teloxide/teloxide/pull/1179)) - Now stack does not overflow on dispatch ([issue 1154](https://github.com/teloxide/teloxide/issues/1154)) +- Fixed calculation of per-second limits in the `Throttle` adaptor ([PR 1212](https://github.com/teloxide/teloxide/pull/1212)) ## 0.13.0 - 2024-08-16 diff --git a/crates/teloxide-core/src/adaptors/throttle/worker.rs b/crates/teloxide-core/src/adaptors/throttle/worker.rs index 26d08493..67c11114 100644 --- a/crates/teloxide-core/src/adaptors/throttle/worker.rs +++ b/crates/teloxide-core/src/adaptors/throttle/worker.rs @@ -213,7 +213,7 @@ pub(super) async fn worker( // as truncates which is ok since in case of truncation it would always be >= // limits.overall_s - let used = history.iter().take_while(|(_, time)| time > &sec_back).count() as u32; + let used = history.iter().rev().take_while(|(_, time)| time > &sec_back).count() as u32; let mut allowed = limits.messages_per_sec_overall.saturating_sub(used); if allowed == 0 { @@ -222,7 +222,7 @@ pub(super) async fn worker( continue; } - for (chat, _) in history.iter().take_while(|(_, time)| time > &sec_back) { + for (chat, _) in history.iter().rev().take_while(|(_, time)| time > &sec_back) { *requests_sent.per_sec.entry(*chat).or_insert(0) += 1; }