diff --git a/osu.Game/Online/PersistentEndpointClientConnector.cs b/osu.Game/Online/PersistentEndpointClientConnector.cs index 024a0fea73..9e7543ce2b 100644 --- a/osu.Game/Online/PersistentEndpointClientConnector.cs +++ b/osu.Game/Online/PersistentEndpointClientConnector.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Logging; +using osu.Framework.Utils; using osu.Game.Online.API; namespace osu.Game.Online @@ -31,6 +32,12 @@ namespace osu.Game.Online private CancellationTokenSource connectCancelSource = new CancellationTokenSource(); private bool started; + /// + /// How much to delay before attempting to connect again, in milliseconds. + /// Subject to exponential back-off. + /// + private int retryDelay = 3000; + /// /// Constructs a new . /// @@ -78,6 +85,8 @@ namespace osu.Game.Online private async Task connect() { cancelExistingConnect(); + // reset retry delay to default. + retryDelay = 3000; if (!await connectionLock.WaitAsync(10000).ConfigureAwait(false)) throw new TimeoutException("Could not obtain a lock to connect. A previous attempt is likely stuck."); @@ -134,8 +143,15 @@ namespace osu.Game.Online /// private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken) { - Logger.Log($"{ClientName} connect attempt failed: {exception.Message}", LoggingTarget.Network); - await Task.Delay(5000, cancellationToken).ConfigureAwait(false); + // random stagger factor to avoid mass incidental synchronisation + // compare: https://github.com/peppy/osu-stable-reference/blob/013c3010a9d495e3471a9c59518de17006f9ad89/osu!/Online/BanchoClient.cs#L331 + int thisDelay = (int)(retryDelay * RNG.NextDouble(0.75, 1.25)); + // exponential backoff with upper limit + // compare: https://github.com/peppy/osu-stable-reference/blob/013c3010a9d495e3471a9c59518de17006f9ad89/osu!/Online/BanchoClient.cs#L539 + retryDelay = Math.Min(120000, (int)(retryDelay * 1.5)); + + Logger.Log($"{ClientName} connect attempt failed: {exception.Message}. Next attempt in {thisDelay / 1000:N0} seconds.", LoggingTarget.Network); + await Task.Delay(thisDelay, cancellationToken).ConfigureAwait(false); } ///