diff --git a/src/api/client/membership/join.rs b/src/api/client/membership/join.rs index 48ede1b1..8fea6334 100644 --- a/src/api/client/membership/join.rs +++ b/src/api/client/membership/join.rs @@ -5,7 +5,7 @@ use ruma::{ RoomId, RoomOrAliasId, api::client::membership::{join_room_by_id, join_room_by_id_or_alias}, }; -use tuwunel_core::Result; +use tuwunel_core::{Result, warn}; use super::banned_room_check; use crate::{Ruma, client::membership::get_join_params}; @@ -36,7 +36,8 @@ pub(crate) async fn join_room_by_id_route( let state_lock = services.state.mutex.lock(&room_id).await; - services + let mut errors = 0_usize; + while let Err(e) = services .membership .join( sender_user, @@ -47,7 +48,17 @@ pub(crate) async fn join_room_by_id_route( &state_lock, ) .boxed() - .await?; + .await + { + errors = errors.saturating_add(1); + if errors >= services.config.max_join_attempts_per_join_request { + warn!( + "Several servers failed. Giving up for this request. Try again for different \ + server selection." + ); + return Err(e); + } + } drop(state_lock); @@ -80,7 +91,8 @@ pub(crate) async fn join_room_by_id_or_alias_route( let state_lock = services.state.mutex.lock(&room_id).await; - services + let mut errors = 0_usize; + while let Err(e) = services .membership .join( sender_user, @@ -91,7 +103,17 @@ pub(crate) async fn join_room_by_id_or_alias_route( &state_lock, ) .boxed() - .await?; + .await + { + errors = errors.saturating_add(1); + if errors >= services.config.max_join_attempts_per_join_request { + warn!( + "Several servers failed. Giving up for this request. Try again for different \ + server selection." + ); + return Err(e); + } + } drop(state_lock); diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index d5beb655..8b275b0a 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1757,6 +1757,26 @@ pub struct Config { )] pub deprioritize_joins_through_servers: RegexSet, + /// Maximum make_join requests to attempt within each join attempt. Each + /// attempt tries a different server, as each server is only tried once; + /// though retries can occur when the join request as a whole is retried. + /// + /// default: 48 + #[serde(default = "default_max_make_join_attempts_per_join_attempt")] + pub max_make_join_attempts_per_join_attempt: usize, + + /// Maximum join attempts to conduct per client join request. Each join + /// attempt consists of one or more make_join requests limited above, and a + /// single send_join request. This value allows for additional servers to + /// act as the join-server prior to reporting the last error back to the + /// client, which can be frustrating for users. Therefor the default value + /// is greater than one, but less than excessively exceeding the client's + /// request timeout, though that may not be avoidable in some cases. + /// + /// default: 3 + #[serde(default = "default_max_join_attempts_per_join_request")] + pub max_join_attempts_per_join_request: usize, + /// Retry failed and incomplete messages to remote servers immediately upon /// startup. This is called bursting. If this is disabled, said messages may /// not be delivered until more messages are queued for that server. Do not @@ -2997,3 +3017,7 @@ fn default_deprioritize_joins_through_servers() -> RegexSet { } fn default_one_time_key_limit() -> usize { 256 } + +fn default_max_make_join_attempts_per_join_attempt() -> usize { 48 } + +fn default_max_join_attempts_per_join_request() -> usize { 3 } diff --git a/src/service/membership/join.rs b/src/service/membership/join.rs index 0b078a94..aeac074a 100644 --- a/src/service/membership/join.rs +++ b/src/service/membership/join.rs @@ -806,10 +806,15 @@ async fn make_join_request( return make_join_response_and_server; } - if make_join_counter > 40 { + let max_attempts = self + .services + .config + .max_make_join_attempts_per_join_attempt; + if make_join_counter >= max_attempts { + warn!(?remote_server, "last make_join failure reason: {e}"); warn!( - "40 servers failed to provide valid make_join response, assuming no server \ - can assist in joining." + "{max_attempts} servers failed to provide valid make_join response, \ + assuming no server can assist in joining." ); make_join_response_and_server = Err!(BadServerResponse("No server available to assist in joining.")); diff --git a/tuwunel-example.toml b/tuwunel-example.toml index f4f10046..fcc34ea1 100644 --- a/tuwunel-example.toml +++ b/tuwunel-example.toml @@ -1516,6 +1516,22 @@ # #deprioritize_joins_through_servers = ["matrix\.org"] +# Maximum make_join requests to attempt within each join attempt. Each +# attempt tries a different server, as each server is only tried once; +# though retries can occur when the join request as a whole is retried. +# +#max_make_join_attempts_per_join_attempt = 48 + +# Maximum join attempts to conduct per client join request. Each join +# attempt consists of one or more make_join requests limited above, and a +# single send_join request. This value allows for additional servers to +# act as the join-server prior to reporting the last error back to the +# client, which can be frustrating for users. Therefor the default value +# is greater than one, but less than excessively exceeding the client's +# request timeout, though that may not be avoidable in some cases. +# +#max_join_attempts_per_join_request = 3 + # Retry failed and incomplete messages to remote servers immediately upon # startup. This is called bursting. If this is disabled, said messages may # not be delivered until more messages are queued for that server. Do not