mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Improve backoff and behaviour in case of RetryAfter errors
This commit is contained in:
parent
ccfc6165e6
commit
3da5a1a4c5
9 changed files with 136 additions and 15 deletions
|
@ -67,6 +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 1112][https://github.com/teloxide/teloxide/pull/1112])
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
|
84
Cargo.lock
generated
84
Cargo.lock
generated
|
@ -691,6 +691,12 @@ 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"
|
||||||
|
@ -736,6 +742,12 @@ 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"
|
||||||
|
@ -1444,6 +1456,15 @@ 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"
|
||||||
|
@ -1597,6 +1618,12 @@ 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"
|
||||||
|
@ -1671,6 +1698,36 @@ 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"
|
||||||
|
@ -2221,6 +2278,7 @@ 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",
|
||||||
|
@ -2411,6 +2469,23 @@ 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"
|
||||||
|
@ -2897,6 +2972,15 @@ 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"
|
||||||
|
|
|
@ -107,6 +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 ([#1112][pr1112])
|
||||||
|
|
||||||
[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
|
||||||
|
@ -116,6 +117,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
[pr990]: https://github.com/teloxide/teloxide/pull/990
|
[pr990]: https://github.com/teloxide/teloxide/pull/990
|
||||||
[pr990]: https://github.com/teloxide/teloxide/pull/990
|
[pr990]: https://github.com/teloxide/teloxide/pull/990
|
||||||
[pr1066]: https://github.com/teloxide/teloxide/pull/1066
|
[pr1066]: https://github.com/teloxide/teloxide/pull/1066
|
||||||
|
[pr1112]: https://github.com/teloxide/teloxide/pull/1112
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ use futures::{future::BoxFuture, FutureExt};
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
errors::AsResponseParameters,
|
||||||
payloads::*,
|
payloads::*,
|
||||||
requests::{HasPayload, Output, Payload, Request, Requester},
|
requests::{HasPayload, Output, Payload, Request, Requester},
|
||||||
types::*,
|
types::*,
|
||||||
|
@ -176,7 +177,7 @@ macro_rules! fwd_erased {
|
||||||
|
|
||||||
impl<'a, Err> Requester for ErasedRequester<'a, Err>
|
impl<'a, Err> Requester for ErasedRequester<'a, Err>
|
||||||
where
|
where
|
||||||
Err: std::error::Error + Send,
|
Err: std::error::Error + Send + AsResponseParameters,
|
||||||
{
|
{
|
||||||
type Err = Err;
|
type Err = Err;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{payloads::*, requests::Request, types::*};
|
use crate::{errors::AsResponseParameters, payloads::*, requests::Request, types::*};
|
||||||
|
|
||||||
/// Telegram Bot API client.
|
/// Telegram Bot API client.
|
||||||
///
|
///
|
||||||
|
@ -142,7 +142,7 @@ use crate::{payloads::*, requests::Request, types::*};
|
||||||
#[cfg_attr(all(any(docsrs, dep_docsrs), feature = "nightly"), doc(notable_trait))]
|
#[cfg_attr(all(any(docsrs, dep_docsrs), feature = "nightly"), doc(notable_trait))]
|
||||||
pub trait Requester {
|
pub trait Requester {
|
||||||
/// Error type returned by all requests.
|
/// Error type returned by all requests.
|
||||||
type Err: std::error::Error + Send;
|
type Err: std::error::Error + Send + AsResponseParameters;
|
||||||
|
|
||||||
// START BLOCK requester_methods
|
// START BLOCK requester_methods
|
||||||
// Generated by `codegen_requester_methods`, do not edit by hand.
|
// Generated by `codegen_requester_methods`, do not edit by hand.
|
||||||
|
|
|
@ -126,6 +126,7 @@ 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]
|
||||||
|
|
|
@ -2,14 +2,30 @@ 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 less than half an hour (1024
|
/// The maximum duration is limited to a little more than a minute: 64s, so the
|
||||||
/// secs), so the successive timings are(in secs): 1, 2, 4, .., 1024, 1024, ..
|
/// successive timings are: 1s, 2s, 4s, .., 64, .., 64
|
||||||
///
|
///
|
||||||
/// 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 {
|
||||||
// The error_count has to be limited so as not to cause overflow: 2^10 = 1024 ~
|
// 2^6 = 64s ~ a little more than a minute
|
||||||
// a little less than half an hour
|
Duration::from_secs(1_u64 << error_count.min(THRESHOLD_ERROR_QUANTITY))
|
||||||
Duration::from_secs(1_u64 << error_count.min(10))
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(1, Duration::from_secs(2))]
|
||||||
|
#[case(5, Duration::from_secs(32))]
|
||||||
|
#[case(42, Duration::from_secs(64))]
|
||||||
|
fn test_exponential_backoff_strategy(#[case] error_count: u32, #[case] expected: Duration) {
|
||||||
|
assert_eq!(exponential_backoff_strategy(error_count), expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use dptree::di::{DependencyMap, Injectable};
|
||||||
|
use futures::future::BoxFuture;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dispatching::{HandlerExt, UpdateFilterExt},
|
dispatching::{HandlerExt, UpdateFilterExt},
|
||||||
error_handlers::LoggingErrorHandler,
|
error_handlers::LoggingErrorHandler,
|
||||||
|
@ -6,9 +11,6 @@ use crate::{
|
||||||
update_listeners::{self, UpdateListener},
|
update_listeners::{self, UpdateListener},
|
||||||
utils::command::BotCommands,
|
utils::command::BotCommands,
|
||||||
};
|
};
|
||||||
use dptree::di::{DependencyMap, Injectable};
|
|
||||||
use futures::future::BoxFuture;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
/// A [REPL] for commands.
|
/// A [REPL] for commands.
|
||||||
///
|
///
|
||||||
|
|
|
@ -13,6 +13,8 @@ use std::{
|
||||||
use futures::{ready, stream::Stream};
|
use futures::{ready, stream::Stream};
|
||||||
use tokio::time::{sleep, Sleep};
|
use tokio::time::{sleep, Sleep};
|
||||||
|
|
||||||
|
use teloxide_core::errors::AsResponseParameters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backoff::{exponential_backoff_strategy, BackoffStrategy},
|
backoff::{exponential_backoff_strategy, BackoffStrategy},
|
||||||
requests::{HasPayload, Request, Requester},
|
requests::{HasPayload, Request, Requester},
|
||||||
|
@ -436,10 +438,22 @@ impl<B: Requester> Stream for PollingStream<'_, B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// Prevents the CPU spike occuring at network connection lose: <https://github.com/teloxide/teloxide/issues/780>
|
/*
|
||||||
let backoff_strategy = &this.polling.backoff_strategy;
|
In case of RetryAfter(..) error we pause the polling for the specified amount
|
||||||
this.eepy.set(Some(sleep(backoff_strategy(*this.error_count))));
|
of seconds.
|
||||||
log::trace!("set {:?} reconnection delay", backoff_strategy(*this.error_count));
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
if let Some(seconds) = err.retry_after() {
|
||||||
|
log::info!("got `RetryAfter({})` error, polling paused", seconds);
|
||||||
|
this.eepy.set(Some(sleep(seconds.duration())));
|
||||||
|
} else {
|
||||||
|
let delay = (this.polling.backoff_strategy)(*this.error_count);
|
||||||
|
log::info!("retrying getting updates in {}s", delay.as_secs());
|
||||||
|
this.eepy.set(Some(sleep(delay)));
|
||||||
|
}
|
||||||
|
|
||||||
return Ready(Some(Err(err)));
|
return Ready(Some(Err(err)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue