From 76ebe6be06007c10d78152c1b33f1480260e646b Mon Sep 17 00:00:00 2001 From: Yavor Georgiev Date: Thu, 22 Aug 2024 00:50:51 +0200 Subject: [PATCH 1/4] Expose ReconnectBackoffOptions on SyncTimeoutOptions --- Realm/Realm/Native/AppConfiguration.cs | 38 +++++++--- Realm/Realm/Sync/App.cs | 20 ++++-- Realm/Realm/Sync/ReconnectBackoffOptions.cs | 80 +++++++++++++++++++++ Realm/Realm/Sync/SyncTimeoutOptions.cs | 8 +++ wrappers/src/app_cs.cpp | 49 +++++++++---- 5 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 Realm/Realm/Sync/ReconnectBackoffOptions.cs diff --git a/Realm/Realm/Native/AppConfiguration.cs b/Realm/Realm/Native/AppConfiguration.cs index 8dc1c83ee7..45ef6880bb 100644 --- a/Realm/Realm/Native/AppConfiguration.cs +++ b/Realm/Realm/Native/AppConfiguration.cs @@ -21,6 +21,34 @@ namespace Realms.Sync.Native { + [StructLayout(LayoutKind.Sequential)] + internal struct ReconnectBackoffOptions + { + internal UInt64 max_resumption_delay_interval_ms; + + internal UInt64 resumption_delay_interval_ms; + + internal int resumption_delay_backoff_multiplier; + + internal int delay_jitter_divisor; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SyncTimeoutOptions + { + internal UInt64 sync_connect_timeout_ms; + + internal UInt64 sync_connection_linger_time_ms; + + internal UInt64 sync_ping_keep_alive_period_ms; + + internal UInt64 sync_pong_keep_alive_timeout_ms; + + internal UInt64 sync_fast_reconnect_limit; + + internal ReconnectBackoffOptions reconnect_backoff_options; + } + [StructLayout(LayoutKind.Sequential)] internal struct AppConfiguration { @@ -83,15 +111,7 @@ internal MetadataPersistenceMode? MetadataPersistence internal IntPtr managed_websocket_provider; - internal UInt64 sync_connect_timeout_ms; - - internal UInt64 sync_connection_linger_time_ms; - - internal UInt64 sync_ping_keep_alive_period_ms; - - internal UInt64 sync_pong_keep_alive_timeout_ms; - - internal UInt64 sync_fast_reconnect_limit; + internal SyncTimeoutOptions sync_timeout_options; [MarshalAs(UnmanagedType.U1)] internal bool use_cache; diff --git a/Realm/Realm/Sync/App.cs b/Realm/Realm/Sync/App.cs index 88036677bc..6e8aff0f81 100644 --- a/Realm/Realm/Sync/App.cs +++ b/Realm/Realm/Sync/App.cs @@ -174,11 +174,21 @@ public static App Create(AppConfiguration config) MetadataPersistence = config.MetadataPersistenceMode, default_request_timeout_ms = (ulong)config.DefaultRequestTimeout.TotalMilliseconds, managed_http_client = GCHandle.ToIntPtr(clientHandle), - sync_connection_linger_time_ms = (ulong)syncTimeouts.ConnectionLingerTime.TotalMilliseconds, - sync_connect_timeout_ms = (ulong)syncTimeouts.ConnectTimeout.TotalMilliseconds, - sync_fast_reconnect_limit = (ulong)syncTimeouts.FastReconnectLimit.TotalMilliseconds, - sync_ping_keep_alive_period_ms = (ulong)syncTimeouts.PingKeepAlivePeriod.TotalMilliseconds, - sync_pong_keep_alive_timeout_ms = (ulong)syncTimeouts.PongKeepAliveTimeout.TotalMilliseconds, + sync_timeout_options = new Native.SyncTimeoutOptions + { + sync_connection_linger_time_ms = (ulong)syncTimeouts.ConnectionLingerTime.TotalMilliseconds, + sync_connect_timeout_ms = (ulong)syncTimeouts.ConnectTimeout.TotalMilliseconds, + sync_fast_reconnect_limit = (ulong)syncTimeouts.FastReconnectLimit.TotalMilliseconds, + sync_ping_keep_alive_period_ms = (ulong)syncTimeouts.PingKeepAlivePeriod.TotalMilliseconds, + sync_pong_keep_alive_timeout_ms = (ulong)syncTimeouts.PongKeepAliveTimeout.TotalMilliseconds, + reconnect_backoff_options = new Native.ReconnectBackoffOptions + { + max_resumption_delay_interval_ms = (ulong)syncTimeouts.ReconnectBackoffOptions.MaxReconnectDelayInterval.TotalMilliseconds, + resumption_delay_interval_ms = (ulong)syncTimeouts.ReconnectBackoffOptions.ReconnectDelayInterval.TotalMilliseconds, + resumption_delay_backoff_multiplier = syncTimeouts.ReconnectBackoffOptions.ReconnectDelayBackoffMultiplier, + delay_jitter_divisor = syncTimeouts.ReconnectBackoffOptions.DelayJitterDivisor + } + }, use_cache = config.UseAppCache, }; diff --git a/Realm/Realm/Sync/ReconnectBackoffOptions.cs b/Realm/Realm/Sync/ReconnectBackoffOptions.cs new file mode 100644 index 0000000000..493ed660f3 --- /dev/null +++ b/Realm/Realm/Sync/ReconnectBackoffOptions.cs @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +using System; + +namespace Realms.Sync +{ + /// + /// Options for configuring the reconnection delay used by the sync client. + /// + /// + /// The sync client employs an exponential backoff delay strategy when reconnecting to the server. + /// In order to not spam the network interface the sync client performs an increasing wait before reconnecting. + /// The wait starts from and multiplies by + /// until it reaches . + /// + public class ReconnectBackoffOptions + { + /// + /// Gets or sets the maximum amount of time to wait before a reconnection attempt. + /// + /// + /// Defaults to 5 minutes. + /// + /// + /// The maximum amount of time to wait before a reconnection attempt. + /// + public TimeSpan MaxReconnectDelayInterval { get; set; } = TimeSpan.FromMinutes(5); + + /// + /// Gets or sets the initial amount of time to wait before a reconnection attempt. + /// + /// + /// Defaults to 1 second. + /// + /// + /// The initial amount of time to wait before a reconnection attempt. + /// + public TimeSpan ReconnectDelayInterval { get; set; } = TimeSpan.FromSeconds(1); + + /// + /// Gets or sets the multiplier to apply to the accumulated reconnection delay before a new reconection attempt. + /// + /// + /// Defaults to 2. + /// + /// + /// The delay multiplier. + /// + public int ReconnectDelayBackoffMultiplier { get; set; } = 2; + + /// + /// Gets or sets the jitter randomization factor to apply to the delay. + /// + /// + /// The reconnection delay is subtracted by a value derived from this divisor so that if a lot of clients lose connection and reconnect at the same time the server won't be overwhelmed. + ///
+ /// Defaults to 4. + ///
+ /// + /// The jitter randomization factor to apply to the delay. + /// + public int DelayJitterDivisor { get; set; } = 4; + } +} \ No newline at end of file diff --git a/Realm/Realm/Sync/SyncTimeoutOptions.cs b/Realm/Realm/Sync/SyncTimeoutOptions.cs index 0cec1717b7..f8af541bfe 100644 --- a/Realm/Realm/Sync/SyncTimeoutOptions.cs +++ b/Realm/Realm/Sync/SyncTimeoutOptions.cs @@ -104,5 +104,13 @@ public class SyncTimeoutOptions /// /// The window in which a drop in connectivity is considered transient. public TimeSpan FastReconnectLimit { get; set; } = TimeSpan.FromMinutes(1); + + /// + /// Gets or sets the options for the reconnection behavior of the sync client. + /// + /// + /// The options controlling how long the sync client waits before attempting to reconnect. + /// + public ReconnectBackoffOptions ReconnectBackoffOptions { get; set; } = new (); } } diff --git a/wrappers/src/app_cs.cpp b/wrappers/src/app_cs.cpp index ccf3f694d1..bdaf6adbe6 100644 --- a/wrappers/src/app_cs.cpp +++ b/wrappers/src/app_cs.cpp @@ -61,6 +61,28 @@ namespace realm { std::function s_string_callback; std::function s_api_keys_callback; + struct SyncTimeoutOptions { + uint64_t sync_connect_timeout_ms; + + uint64_t sync_connection_linger_time_ms; + + uint64_t sync_ping_keep_alive_period_ms; + + uint64_t sync_pong_keep_alive_timeout_ms; + + uint64_t sync_fast_reconnect_limit; + + struct { + uint64_t max_resumption_delay_interval_ms; + + uint64_t resumption_delay_interval_ms; + + int resumption_delay_backoff_multiplier; + + int delay_jitter_divisor; + } reconnect_backoff_info; + }; + struct AppConfiguration { uint16_t* app_id; @@ -82,15 +104,7 @@ namespace realm { void* managed_websocket_provider; - uint64_t sync_connect_timeout_ms; - - uint64_t sync_connection_linger_time_ms; - - uint64_t sync_ping_keep_alive_period_ms; - - uint64_t sync_pong_keep_alive_timeout_ms; - - uint64_t sync_fast_reconnect_limit; + SyncTimeoutOptions sync_timeout_options; bool use_cache; }; @@ -149,11 +163,18 @@ extern "C" { } config.base_file_path = Utf16StringAccessor(app_config.base_file_path, app_config.base_file_path_len); - config.sync_client_config.timeouts.connection_linger_time = app_config.sync_connection_linger_time_ms; - config.sync_client_config.timeouts.connect_timeout = app_config.sync_connect_timeout_ms; - config.sync_client_config.timeouts.fast_reconnect_limit = app_config.sync_fast_reconnect_limit; - config.sync_client_config.timeouts.ping_keepalive_period = app_config.sync_ping_keep_alive_period_ms; - config.sync_client_config.timeouts.pong_keepalive_timeout = app_config.sync_pong_keep_alive_timeout_ms; + + auto& timeout_options = config.sync_client_config.timeouts; + const auto& managed_timeout_options = app_config.sync_timeout_options; + timeout_options.connection_linger_time = managed_timeout_options.sync_connection_linger_time_ms; + timeout_options.connect_timeout = managed_timeout_options.sync_connect_timeout_ms; + timeout_options.fast_reconnect_limit = managed_timeout_options.sync_fast_reconnect_limit; + timeout_options.ping_keepalive_period = managed_timeout_options.sync_ping_keep_alive_period_ms; + timeout_options.pong_keepalive_timeout = managed_timeout_options.sync_pong_keep_alive_timeout_ms; + timeout_options.reconnect_backoff_info.max_resumption_delay_interval = managed_timeout_options.reconnect_backoff_info.max_resumption_delay_interval_ms; + timeout_options.reconnect_backoff_info.resumption_delay_interval = managed_timeout_options.reconnect_backoff_info.resumption_delay_interval_ms; + timeout_options.reconnect_backoff_info.resumption_delay_backoff_multiplier = managed_timeout_options.reconnect_backoff_info.resumption_delay_backoff_multiplier; + timeout_options.reconnect_backoff_info.delay_jitter_divisor = managed_timeout_options.reconnect_backoff_info.delay_jitter_divisor; if (app_config.managed_websocket_provider) { config.sync_client_config.socket_provider = make_websocket_provider(app_config.managed_websocket_provider); From 64750bd7a514ee1e22cd83aa9887583cd861f88d Mon Sep 17 00:00:00 2001 From: Yavor Georgiev Date: Thu, 22 Aug 2024 00:56:15 +0200 Subject: [PATCH 2/4] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f24bac556d..a72567c72b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## vNext (TBD) ### Enhancements -* None +* Introduce a `ReconnectBackoffOptions` property on `SyncTimeoutOptions` that allows control over the delay the sync client applies before attempting to reconnect. (PR [#3661](https://github.com/realm/realm-dotnet/pull/3661)). ### Fixed * None From f107e1cfcca472aa79629c07df45f2966a460e2a Mon Sep 17 00:00:00 2001 From: Yavor Georgiev Date: Thu, 22 Aug 2024 01:02:32 +0200 Subject: [PATCH 3/4] fix --- wrappers/src/app_cs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrappers/src/app_cs.cpp b/wrappers/src/app_cs.cpp index bdaf6adbe6..d499baaf64 100644 --- a/wrappers/src/app_cs.cpp +++ b/wrappers/src/app_cs.cpp @@ -171,8 +171,8 @@ extern "C" { timeout_options.fast_reconnect_limit = managed_timeout_options.sync_fast_reconnect_limit; timeout_options.ping_keepalive_period = managed_timeout_options.sync_ping_keep_alive_period_ms; timeout_options.pong_keepalive_timeout = managed_timeout_options.sync_pong_keep_alive_timeout_ms; - timeout_options.reconnect_backoff_info.max_resumption_delay_interval = managed_timeout_options.reconnect_backoff_info.max_resumption_delay_interval_ms; - timeout_options.reconnect_backoff_info.resumption_delay_interval = managed_timeout_options.reconnect_backoff_info.resumption_delay_interval_ms; + timeout_options.reconnect_backoff_info.max_resumption_delay_interval = std::chrono::milliseconds(managed_timeout_options.reconnect_backoff_info.max_resumption_delay_interval_ms); + timeout_options.reconnect_backoff_info.resumption_delay_interval = std::chrono::milliseconds(managed_timeout_options.reconnect_backoff_info.resumption_delay_interval_ms); timeout_options.reconnect_backoff_info.resumption_delay_backoff_multiplier = managed_timeout_options.reconnect_backoff_info.resumption_delay_backoff_multiplier; timeout_options.reconnect_backoff_info.delay_jitter_divisor = managed_timeout_options.reconnect_backoff_info.delay_jitter_divisor; From fdf46221b887dceeb2439651f65f25db29d713b5 Mon Sep 17 00:00:00 2001 From: Yavor Georgiev Date: Fri, 23 Aug 2024 14:32:26 +0200 Subject: [PATCH 4/4] fix warning --- Realm/Realm/Native/AppConfiguration.cs | 56 +++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Realm/Realm/Native/AppConfiguration.cs b/Realm/Realm/Native/AppConfiguration.cs index 45ef6880bb..65362d0ccd 100644 --- a/Realm/Realm/Native/AppConfiguration.cs +++ b/Realm/Realm/Native/AppConfiguration.cs @@ -21,34 +21,6 @@ namespace Realms.Sync.Native { - [StructLayout(LayoutKind.Sequential)] - internal struct ReconnectBackoffOptions - { - internal UInt64 max_resumption_delay_interval_ms; - - internal UInt64 resumption_delay_interval_ms; - - internal int resumption_delay_backoff_multiplier; - - internal int delay_jitter_divisor; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct SyncTimeoutOptions - { - internal UInt64 sync_connect_timeout_ms; - - internal UInt64 sync_connection_linger_time_ms; - - internal UInt64 sync_ping_keep_alive_period_ms; - - internal UInt64 sync_pong_keep_alive_timeout_ms; - - internal UInt64 sync_fast_reconnect_limit; - - internal ReconnectBackoffOptions reconnect_backoff_options; - } - [StructLayout(LayoutKind.Sequential)] internal struct AppConfiguration { @@ -116,4 +88,32 @@ internal MetadataPersistenceMode? MetadataPersistence [MarshalAs(UnmanagedType.U1)] internal bool use_cache; } + + [StructLayout(LayoutKind.Sequential)] + internal struct SyncTimeoutOptions + { + internal UInt64 sync_connect_timeout_ms; + + internal UInt64 sync_connection_linger_time_ms; + + internal UInt64 sync_ping_keep_alive_period_ms; + + internal UInt64 sync_pong_keep_alive_timeout_ms; + + internal UInt64 sync_fast_reconnect_limit; + + internal ReconnectBackoffOptions reconnect_backoff_options; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ReconnectBackoffOptions + { + internal UInt64 max_resumption_delay_interval_ms; + + internal UInt64 resumption_delay_interval_ms; + + internal int resumption_delay_backoff_multiplier; + + internal int delay_jitter_divisor; + } }