Skip to content

Commit

Permalink
Expose ReconnectBackoffOptions on SyncTimeoutOptions (#3661)
Browse files Browse the repository at this point in the history
* Expose ReconnectBackoffOptions on SyncTimeoutOptions

* changelog

* fix

* fix warning

---------

Co-authored-by: Nikola Irinchev <[email protected]>
  • Loading branch information
fealebenpae and nirinchev authored Aug 23, 2024
1 parent 0cf0dba commit b73263e
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## vNext (TBD)

### Enhancements
* 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)).
* Role and permissions changes no longer require a client reset to update the local realm. (Core 14.11.0)
* On Windows devices Device Sync will additionally look up SSL certificates in the Windows Trusted Root Certification Authorities certificate store when establishing a connection. (Core 14.11.0)
* Sync log statements now include the app services connection id in their prefix (e.g `Connection[1:<connection id>] Session[1]: log message`) to make correlating sync activity to server logs easier during troubleshooting. (Core 14.11.2)
Expand Down
24 changes: 22 additions & 2 deletions Realm/Realm/Native/AppConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ internal MetadataPersistenceMode? MetadataPersistence

internal IntPtr managed_websocket_provider;

internal SyncTimeoutOptions sync_timeout_options;

[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;
Expand All @@ -93,7 +102,18 @@ internal MetadataPersistenceMode? MetadataPersistence

internal UInt64 sync_fast_reconnect_limit;

[MarshalAs(UnmanagedType.U1)]
internal bool use_cache;
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;
}
}
20 changes: 15 additions & 5 deletions Realm/Realm/Sync/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down
80 changes: 80 additions & 0 deletions Realm/Realm/Sync/ReconnectBackoffOptions.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Options for configuring the reconnection delay used by the sync client.
/// </summary>
/// <remarks>
/// 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 <see cref="ReconnectDelayInterval"/> and multiplies by <see cref="ReconnectDelayBackoffMultiplier"/>
/// until it reaches <see cref="MaxReconnectDelayInterval"/>.
/// </remarks>
public class ReconnectBackoffOptions
{
/// <summary>
/// Gets or sets the maximum amount of time to wait before a reconnection attempt.
/// </summary>
/// <remarks>
/// Defaults to 5 minutes.
/// </remarks>
/// <value>
/// The maximum amount of time to wait before a reconnection attempt.
/// </value>
public TimeSpan MaxReconnectDelayInterval { get; set; } = TimeSpan.FromMinutes(5);

/// <summary>
/// Gets or sets the initial amount of time to wait before a reconnection attempt.
/// </summary>
/// <remarks>
/// Defaults to 1 second.
/// </remarks>
/// <value>
/// The initial amount of time to wait before a reconnection attempt.
/// </value>
public TimeSpan ReconnectDelayInterval { get; set; } = TimeSpan.FromSeconds(1);

/// <summary>
/// Gets or sets the multiplier to apply to the accumulated reconnection delay before a new reconection attempt.
/// </summary>
/// <remarks>
/// Defaults to 2.
/// </remarks>
/// <value>
/// The delay multiplier.
/// </value>
public int ReconnectDelayBackoffMultiplier { get; set; } = 2;

/// <summary>
/// Gets or sets the jitter randomization factor to apply to the delay.
/// </summary>
/// <remarks>
/// 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.
/// <br />
/// Defaults to 4.
/// </remarks>
/// <value>
/// The jitter randomization factor to apply to the delay.
/// </value>
public int DelayJitterDivisor { get; set; } = 4;
}
}
8 changes: 8 additions & 0 deletions Realm/Realm/Sync/SyncTimeoutOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,13 @@ public class SyncTimeoutOptions
/// </remarks>
/// <value>The window in which a drop in connectivity is considered transient.</value>
public TimeSpan FastReconnectLimit { get; set; } = TimeSpan.FromMinutes(1);

/// <summary>
/// Gets or sets the options for the reconnection behavior of the sync client.
/// </summary>
/// <value>
/// The options controlling how long the sync client waits before attempting to reconnect.
/// </value>
public ReconnectBackoffOptions ReconnectBackoffOptions { get; set; } = new ();
}
}
49 changes: 35 additions & 14 deletions wrappers/src/app_cs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,28 @@ namespace realm {
std::function<StringCallbackT> s_string_callback;
std::function<ApiKeysCallbackT> 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;
Expand All @@ -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;
};
Expand Down Expand Up @@ -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 = 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;

if (app_config.managed_websocket_provider) {
config.sync_client_config.socket_provider = make_websocket_provider(app_config.managed_websocket_provider);
Expand Down

0 comments on commit b73263e

Please sign in to comment.