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);