Refactor backoff-related code

This commit is contained in:
Сырцев Вадим Игоревич 2024-07-28 13:03:21 +03:00
parent 87188d5dea
commit 4f1a458fb7
No known key found for this signature in database
GPG key ID: D581B7E10673309B
6 changed files with 24 additions and 107 deletions

View file

@ -67,7 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Feature `sqlite-storage` was renamed to `sqlite-storage-nativetls`([PR 995](https://github.com/teloxide/teloxide/pull/995)) - Feature `sqlite-storage` was renamed to `sqlite-storage-nativetls`([PR 995](https://github.com/teloxide/teloxide/pull/995))
- MSRV (Minimal Supported Rust Version) was bumped from `1.68.0` to `1.70.0` ([PR 996][https://github.com/teloxide/teloxide/pull/996]) - MSRV (Minimal Supported Rust Version) was bumped from `1.68.0` to `1.70.0` ([PR 996][https://github.com/teloxide/teloxide/pull/996])
- `axum` was bumped to `0.7`, along with related libraries used for webhooks ([PR 1093][https://github.com/teloxide/teloxide/pull/1093]) - `axum` was bumped to `0.7`, along with related libraries used for webhooks ([PR 1093][https://github.com/teloxide/teloxide/pull/1093])
- Exponential backoff now results in 64 seconds maximum delay instead of 17 minutes ([PR 1113][https://github.com/teloxide/teloxide/pull/1113]) - `Polling`'s exponential backoff now results in 64 seconds maximum delay instead of 17 minutes ([PR 1113][https://github.com/teloxide/teloxide/pull/1113])
### Removed ### Removed

84
Cargo.lock generated
View file

@ -691,12 +691,6 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.30" version = "0.3.30"
@ -742,12 +736,6 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.26" version = "0.3.26"
@ -1456,15 +1444,6 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "proc-macro-crate"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
dependencies = [
"toml_edit",
]
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.4" version = "1.0.4"
@ -1618,12 +1597,6 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "relative-path"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.27" version = "0.11.27"
@ -1698,36 +1671,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "rstest"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afd55a67069d6e434a95161415f5beeada95a01c7b815508a82dcb0e1593682"
dependencies = [
"futures",
"futures-timer",
"rstest_macros",
"rustc_version",
]
[[package]]
name = "rstest_macros"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4165dfae59a39dd41d8dec720d3cbfbc71f69744efb480a3920f5d4e0cc6798d"
dependencies = [
"cfg-if",
"glob",
"proc-macro-crate",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn 2.0.52",
"unicode-ident",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -2278,7 +2221,6 @@ dependencies = [
"pretty_env_logger 0.5.0", "pretty_env_logger 0.5.0",
"rand", "rand",
"reqwest", "reqwest",
"rstest",
"serde", "serde",
"serde_cbor", "serde_cbor",
"serde_json", "serde_json",
@ -2469,23 +2411,6 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "toml_datetime"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
[[package]]
name = "toml_edit"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [
"indexmap 2.2.5",
"toml_datetime",
"winnow",
]
[[package]] [[package]]
name = "tower" name = "tower"
version = "0.4.13" version = "0.4.13"
@ -2972,15 +2897,6 @@ version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.50.0" version = "0.50.0"

View file

@ -107,7 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Deserialization of `ApiError::CantParseEntities` ([#839][pr839]) - Deserialization of `ApiError::CantParseEntities` ([#839][pr839])
- Deserialization of empty (content-less) messages that can sometimes appear as a part of callback query ([#850][pr850], issue [#873][issue873]) - Deserialization of empty (content-less) messages that can sometimes appear as a part of callback query ([#850][pr850], issue [#873][issue873])
- Type of `chat_id` in `send_game`: `u32` => `ChatId` ([#1066][pr1066]) - Type of `chat_id` in `send_game`: `u32` => `ChatId` ([#1066][pr1066])
- In case of `RetryAfter(..)` errors the polling is paused for the specified delay instead of falling into the backoff strategy ([#1113][pr1113]) - `Requester::Err` was bounded to the `AsResponseParameters`. In case of `RetryAfter(..)` errors the polling is paused for the specified delay instead of falling into the backoff strategy ([#1113][pr1113])
[pr839]: https://github.com/teloxide/teloxide/pull/839 [pr839]: https://github.com/teloxide/teloxide/pull/839
[pr879]: https://github.com/teloxide/teloxide/pull/879 [pr879]: https://github.com/teloxide/teloxide/pull/879

View file

@ -126,7 +126,6 @@ tokio = { version = "1.8", features = ["fs", "rt-multi-thread", "macros"] }
reqwest = "0.11.11" reqwest = "0.11.11"
chrono = "0.4" chrono = "0.4"
tokio-stream = "0.1" tokio-stream = "0.1"
rstest = "0.21.0"
[package.metadata.docs.rs] [package.metadata.docs.rs]

View file

@ -2,8 +2,6 @@ use std::time::Duration;
pub type BackoffStrategy = Box<dyn Send + Fn(u32) -> Duration>; pub type BackoffStrategy = Box<dyn Send + Fn(u32) -> Duration>;
const THRESHOLD_ERROR_QUANTITY: u32 = 6;
/// Calculates the backoff time in seconds for exponential strategy with base 2 /// Calculates the backoff time in seconds for exponential strategy with base 2
/// ///
/// The maximum duration is limited to a little more than a minute: 64s, so the /// The maximum duration is limited to a little more than a minute: 64s, so the
@ -12,20 +10,23 @@ const THRESHOLD_ERROR_QUANTITY: u32 = 6;
/// More at: <https://en.wikipedia.org/wiki/Exponential_backoff#Exponential_backoff_algorithm> /// More at: <https://en.wikipedia.org/wiki/Exponential_backoff#Exponential_backoff_algorithm>
pub fn exponential_backoff_strategy(error_count: u32) -> Duration { pub fn exponential_backoff_strategy(error_count: u32) -> Duration {
// 2^6 = 64s ~ a little more than a minute // 2^6 = 64s ~ a little more than a minute
Duration::from_secs(1_u64 << error_count.min(THRESHOLD_ERROR_QUANTITY)) Duration::from_secs(1_u64 << error_count.min(6))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rstest::rstest;
use super::*; use super::*;
#[rstest] #[test]
#[case(1, Duration::from_secs(2))] fn test_exponential_backoff_strategy() {
#[case(5, Duration::from_secs(32))] let cases = [
#[case(42, Duration::from_secs(64))] (1, Duration::from_secs(2)),
fn test_exponential_backoff_strategy(#[case] error_count: u32, #[case] expected: Duration) { (5, Duration::from_secs(32)),
(42, Duration::from_secs(64)),
];
for (error_count, expected) in cases {
assert_eq!(exponential_backoff_strategy(error_count), expected); assert_eq!(exponential_backoff_strategy(error_count), expected);
} }
} }
}

View file

@ -425,7 +425,7 @@ impl<B: Requester> Stream for PollingStream<'_, B> {
return Ready(Some(Err(err))); return Ready(Some(Err(err)));
} }
Ok(updates) => { Ok(updates) => {
// Once we got the update hense the backoff reconnection strategy worked // Once we got the update the backoff reconnection strategy worked
*this.error_count = 0; *this.error_count = 0;
if let Some(upd) = updates.last() { if let Some(upd) = updates.last() {
@ -445,14 +445,15 @@ impl<B: Requester> Stream for PollingStream<'_, B> {
Otherwise, in case of network connection lose (<https://github.com/teloxide/teloxide/issues/780>) we Otherwise, in case of network connection lose (<https://github.com/teloxide/teloxide/issues/780>) we
use the backoff strategy to prevent the high CPU usage due to multiple instant reconnections use the backoff strategy to prevent the high CPU usage due to multiple instant reconnections
*/ */
if let Some(seconds) = err.retry_after() { let delay = match err.retry_after() {
log::info!("got `RetryAfter({})` error, polling paused", seconds); Some(seconds) => {
this.eepy.set(Some(sleep(seconds.duration()))); *this.error_count = 0;
} else { seconds.duration()
let delay = (this.polling.backoff_strategy)(*this.error_count); }
None => (this.polling.backoff_strategy)(*this.error_count),
};
log::info!("retrying getting updates in {}s", delay.as_secs()); log::info!("retrying getting updates in {}s", delay.as_secs());
this.eepy.set(Some(sleep(delay))); this.eepy.set(Some(sleep(delay)));
}
return Ready(Some(Err(err))); return Ready(Some(Err(err)));
} }