From 2cd00220d771e6e739606a438a0a985b6a6ccf10 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:23:42 +0100 Subject: [PATCH 01/24] Added stub for progress notifications --- Realm/Realm/Handles/SessionHandle.cs | 6 +++--- .../ProgressNotificationToken.cs | 4 ++-- .../Sync/ProgressNotifications/SyncProgress.cs | 15 +++++++++++++-- Realm/Realm/Sync/Session.cs | 3 +++ wrappers/realm-core | 2 +- wrappers/src/async_open_task_cs.cpp | 4 ++-- wrappers/src/sync_session_cs.cpp | 4 ++-- wrappers/src/sync_session_cs.hpp | 2 +- 8 files changed, 27 insertions(+), 13 deletions(-) diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs index 9ad67eff89..458c208fc4 100644 --- a/Realm/Realm/Handles/SessionHandle.cs +++ b/Realm/Realm/Handles/SessionHandle.cs @@ -42,7 +42,7 @@ public delegate void SessionErrorCallback(IntPtr session_handle_ptr, IntPtr managed_sync_config_handle); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void SessionProgressCallback(IntPtr progress_token_ptr, ulong transferred_bytes, ulong transferable_bytes); + public delegate void SessionProgressCallback(IntPtr progress_token_ptr, ulong transferred_bytes, ulong transferable_bytes, double progressEstimate); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void SessionWaitCallback(IntPtr task_completion_source, int error_code, PrimitiveValue message); @@ -405,10 +405,10 @@ private static IntPtr NotifyAfterClientReset(IntPtr beforeFrozen, IntPtr after, } [MonoPInvokeCallback(typeof(NativeMethods.SessionProgressCallback))] - private static void HandleSessionProgress(IntPtr tokenPtr, ulong transferredBytes, ulong transferableBytes) + private static void HandleSessionProgress(IntPtr tokenPtr, ulong transferredBytes, ulong transferableBytes, double progressEstimate) { var token = (ProgressNotificationToken?)GCHandle.FromIntPtr(tokenPtr).Target; - token?.Notify(transferredBytes, transferableBytes); + token?.Notify(transferredBytes, transferableBytes, progressEstimate); } [MonoPInvokeCallback(typeof(NativeMethods.SessionWaitCallback))] diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs index 061548b3f7..922bdfc2c7 100644 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs +++ b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs @@ -48,13 +48,13 @@ public ProgressNotificationToken(Action observer, Func { try { - _observer(new SyncProgress(transferredBytes, transferableBytes)); + _observer(new SyncProgress(transferredBytes, transferableBytes, progressEstimate)); } catch (Exception ex) { diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index 1d9ef85f7d..9c4f4b4c41 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -16,6 +16,8 @@ // //////////////////////////////////////////////////////////////////////////// +using System; + namespace Realms.Sync { /// @@ -27,6 +29,7 @@ public struct SyncProgress /// Gets the number of bytes that have been transferred since subscribing for progress notifications. /// /// The number of transferred bytes. + [Obsolete("Use ProgressEstimate instead.")] public ulong TransferredBytes { get; } /// @@ -36,14 +39,22 @@ public struct SyncProgress /// successfully transferred. /// /// The number of transferable bytes. + [Obsolete("Use ProgressEstimate instead.")] public ulong TransferableBytes { get; } - internal SyncProgress(ulong transferred, ulong transferable) + /// + /// Gets the percentage estimate of the current progress, expressed as a float between 0.0 and 1.0. + /// + /// A percentage estimate of the progress. + public double ProgressEstimate { get; } + + internal SyncProgress(ulong transferred, ulong transferable, double progressEstimate) { TransferredBytes = transferred; TransferableBytes = transferable; + ProgressEstimate = progressEstimate; } - internal bool IsComplete => TransferableBytes == TransferredBytes; + internal readonly bool IsComplete => ProgressEstimate >= 1.0; } } diff --git a/Realm/Realm/Sync/Session.cs b/Realm/Realm/Sync/Session.cs index a07eb8496d..1e0e97bfdd 100644 --- a/Realm/Realm/Sync/Session.cs +++ b/Realm/Realm/Sync/Session.cs @@ -155,6 +155,9 @@ public event PropertyChangedEventHandler? PropertyChanged /// public IObservable GetProgressObservable(ProgressDirection direction, ProgressMode mode) => new SyncProgressObservable(Handle, direction, mode); + // TODO I think the example in the documentation of GetProgressObservable never worked, we can't use a lambda instead of an IObserver + // We can do it only if we install the System.Reactive package. The docs are also wrong (the package is installed in their repo) + /// /// Waits for the to finish all pending uploads. /// diff --git a/wrappers/realm-core b/wrappers/realm-core index 1527dc439a..88756b3a8c 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 1527dc439a9609beae88d39b4101b741e7a6a741 +Subproject commit 88756b3a8c0e3ea628415d94f92c8f538ba09349 diff --git a/wrappers/src/async_open_task_cs.cpp b/wrappers/src/async_open_task_cs.cpp index d0f90d9a1f..0111b506ff 100644 --- a/wrappers/src/async_open_task_cs.cpp +++ b/wrappers/src/async_open_task_cs.cpp @@ -43,8 +43,8 @@ REALM_EXPORT void realm_asyncopentask_cancel(SharedAsyncOpenTask& task, NativeEx REALM_EXPORT uint64_t realm_asyncopentask_register_progress_notifier(const SharedAsyncOpenTask& task, void* managed_state, NativeException::Marshallable& ex) { return handle_errors(ex, [&] { - return task->register_download_progress_notifier([managed_state](uint64_t transferred, uint64_t transferable) { - s_progress_callback(managed_state, transferred, transferable); + return task->register_download_progress_notifier([managed_state](uint64_t transferred, uint64_t transferable, double progress_estimate) { + s_progress_callback(managed_state, transferred, transferable, progress_estimate); }); }); } diff --git a/wrappers/src/sync_session_cs.cpp b/wrappers/src/sync_session_cs.cpp index c4f915a0c5..1b942043e8 100644 --- a/wrappers/src/sync_session_cs.cpp +++ b/wrappers/src/sync_session_cs.cpp @@ -119,8 +119,8 @@ REALM_EXPORT uint64_t realm_syncsession_register_progress_notifier(const SharedS ? SyncSession::ProgressDirection::upload : SyncSession::ProgressDirection::download; - return session->register_progress_notifier([managed_state](uint64_t transferred, uint64_t transferable) { - s_progress_callback(managed_state, transferred, transferable); + return session->register_progress_notifier([managed_state](uint64_t transferred, uint64_t transferable, double progress_estimate) { + s_progress_callback(managed_state, transferred, transferable, progress_estimate); }, notifier_direction, is_streaming); }); } diff --git a/wrappers/src/sync_session_cs.hpp b/wrappers/src/sync_session_cs.hpp index 8a13dd063d..2d2e4ab2ce 100644 --- a/wrappers/src/sync_session_cs.hpp +++ b/wrappers/src/sync_session_cs.hpp @@ -25,7 +25,7 @@ namespace realm::binding { using SharedSyncSession = std::shared_ptr; using SessionErrorCallbackT = void(SharedSyncSession* session, realm_sync_error error, void* managed_sync_config); -using ProgressCallbackT = void(void* state, uint64_t transferred_bytes, uint64_t transferrable_bytes); +using ProgressCallbackT = void(void* state, uint64_t transferred_bytes, uint64_t transferrable_bytes, double progress_estimate); using NotifyBeforeClientResetCallbackT = void*(SharedRealm& before_frozen, void* managed_sync_config); using NotifyAfterClientResetCallbackT = void*(SharedRealm& before_frozen, SharedRealm& after, void* managed_sync_config, bool did_recover); From 3a98f21f8aabfcec8f6b21fee1390bdb61b820f8 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 17 Nov 2023 11:48:27 +0100 Subject: [PATCH 02/24] Added test for flexible sync --- Tests/Realm.Tests/Sync/SessionTests.cs | 66 +++++++++++++++++++++----- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 3986a6152d..d491689ce6 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -46,6 +46,12 @@ namespace Realms.Tests.Sync [TestFixture, Preserve(AllMembers = true)] public class SessionTests : SyncTestBase { + public static readonly string[] AppTypes = new[] + { + AppConfigType.Default, + AppConfigType.FlexibleSync + }; + public static readonly object[] AllClientResetHandlers = new object[] { typeof(DiscardUnsyncedChangesHandler), @@ -53,6 +59,12 @@ public class SessionTests : SyncTestBase typeof(RecoverOrDiscardUnsyncedChangesHandler), }; + public static readonly ProgressMode[] ProgressModeTypes = new ProgressMode[] + { + ProgressMode.ForCurrentlyOutstandingWork, + ProgressMode.ReportIndefinitely, + }; + [Preserve] static SessionTests() { @@ -79,12 +91,6 @@ static SessionTests() }; } - public static readonly string[] AppTypes = new[] - { - AppConfigType.Default, - AppConfigType.FlexibleSync - }; - [Test] public void Realm_SyncSession_WhenSyncedRealm() { @@ -750,16 +756,33 @@ public void Session_OnSessionError() }); } - [TestCase(ProgressMode.ForCurrentlyOutstandingWork)] - [TestCase(ProgressMode.ReportIndefinitely)] - public void SessionIntegrationTest_ProgressObservable(ProgressMode mode) +#pragma warning disable CS0618 // Type or member is obsolete + [Test] + public void SessionIntegrationTest_ProgressObservable( + [ValueSource(nameof(AppTypes))] string appType, + [ValueSource(nameof(ProgressModeTypes))] ProgressMode mode) { const int objectSize = 1_000_000; const int objectsToRecord = 2; SyncTestHelpers.RunBaasTestAsync(async () => { - var config = await GetIntegrationConfigAsync(Guid.NewGuid().ToString()); - using var realm = GetRealm(config); + Realm realm; + if (appType == AppConfigType.Default) + { + var config = await GetIntegrationConfigAsync(Guid.NewGuid().ToString()); + realm = GetRealm(config); + } + else + { + var config = await GetFLXIntegrationConfigAsync(); + config.PopulateInitialSubscriptions = (r) => + { + var query = r.All(); + + r.Subscriptions.Add(r.All()); + }; + realm = await GetRealmAsync(config); + } var completionTcs = new TaskCompletionSource(); var callbacksInvoked = 0; @@ -796,6 +819,16 @@ public void SessionIntegrationTest_ProgressObservable(ProgressMode mode) throw new Exception($"Expected: {p.TransferableBytes} to be in the ({objectSize}, {(objectsToRecord + 1) * objectSize}) range."); } } + + if (p.TransferredBytes == 0 && p.ProgressEstimate != 0.0) + { + throw new Exception($"Expected progress estimate to be 0.0 when TransferredBytes == 0, but was {p.ProgressEstimate}"); + } + + if (p.TransferredBytes > 0 && (p.ProgressEstimate <= 0.0 || p.ProgressEstimate > 1.0)) + { + throw new Exception($"Expected progress estimate to be between 0.0 and 1.0 TransferredBytes >= 0, but was {p.ProgressEstimate}"); + } } catch (Exception e) { @@ -804,6 +837,16 @@ public void SessionIntegrationTest_ProgressObservable(ProgressMode mode) if (p.TransferredBytes >= p.TransferableBytes) { + if (p.ProgressEstimate != 1.0) + { + throw new Exception($"Expected progress estimate to be 1.0 when TransferredBytes >= TransferableBytes, but was {p.ProgressEstimate}"); + } + + if (p.IsComplete is false) + { + throw new Exception($"Expected IsComplete to be true when TransferredBytes >= TransferableBytes, but was false"); + } + completionTcs.TrySetResult(p.TransferredBytes); } }); @@ -831,6 +874,7 @@ public void SessionIntegrationTest_ProgressObservable(ProgressMode mode) Assert.That(callbacksInvoked, Is.GreaterThan(1)); }, timeout: 120_000); } +#pragma warning restore CS0618 // Type or member is obsolete [Test] public void Session_Stop_StopsSession() From 02417efbef63af40a4cc66a512c0b149d4f15bd4 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 13:20:44 +0100 Subject: [PATCH 03/24] Using correct core branch --- wrappers/realm-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/realm-core b/wrappers/realm-core index 88756b3a8c..9a556398d1 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 88756b3a8c0e3ea628415d94f92c8f538ba09349 +Subproject commit 9a556398d1699788aa46995d263cc7bd9e2e044a From e7a3f7b4a3036197f05876db6a3c588870ca5bb9 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:28:37 +0100 Subject: [PATCH 04/24] Fixed test --- Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs | 2 ++ Realm/Realm/Sync/Session.cs | 3 --- Tests/Realm.Tests/Sync/SessionTests.cs | 4 +--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index 9c4f4b4c41..5839bbac5e 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -50,8 +50,10 @@ public struct SyncProgress internal SyncProgress(ulong transferred, ulong transferable, double progressEstimate) { +#pragma warning disable CS0618 // Type or member is obsolete TransferredBytes = transferred; TransferableBytes = transferable; +#pragma warning restore CS0618 // Type or member is obsolete ProgressEstimate = progressEstimate; } diff --git a/Realm/Realm/Sync/Session.cs b/Realm/Realm/Sync/Session.cs index 1e0e97bfdd..a07eb8496d 100644 --- a/Realm/Realm/Sync/Session.cs +++ b/Realm/Realm/Sync/Session.cs @@ -155,9 +155,6 @@ public event PropertyChangedEventHandler? PropertyChanged /// public IObservable GetProgressObservable(ProgressDirection direction, ProgressMode mode) => new SyncProgressObservable(Handle, direction, mode); - // TODO I think the example in the documentation of GetProgressObservable never worked, we can't use a lambda instead of an IObserver - // We can do it only if we install the System.Reactive package. The docs are also wrong (the package is installed in their repo) - /// /// Waits for the to finish all pending uploads. /// diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index efc77dac05..1467860684 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -777,8 +777,6 @@ public void SessionIntegrationTest_ProgressObservable( var config = await GetFLXIntegrationConfigAsync(); config.PopulateInitialSubscriptions = (r) => { - var query = r.All(); - r.Subscriptions.Add(r.All()); }; realm = await GetRealmAsync(config); @@ -835,7 +833,7 @@ public void SessionIntegrationTest_ProgressObservable( completionTcs.TrySetException(e); } - if (p.TransferredBytes >= p.TransferableBytes) + if (p.TransferredBytes >= p.TransferableBytes && p.TransferredBytes > objectSize) { if (p.ProgressEstimate != 1.0) { From 728f14807262713b403fad2d1da73237a7c9ff6b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:41:45 +0100 Subject: [PATCH 05/24] Small fix --- Tests/Realm.Tests/Sync/SessionTests.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 1467860684..603056dc2d 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -805,8 +805,12 @@ public void SessionIntegrationTest_ProgressObservable( if (p.TransferredBytes > p.TransferableBytes) { - // TODO https://github.com/realm/realm-dotnet/issues/2360: this seems to be a regression in Sync. - // throw new Exception($"Expected: {p.TransferredBytes} <= {p.TransferableBytes}"); + throw new Exception($"Expected: {p.TransferredBytes} <= {p.TransferableBytes}"); + } + + if (p.ProgressEstimate < 0.0 || p.ProgressEstimate > 1.0) + { + throw new Exception($"Expected progress estimate to be between 0.0 and 1.0, but was {p.ProgressEstimate}"); } if (mode == ProgressMode.ForCurrentlyOutstandingWork) @@ -825,7 +829,7 @@ public void SessionIntegrationTest_ProgressObservable( if (p.TransferredBytes > 0 && (p.ProgressEstimate <= 0.0 || p.ProgressEstimate > 1.0)) { - throw new Exception($"Expected progress estimate to be between 0.0 and 1.0 TransferredBytes >= 0, but was {p.ProgressEstimate}"); + throw new Exception($"Expected progress estimate to be between 0.0 and 1.0 when TransferredBytes >= 0, but was {p.ProgressEstimate}"); } } catch (Exception e) From febeb00511704e4c5e7ba1cba2819ee972a8e5ca Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:53:29 +0100 Subject: [PATCH 06/24] Small fixes --- CHANGELOG.md | 2 ++ Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs | 6 +++--- Realm/Realm/Sync/Session.cs | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b9d206185..9b9e1b3ea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ * Automatic client reset recovery now does a better job of recovering changes when changesets were downloaded from the server after the unuploaded local changes were committed. If the local Realm happened to be fully up to date with the server prior to the client reset, automatic recovery should now always produce exactly the same state as if no client reset was involved. (Core 13.24.1) * Exceptions thrown during bootstrap application will now be surfaced to the user rather than terminating the program with an unhandled exception. (Core 13.25.0) * Allow the using `>`, `>=`, `<`, `<=` operators in `Realm.Filter()` queries for string constants. This is a case sensitive lexicographical comparison. Improved performance of RQL (`.Filter()`) queries on a non-linked string property using: >, >=, <, <=, operators and fixed behaviour that a null string should be evaluated as less than everything, previously nulls were not matched. (Core 13.26.0-14-gdf25f) +* Added `ProgressEstimate` to `SyncProgress`. `ProgressEstimate` is a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. At the same time `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been marked as obsolete in favour of `SyncProgress.ProgressEstimate`. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) +* `Session.GetProgressObservable` can now be used with Flexible Sync. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) ### Fixed * Automatic client reset recovery would duplicate insertions in a list when recovering a write which made an unrecoverable change to a list (i.e. modifying or deleting a pre-existing entry), followed by a subscription change, followed by a write which added an entry to the list. (Core 13.24.0) diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index 5839bbac5e..acb1eeec01 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -23,13 +23,13 @@ namespace Realms.Sync /// /// A struct containing information about the progress state at a given instant. /// - public struct SyncProgress + public readonly struct SyncProgress { /// /// Gets the number of bytes that have been transferred since subscribing for progress notifications. /// /// The number of transferred bytes. - [Obsolete("Use ProgressEstimate instead.")] + [Obsolete("Not accurate, use ProgressEstimate instead.")] public ulong TransferredBytes { get; } /// @@ -39,7 +39,7 @@ public struct SyncProgress /// successfully transferred. /// /// The number of transferable bytes. - [Obsolete("Use ProgressEstimate instead.")] + [Obsolete("Not accurate, use ProgressEstimate instead.")] public ulong TransferableBytes { get; } /// diff --git a/Realm/Realm/Sync/Session.cs b/Realm/Realm/Sync/Session.cs index a07eb8496d..51d9e2c96e 100644 --- a/Realm/Realm/Sync/Session.cs +++ b/Realm/Realm/Sync/Session.cs @@ -136,8 +136,7 @@ public event PropertyChangedEventHandler? PropertyChanged /// var observable = session.GetProgressObservable(ProgressDirection.Upload, ProgressMode.ReportIndefinitely); /// notificationToken = observable.Subscribe(progress => /// { - /// // Update relevant properties by accessing - /// // progress.TransferredBytes and progress.TransferableBytes + /// // Update relevant properties by accessing progress.ProgressEstimate /// }); /// } /// From e14c6bc5955fb903334ae85f9bf24cab9fa888df Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:54:51 +0100 Subject: [PATCH 07/24] Corrected changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9e1b3ea4..df67c9a414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ * Automatic client reset recovery now does a better job of recovering changes when changesets were downloaded from the server after the unuploaded local changes were committed. If the local Realm happened to be fully up to date with the server prior to the client reset, automatic recovery should now always produce exactly the same state as if no client reset was involved. (Core 13.24.1) * Exceptions thrown during bootstrap application will now be surfaced to the user rather than terminating the program with an unhandled exception. (Core 13.25.0) * Allow the using `>`, `>=`, `<`, `<=` operators in `Realm.Filter()` queries for string constants. This is a case sensitive lexicographical comparison. Improved performance of RQL (`.Filter()`) queries on a non-linked string property using: >, >=, <, <=, operators and fixed behaviour that a null string should be evaluated as less than everything, previously nulls were not matched. (Core 13.26.0-14-gdf25f) -* Added `ProgressEstimate` to `SyncProgress`. `ProgressEstimate` is a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. At the same time `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been marked as obsolete in favour of `SyncProgress.ProgressEstimate`. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) +* Added `SyncProgress.ProgressEstimate`, a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. At the same time `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been marked as obsolete in favour of `SyncProgress.ProgressEstimate`. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) * `Session.GetProgressObservable` can now be used with Flexible Sync. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) ### Fixed From d6c59287bb786cf4449c03f9ccff72f0bbda8669 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:56:03 +0100 Subject: [PATCH 08/24] Removed extra space --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df67c9a414..27dd9cbbea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ * Automatic client reset recovery now does a better job of recovering changes when changesets were downloaded from the server after the unuploaded local changes were committed. If the local Realm happened to be fully up to date with the server prior to the client reset, automatic recovery should now always produce exactly the same state as if no client reset was involved. (Core 13.24.1) * Exceptions thrown during bootstrap application will now be surfaced to the user rather than terminating the program with an unhandled exception. (Core 13.25.0) * Allow the using `>`, `>=`, `<`, `<=` operators in `Realm.Filter()` queries for string constants. This is a case sensitive lexicographical comparison. Improved performance of RQL (`.Filter()`) queries on a non-linked string property using: >, >=, <, <=, operators and fixed behaviour that a null string should be evaluated as less than everything, previously nulls were not matched. (Core 13.26.0-14-gdf25f) -* Added `SyncProgress.ProgressEstimate`, a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. At the same time `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been marked as obsolete in favour of `SyncProgress.ProgressEstimate`. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) +* Added `SyncProgress.ProgressEstimate`, a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. At the same time `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been marked as obsolete in favour of `SyncProgress.ProgressEstimate`. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) * `Session.GetProgressObservable` can now be used with Flexible Sync. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) ### Fixed From f65e9e081f31091856aa0be608d3263d708e5154 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:31:55 +0100 Subject: [PATCH 09/24] Removed obsolete --- CHANGELOG.md | 2 +- Realm/Realm/Handles/SessionHandle.cs | 6 +-- .../ProgressNotificationToken.cs | 4 +- .../ProgressNotifications/SyncProgress.cs | 23 +------- Tests/Realm.Tests/Sync/SessionTests.cs | 54 +++---------------- .../Sync/SynchronizedInstanceTests.cs | 12 +---- wrappers/src/async_open_task_cs.cpp | 2 +- wrappers/src/sync_session_cs.hpp | 2 +- 8 files changed, 17 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27dd9cbbea..a27e376d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ * Automatic client reset recovery now does a better job of recovering changes when changesets were downloaded from the server after the unuploaded local changes were committed. If the local Realm happened to be fully up to date with the server prior to the client reset, automatic recovery should now always produce exactly the same state as if no client reset was involved. (Core 13.24.1) * Exceptions thrown during bootstrap application will now be surfaced to the user rather than terminating the program with an unhandled exception. (Core 13.25.0) * Allow the using `>`, `>=`, `<`, `<=` operators in `Realm.Filter()` queries for string constants. This is a case sensitive lexicographical comparison. Improved performance of RQL (`.Filter()`) queries on a non-linked string property using: >, >=, <, <=, operators and fixed behaviour that a null string should be evaluated as less than everything, previously nulls were not matched. (Core 13.26.0-14-gdf25f) -* Added `SyncProgress.ProgressEstimate`, a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. At the same time `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been marked as obsolete in favour of `SyncProgress.ProgressEstimate`. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) +* `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been removed in favour of `SyncProgress.ProgressEstimate`, a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) * `Session.GetProgressObservable` can now be used with Flexible Sync. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) ### Fixed diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs index 458c208fc4..6068ce6102 100644 --- a/Realm/Realm/Handles/SessionHandle.cs +++ b/Realm/Realm/Handles/SessionHandle.cs @@ -42,7 +42,7 @@ public delegate void SessionErrorCallback(IntPtr session_handle_ptr, IntPtr managed_sync_config_handle); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void SessionProgressCallback(IntPtr progress_token_ptr, ulong transferred_bytes, ulong transferable_bytes, double progressEstimate); + public delegate void SessionProgressCallback(IntPtr progress_token_ptr, double progressEstimate); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void SessionWaitCallback(IntPtr task_completion_source, int error_code, PrimitiveValue message); @@ -405,10 +405,10 @@ private static IntPtr NotifyAfterClientReset(IntPtr beforeFrozen, IntPtr after, } [MonoPInvokeCallback(typeof(NativeMethods.SessionProgressCallback))] - private static void HandleSessionProgress(IntPtr tokenPtr, ulong transferredBytes, ulong transferableBytes, double progressEstimate) + private static void HandleSessionProgress(IntPtr tokenPtr, double progressEstimate) { var token = (ProgressNotificationToken?)GCHandle.FromIntPtr(tokenPtr).Target; - token?.Notify(transferredBytes, transferableBytes, progressEstimate); + token?.Notify(progressEstimate); } [MonoPInvokeCallback(typeof(NativeMethods.SessionWaitCallback))] diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs index 922bdfc2c7..57674fc30a 100644 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs +++ b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs @@ -48,13 +48,13 @@ public ProgressNotificationToken(Action observer, Func { try { - _observer(new SyncProgress(transferredBytes, transferableBytes, progressEstimate)); + _observer(new SyncProgress(progressEstimate)); } catch (Exception ex) { diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index acb1eeec01..460acb1f11 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -25,35 +25,14 @@ namespace Realms.Sync /// public readonly struct SyncProgress { - /// - /// Gets the number of bytes that have been transferred since subscribing for progress notifications. - /// - /// The number of transferred bytes. - [Obsolete("Not accurate, use ProgressEstimate instead.")] - public ulong TransferredBytes { get; } - - /// - /// Gets the total number of bytes that have to be transferred since subscribing for progress notifications. - /// The difference between that number and gives you the number of bytes not yet - /// transferred. If the difference is 0, then all changes at the instant the callback fires have been - /// successfully transferred. - /// - /// The number of transferable bytes. - [Obsolete("Not accurate, use ProgressEstimate instead.")] - public ulong TransferableBytes { get; } - /// /// Gets the percentage estimate of the current progress, expressed as a float between 0.0 and 1.0. /// /// A percentage estimate of the progress. public double ProgressEstimate { get; } - internal SyncProgress(ulong transferred, ulong transferable, double progressEstimate) + internal SyncProgress(double progressEstimate) { -#pragma warning disable CS0618 // Type or member is obsolete - TransferredBytes = transferred; - TransferableBytes = transferable; -#pragma warning restore CS0618 // Type or member is obsolete ProgressEstimate = progressEstimate; } diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 603056dc2d..9f5fbaef0e 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -782,7 +782,7 @@ public void SessionIntegrationTest_ProgressObservable( realm = await GetRealmAsync(config); } - var completionTcs = new TaskCompletionSource(); + var completionTcs = new TaskCompletionSource(); var callbacksInvoked = 0; var session = GetSession(realm); @@ -803,53 +803,24 @@ public void SessionIntegrationTest_ProgressObservable( { callbacksInvoked++; - if (p.TransferredBytes > p.TransferableBytes) - { - throw new Exception($"Expected: {p.TransferredBytes} <= {p.TransferableBytes}"); - } - if (p.ProgressEstimate < 0.0 || p.ProgressEstimate > 1.0) { throw new Exception($"Expected progress estimate to be between 0.0 and 1.0, but was {p.ProgressEstimate}"); } - - if (mode == ProgressMode.ForCurrentlyOutstandingWork) - { - if (p.TransferableBytes <= objectSize || - p.TransferableBytes >= (objectsToRecord + 2) * objectSize) - { - throw new Exception($"Expected: {p.TransferableBytes} to be in the ({objectSize}, {(objectsToRecord + 1) * objectSize}) range."); - } - } - - if (p.TransferredBytes == 0 && p.ProgressEstimate != 0.0) - { - throw new Exception($"Expected progress estimate to be 0.0 when TransferredBytes == 0, but was {p.ProgressEstimate}"); - } - - if (p.TransferredBytes > 0 && (p.ProgressEstimate <= 0.0 || p.ProgressEstimate > 1.0)) - { - throw new Exception($"Expected progress estimate to be between 0.0 and 1.0 when TransferredBytes >= 0, but was {p.ProgressEstimate}"); - } } catch (Exception e) { completionTcs.TrySetException(e); } - if (p.TransferredBytes >= p.TransferableBytes && p.TransferredBytes > objectSize) + if (p.IsComplete) { if (p.ProgressEstimate != 1.0) { - throw new Exception($"Expected progress estimate to be 1.0 when TransferredBytes >= TransferableBytes, but was {p.ProgressEstimate}"); - } - - if (p.IsComplete is false) - { - throw new Exception($"Expected IsComplete to be true when TransferredBytes >= TransferableBytes, but was false"); + throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); } - completionTcs.TrySetResult(p.TransferredBytes); + completionTcs.TrySetResult(); } }); @@ -858,22 +829,9 @@ public void SessionIntegrationTest_ProgressObservable( realm.Add(new HugeSyncObject(objectSize)); }); - var totalTransferred = await completionTcs.Task; - - if (mode == ProgressMode.ForCurrentlyOutstandingWork) - { - Assert.That(totalTransferred, Is.GreaterThanOrEqualTo(objectSize)); - - // We add ObjectsToRecord + 1 items, but the last item is added after subscribing - // so in the fixed mode, we should not get updates for it. - Assert.That(totalTransferred, Is.LessThan((objectsToRecord + 5) * objectSize)); - } - else - { - Assert.That(totalTransferred, Is.GreaterThanOrEqualTo((objectsToRecord + 1) * objectSize)); - } + await completionTcs.Task; - Assert.That(callbacksInvoked, Is.GreaterThan(1)); + Assert.That(callbacksInvoked, Is.GreaterThanOrEqualTo(1)); }, timeout: 120_000); } #pragma warning restore CS0618 // Type or member is obsolete diff --git a/Tests/Realm.Tests/Sync/SynchronizedInstanceTests.cs b/Tests/Realm.Tests/Sync/SynchronizedInstanceTests.cs index 2b55ea6b3a..7daf37f9b9 100644 --- a/Tests/Realm.Tests/Sync/SynchronizedInstanceTests.cs +++ b/Tests/Realm.Tests/Sync/SynchronizedInstanceTests.cs @@ -129,11 +129,7 @@ public void GetInstanceAsync_ReportsProgress() using var realm = await GetRealmAsync(config); Assert.That(realm.All().Count(), Is.EqualTo(NumberOfObjects)); Assert.That(callbacksInvoked, Is.GreaterThan(0)); - - // We can't validate exact values because there's a reasonable chance that - // the last notification won't be invoked if the Realm is downloaded first. - Assert.That(lastProgress.TransferredBytes, Is.GreaterThan(OneMegabyte)); - Assert.That(lastProgress.TransferableBytes, Is.GreaterThan(OneMegabyte)); + Assert.That(lastProgress.ProgressEstimate, Is.GreaterThan(0.0)); }, 60000); } @@ -163,11 +159,7 @@ public void GetInstanceAsync_WithOnProgress_DoesntThrowWhenOnProgressIsSetToNull Assert.That(realm.All().Count(), Is.EqualTo(NumberOfObjects)); Assert.That(callbacksInvoked, Is.GreaterThan(0)); - - // We can't validate exact values because there's a reasonable chance that - // the last notification won't be invoked if the Realm is downloaded first. - Assert.That(lastProgress.TransferredBytes, Is.GreaterThan(OneMegabyte)); - Assert.That(lastProgress.TransferableBytes, Is.GreaterThan(OneMegabyte)); + Assert.That(lastProgress.ProgressEstimate, Is.GreaterThan(0.0)); }, 60000); } diff --git a/wrappers/src/async_open_task_cs.cpp b/wrappers/src/async_open_task_cs.cpp index 0111b506ff..ad10ef68ca 100644 --- a/wrappers/src/async_open_task_cs.cpp +++ b/wrappers/src/async_open_task_cs.cpp @@ -44,7 +44,7 @@ REALM_EXPORT uint64_t realm_asyncopentask_register_progress_notifier(const Share { return handle_errors(ex, [&] { return task->register_download_progress_notifier([managed_state](uint64_t transferred, uint64_t transferable, double progress_estimate) { - s_progress_callback(managed_state, transferred, transferable, progress_estimate); + s_progress_callback(managed_state, progress_estimate); }); }); } diff --git a/wrappers/src/sync_session_cs.hpp b/wrappers/src/sync_session_cs.hpp index 2d2e4ab2ce..dacc7b336c 100644 --- a/wrappers/src/sync_session_cs.hpp +++ b/wrappers/src/sync_session_cs.hpp @@ -25,7 +25,7 @@ namespace realm::binding { using SharedSyncSession = std::shared_ptr; using SessionErrorCallbackT = void(SharedSyncSession* session, realm_sync_error error, void* managed_sync_config); -using ProgressCallbackT = void(void* state, uint64_t transferred_bytes, uint64_t transferrable_bytes, double progress_estimate); +using ProgressCallbackT = void(void* state, double progress_estimate); using NotifyBeforeClientResetCallbackT = void*(SharedRealm& before_frozen, void* managed_sync_config); using NotifyAfterClientResetCallbackT = void*(SharedRealm& before_frozen, SharedRealm& after, void* managed_sync_config, bool did_recover); From fce8d1ee8fc13d9f8e7dcc548c125c5d0538f664 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:42:41 +0100 Subject: [PATCH 10/24] Last fix --- wrappers/src/sync_session_cs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/src/sync_session_cs.cpp b/wrappers/src/sync_session_cs.cpp index 1b942043e8..89d25a9cb7 100644 --- a/wrappers/src/sync_session_cs.cpp +++ b/wrappers/src/sync_session_cs.cpp @@ -120,7 +120,7 @@ REALM_EXPORT uint64_t realm_syncsession_register_progress_notifier(const SharedS : SyncSession::ProgressDirection::download; return session->register_progress_notifier([managed_state](uint64_t transferred, uint64_t transferable, double progress_estimate) { - s_progress_callback(managed_state, transferred, transferable, progress_estimate); + s_progress_callback(managed_state, progress_estimate); }, notifier_direction, is_streaming); }); } From 7187f58e079299d43d27a71f43e60db42622477f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:47:23 +0100 Subject: [PATCH 11/24] Fixed changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f6ab536c4..72b11f71a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Breaking Changes * Added automatic serialization and deserialization of Realm classes when using methods on `MongoClient.Collection`, without the need to annotate classes with `MongoDB.Bson`attributes. This feature required to change the default serialization for various types (including `DateTimeOffset`). If you prefer to use the previous serialization, you need to call `Realm.SetLegacySerialization` before any kind of serialization is done, otherwise it may not work as epxected. [#3459](https://github.com/realm/realm-dotnet/pull/3459) +* `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been removed in favour of `SyncProgress.ProgressEstimate`, a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) ### Enhancements * Added the `MongoClient.GetCollection` method to get a collection of documents from MongoDB that can be deserialized in Realm objects. This methods works the same as `MongoClient.GetDatabase(dbName).GetCollection(collectionName)`, but the database name and collection name are automatically derived from the Realm object class. [#3414](https://github.com/realm/realm-dotnet/pull/3414) @@ -21,7 +22,6 @@ * Automatic client reset recovery now does a better job of recovering changes when changesets were downloaded from the server after the unuploaded local changes were committed. If the local Realm happened to be fully up to date with the server prior to the client reset, automatic recovery should now always produce exactly the same state as if no client reset was involved. (Core 13.24.1) * Exceptions thrown during bootstrap application will now be surfaced to the user rather than terminating the program with an unhandled exception. (Core 13.25.0) * Allow the using `>`, `>=`, `<`, `<=` operators in `Realm.Filter()` queries for string constants. This is a case sensitive lexicographical comparison. Improved performance of RQL (`.Filter()`) queries on a non-linked string property using: >, >=, <, <=, operators and fixed behaviour that a null string should be evaluated as less than everything, previously nulls were not matched. (Core 13.26.0-14-gdf25f) -* `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been removed in favour of `SyncProgress.ProgressEstimate`, a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) * `Session.GetProgressObservable` can now be used with Flexible Sync. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) ### Fixed From 8ce794d11cd4aab9fb5ac3fed5c49759265a227f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:03:23 +0100 Subject: [PATCH 12/24] Various fixes --- CHANGELOG.md | 2 +- .../ProgressNotifications/ProgressMode.cs | 7 +--- .../ProgressNotifications/SyncProgress.cs | 4 +- Tests/Realm.Tests/Sync/SessionTests.cs | 39 +++++++++++++------ 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72b11f71a9..1cfff79b23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Breaking Changes * Added automatic serialization and deserialization of Realm classes when using methods on `MongoClient.Collection`, without the need to annotate classes with `MongoDB.Bson`attributes. This feature required to change the default serialization for various types (including `DateTimeOffset`). If you prefer to use the previous serialization, you need to call `Realm.SetLegacySerialization` before any kind of serialization is done, otherwise it may not work as epxected. [#3459](https://github.com/realm/realm-dotnet/pull/3459) -* `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been removed in favour of `SyncProgress.ProgressEstimate`, a float value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) +* `SyncProgress.TransferredBytes` and `SyncProgress.TransferableBytes` have been removed in favour of `SyncProgress.ProgressEstimate`, a double value between 0.0 and 1.0 that expresses the percentage estimate of the current progress. (Issue [#3478](https://github.com/realm/realm-dotnet/issues/3478])) ### Enhancements * Added the `MongoClient.GetCollection` method to get a collection of documents from MongoDB that can be deserialized in Realm objects. This methods works the same as `MongoClient.GetDatabase(dbName).GetCollection(collectionName)`, but the database name and collection name are automatically derived from the Realm object class. [#3414](https://github.com/realm/realm-dotnet/pull/3414) diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs index c999e9c05c..145c70d969 100644 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs +++ b/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs @@ -25,15 +25,12 @@ public enum ProgressMode { /// /// The callback will be called forever, or until it is unregistered by disposing the subscription token. - /// Notifications will always report the latest number of transferred bytes, and the most up-to-date number of - /// total transferable bytes. /// ReportIndefinitely, /// - /// The callback will, upon registration, store the total number of bytes to be transferred. When invoked, it will - /// always report the most up-to-date number of transferable bytes out of that original number of transferable bytes. - /// When the number of transferred bytes reaches or exceeds the number of transferable bytes, the callback will + /// The callback will be active until the current transferable bytes are transferred. When invoked, it will always report the + /// progress estimate as a percentage related to the initial transferable bytes. When those bytes are transfered, the callback will /// be unregistered. /// ForCurrentlyOutstandingWork diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index 460acb1f11..a639c13b82 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -26,7 +26,7 @@ namespace Realms.Sync public readonly struct SyncProgress { /// - /// Gets the percentage estimate of the current progress, expressed as a float between 0.0 and 1.0. + /// Gets the percentage estimate of the current progress, expressed as a double between 0.0 and 1.0. /// /// A percentage estimate of the progress. public double ProgressEstimate { get; } @@ -36,6 +36,6 @@ internal SyncProgress(double progressEstimate) ProgressEstimate = progressEstimate; } - internal readonly bool IsComplete => ProgressEstimate >= 1.0; + internal bool IsComplete => ProgressEstimate >= 1.0; } } diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 9f5fbaef0e..aaf41ed537 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -756,7 +756,6 @@ public void Session_OnSessionError() }); } -#pragma warning disable CS0618 // Type or member is obsolete [Test] public void SessionIntegrationTest_ProgressObservable( [ValueSource(nameof(AppTypes))] string appType, @@ -797,6 +796,8 @@ public void SessionIntegrationTest_ProgressObservable( }); } + var lastReportedProgress = 0.0d; + using var token = observable.Subscribe(p => { try @@ -807,20 +808,35 @@ public void SessionIntegrationTest_ProgressObservable( { throw new Exception($"Expected progress estimate to be between 0.0 and 1.0, but was {p.ProgressEstimate}"); } - } - catch (Exception e) - { - completionTcs.TrySetException(e); - } - if (p.IsComplete) - { - if (p.ProgressEstimate != 1.0) + if (mode == ProgressMode.ForCurrentlyOutstandingWork) { - throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); + if (p.ProgressEstimate < lastReportedProgress) + { + throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); + } + + if (p.IsComplete) + { + if (p.ProgressEstimate != 1.0) + { + throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); + } + + completionTcs.TrySetResult(); + } } + else if (mode == ProgressMode.ReportIndefinitely) + { - completionTcs.TrySetResult(); + } + + + lastReportedProgress = p.ProgressEstimate; + } + catch (Exception e) + { + completionTcs.TrySetException(e); } }); @@ -834,7 +850,6 @@ public void SessionIntegrationTest_ProgressObservable( Assert.That(callbacksInvoked, Is.GreaterThanOrEqualTo(1)); }, timeout: 120_000); } -#pragma warning restore CS0618 // Type or member is obsolete [Test] public void Session_Stop_StopsSession() From 8d8dc5aa5519d9064d0159419c2b62c8497921c5 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:21:50 +0100 Subject: [PATCH 13/24] Small fix --- .../ObjectWithPartitionValue_generated.cs | 1 + ...cObjectWithRequiredStringList_generated.cs | 1 + Tests/Realm.Tests/Sync/SessionTests.cs | 115 +++++++++++++++++- 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/ObjectWithPartitionValue_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/ObjectWithPartitionValue_generated.cs index b1b2795ff5..04f6ab2e9d 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/ObjectWithPartitionValue_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/ObjectWithPartitionValue_generated.cs @@ -4,6 +4,7 @@ using Baas; using MongoDB.Bson.Serialization; using NUnit.Framework; +using NUnit.Framework.Internal; using Realms; using Realms.Exceptions.Sync; using Realms.Schema; diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/SyncObjectWithRequiredStringList_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/SyncObjectWithRequiredStringList_generated.cs index 4d82bc34b0..f28eb56af7 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/SyncObjectWithRequiredStringList_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/SyncObjectWithRequiredStringList_generated.cs @@ -4,6 +4,7 @@ using Baas; using MongoDB.Bson.Serialization; using NUnit.Framework; +using NUnit.Framework.Internal; using Realms; using Realms.Exceptions.Sync; using Realms.Schema; diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index aaf41ed537..2753f808e6 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -27,6 +27,7 @@ using System.Threading.Tasks; using Baas; using NUnit.Framework; +using NUnit.Framework.Internal; using Realms.Exceptions.Sync; using Realms.Sync; using Realms.Sync.ErrorHandling; @@ -761,6 +762,8 @@ public void SessionIntegrationTest_ProgressObservable( [ValueSource(nameof(AppTypes))] string appType, [ValueSource(nameof(ProgressModeTypes))] ProgressMode mode) { + Realms.Logging.Logger.Default = Realms.Logging.Logger.Function((w) => TestContext.Out.WriteLine(w)); + Realms.Logging.Logger.LogLevel = Logging.LogLevel.Debug; const int objectSize = 1_000_000; const int objectsToRecord = 2; SyncTestHelpers.RunBaasTestAsync(async () => @@ -803,6 +806,7 @@ public void SessionIntegrationTest_ProgressObservable( try { callbacksInvoked++; + TestContext.Out.WriteLine($"CALLBACK: {callbacksInvoked}"); if (p.ProgressEstimate < 0.0 || p.ProgressEstimate > 1.0) { @@ -833,6 +837,9 @@ public void SessionIntegrationTest_ProgressObservable( lastReportedProgress = p.ProgressEstimate; + + Debug.WriteLine($"END: {callbacksInvoked}"); + } catch (Exception e) { @@ -840,13 +847,117 @@ public void SessionIntegrationTest_ProgressObservable( } }); - realm.Write(() => + //realm.Write(() => + //{ + // realm.Add(new HugeSyncObject(objectSize)); + //}); + + await Task.Delay(15000); + //token.Dispose(); + + Assert.That(callbacksInvoked, Is.GreaterThanOrEqualTo(1)); + }, timeout: 120_000); + } + + [Test] + public void SessionIntegrationTest_ProgressObservable2() + { + var appType = AppConfigType.FlexibleSync; + var mode = ProgressMode.ReportIndefinitely; + + Realms.Logging.Logger.Default = Realms.Logging.Logger.Function((w) => TestContext.Out.WriteLine(w)); + Realms.Logging.Logger.LogLevel = Logging.LogLevel.Debug; + const int objectSize = 1_000_000; + const int objectsToRecord = 2; + SyncTestHelpers.RunBaasTestAsync(async () => + { + Realm realm; + if (appType == AppConfigType.Default) + { + var config = await GetIntegrationConfigAsync(Guid.NewGuid().ToString()); + realm = GetRealm(config); + } + else + { + var config = await GetFLXIntegrationConfigAsync(); + config.PopulateInitialSubscriptions = (r) => + { + r.Subscriptions.Add(r.All()); + }; + realm = await GetRealmAsync(config); + await realm.SyncSession.WaitForDownloadAsync(); + await realm.SyncSession.WaitForUploadAsync(); + } + + var completionTcs = new TaskCompletionSource(); + var callbacksInvoked = 0; + + var session = GetSession(realm); + + var observable = session.GetProgressObservable(ProgressDirection.Upload, mode); + + for (var i = 0; i < objectsToRecord; i++) { - realm.Add(new HugeSyncObject(objectSize)); + realm.Write(() => + { + realm.Add(new HugeSyncObject(objectSize)); + }); + } + + var lastReportedProgress = 0.0d; + + var estimates = new List { }; + + using var token = observable.Subscribe(p => + { + try + { + callbacksInvoked++; + estimates.Add(p.ProgressEstimate); + if (p.ProgressEstimate < 0.0 || p.ProgressEstimate > 1.0) + { + throw new Exception($"Expected progress estimate to be between 0.0 and 1.0, but was {p.ProgressEstimate}"); + } + + if (mode == ProgressMode.ForCurrentlyOutstandingWork) + { + if (p.ProgressEstimate < lastReportedProgress) + { + throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); + } + + if (p.IsComplete) + { + if (p.ProgressEstimate != 1.0) + { + throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); + } + + completionTcs.TrySetResult(); + } + } + else if (mode == ProgressMode.ReportIndefinitely && callbacksInvoked == 3) + { + completionTcs.TrySetResult(); + } + + lastReportedProgress = p.ProgressEstimate; + } + catch (Exception e) + { + completionTcs.TrySetException(e); + } }); + //realm.Write(() => + //{ + // realm.Add(new HugeSyncObject(objectSize)); + //}); + await completionTcs.Task; + TestContext.Out.WriteLine(string.Join(", ", estimates)); + Assert.That(callbacksInvoked, Is.GreaterThanOrEqualTo(1)); }, timeout: 120_000); } From d67355d3305b2cb2a3bcfb3f54590e16a4c21ff9 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:46:46 +0100 Subject: [PATCH 14/24] Fix for E2E tests --- Realm/Realm/Handles/SessionHandle.cs | 6 +-- .../ProgressNotificationToken.cs | 4 +- .../ProgressNotifications/SyncProgress.cs | 24 ++++++++++- Tests/Realm.Tests/Sync/SessionTests.cs | 40 ++++++------------- wrappers/src/async_open_task_cs.cpp | 2 +- wrappers/src/sync_session_cs.hpp | 2 +- 6 files changed, 43 insertions(+), 35 deletions(-) diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs index 6068ce6102..458c208fc4 100644 --- a/Realm/Realm/Handles/SessionHandle.cs +++ b/Realm/Realm/Handles/SessionHandle.cs @@ -42,7 +42,7 @@ public delegate void SessionErrorCallback(IntPtr session_handle_ptr, IntPtr managed_sync_config_handle); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void SessionProgressCallback(IntPtr progress_token_ptr, double progressEstimate); + public delegate void SessionProgressCallback(IntPtr progress_token_ptr, ulong transferred_bytes, ulong transferable_bytes, double progressEstimate); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void SessionWaitCallback(IntPtr task_completion_source, int error_code, PrimitiveValue message); @@ -405,10 +405,10 @@ private static IntPtr NotifyAfterClientReset(IntPtr beforeFrozen, IntPtr after, } [MonoPInvokeCallback(typeof(NativeMethods.SessionProgressCallback))] - private static void HandleSessionProgress(IntPtr tokenPtr, double progressEstimate) + private static void HandleSessionProgress(IntPtr tokenPtr, ulong transferredBytes, ulong transferableBytes, double progressEstimate) { var token = (ProgressNotificationToken?)GCHandle.FromIntPtr(tokenPtr).Target; - token?.Notify(progressEstimate); + token?.Notify(transferredBytes, transferableBytes, progressEstimate); } [MonoPInvokeCallback(typeof(NativeMethods.SessionWaitCallback))] diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs index 57674fc30a..922bdfc2c7 100644 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs +++ b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs @@ -48,13 +48,13 @@ public ProgressNotificationToken(Action observer, Func { try { - _observer(new SyncProgress(progressEstimate)); + _observer(new SyncProgress(transferredBytes, transferableBytes, progressEstimate)); } catch (Exception ex) { diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index a639c13b82..2ef5dd63fb 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -25,14 +25,36 @@ namespace Realms.Sync /// public readonly struct SyncProgress { + /// + /// Gets the number of bytes that have been transferred since subscribing for progress notifications. + /// + /// The number of transferred bytes. + [Obsolete("Not accurate, use ProgressEstimate instead.")] + public ulong TransferredBytes { get; } + + /// + /// Gets the total number of bytes that have to be transferred since subscribing for progress notifications. + /// The difference between that number and gives you the number of bytes not yet + /// transferred. If the difference is 0, then all changes at the instant the callback fires have been + /// successfully transferred. + /// + /// The number of transferable bytes. + [Obsolete("Not accurate, use ProgressEstimate instead.")] + public ulong TransferableBytes { get; } + + /// /// Gets the percentage estimate of the current progress, expressed as a double between 0.0 and 1.0. /// /// A percentage estimate of the progress. public double ProgressEstimate { get; } - internal SyncProgress(double progressEstimate) + internal SyncProgress(ulong transferred, ulong transferable, double progressEstimate) { +#pragma warning disable CS0618 // Type or member is obsolete + TransferredBytes = transferred; + TransferableBytes = transferable; +#pragma warning restore CS0618 // Type or member is obsolete ProgressEstimate = progressEstimate; } diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 2753f808e6..ce95494ad9 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -762,10 +762,9 @@ public void SessionIntegrationTest_ProgressObservable( [ValueSource(nameof(AppTypes))] string appType, [ValueSource(nameof(ProgressModeTypes))] ProgressMode mode) { - Realms.Logging.Logger.Default = Realms.Logging.Logger.Function((w) => TestContext.Out.WriteLine(w)); - Realms.Logging.Logger.LogLevel = Logging.LogLevel.Debug; const int objectSize = 1_000_000; const int objectsToRecord = 2; + SyncTestHelpers.RunBaasTestAsync(async () => { Realm realm; @@ -806,40 +805,28 @@ public void SessionIntegrationTest_ProgressObservable( try { callbacksInvoked++; - TestContext.Out.WriteLine($"CALLBACK: {callbacksInvoked}"); if (p.ProgressEstimate < 0.0 || p.ProgressEstimate > 1.0) { throw new Exception($"Expected progress estimate to be between 0.0 and 1.0, but was {p.ProgressEstimate}"); } - if (mode == ProgressMode.ForCurrentlyOutstandingWork) + if (p.ProgressEstimate < lastReportedProgress) { - if (p.ProgressEstimate < lastReportedProgress) - { - throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); - } + throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); + } - if (p.IsComplete) + if (p.IsComplete) + { + if (p.ProgressEstimate != 1.0) { - if (p.ProgressEstimate != 1.0) - { - throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); - } - - completionTcs.TrySetResult(); + throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); } - } - else if (mode == ProgressMode.ReportIndefinitely) - { + completionTcs.TrySetResult(); } - lastReportedProgress = p.ProgressEstimate; - - Debug.WriteLine($"END: {callbacksInvoked}"); - } catch (Exception e) { @@ -847,13 +834,12 @@ public void SessionIntegrationTest_ProgressObservable( } }); - //realm.Write(() => - //{ - // realm.Add(new HugeSyncObject(objectSize)); - //}); + realm.Write(() => + { + realm.Add(new HugeSyncObject(objectSize)); + }); await Task.Delay(15000); - //token.Dispose(); Assert.That(callbacksInvoked, Is.GreaterThanOrEqualTo(1)); }, timeout: 120_000); diff --git a/wrappers/src/async_open_task_cs.cpp b/wrappers/src/async_open_task_cs.cpp index ad10ef68ca..0111b506ff 100644 --- a/wrappers/src/async_open_task_cs.cpp +++ b/wrappers/src/async_open_task_cs.cpp @@ -44,7 +44,7 @@ REALM_EXPORT uint64_t realm_asyncopentask_register_progress_notifier(const Share { return handle_errors(ex, [&] { return task->register_download_progress_notifier([managed_state](uint64_t transferred, uint64_t transferable, double progress_estimate) { - s_progress_callback(managed_state, progress_estimate); + s_progress_callback(managed_state, transferred, transferable, progress_estimate); }); }); } diff --git a/wrappers/src/sync_session_cs.hpp b/wrappers/src/sync_session_cs.hpp index dacc7b336c..2d2e4ab2ce 100644 --- a/wrappers/src/sync_session_cs.hpp +++ b/wrappers/src/sync_session_cs.hpp @@ -25,7 +25,7 @@ namespace realm::binding { using SharedSyncSession = std::shared_ptr; using SessionErrorCallbackT = void(SharedSyncSession* session, realm_sync_error error, void* managed_sync_config); -using ProgressCallbackT = void(void* state, double progress_estimate); +using ProgressCallbackT = void(void* state, uint64_t transferred_bytes, uint64_t transferrable_bytes, double progress_estimate); using NotifyBeforeClientResetCallbackT = void*(SharedRealm& before_frozen, void* managed_sync_config); using NotifyAfterClientResetCallbackT = void*(SharedRealm& before_frozen, SharedRealm& after, void* managed_sync_config, bool did_recover); From 6a8ef77b71fc5db6ecbcc15ecd82dbf5e8c79133 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:53:20 +0100 Subject: [PATCH 15/24] Fixes --- Tests/Realm.Tests/Sync/SessionTests.cs | 4 ++++ wrappers/src/sync_session_cs.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index ce95494ad9..3415d9af49 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -757,7 +757,11 @@ public void Session_OnSessionError() }); } + + // These tests would all pass now, but they're a deceiving, as for FLX with ReportIndefinitely, it would call it first with ProgressEstimate = 1 + // and then go to the "actual" progress notification [Test] + [Ignore("Ignored for now, until we have a better idea of what we expect from tests")] public void SessionIntegrationTest_ProgressObservable( [ValueSource(nameof(AppTypes))] string appType, [ValueSource(nameof(ProgressModeTypes))] ProgressMode mode) diff --git a/wrappers/src/sync_session_cs.cpp b/wrappers/src/sync_session_cs.cpp index 89d25a9cb7..1b942043e8 100644 --- a/wrappers/src/sync_session_cs.cpp +++ b/wrappers/src/sync_session_cs.cpp @@ -120,7 +120,7 @@ REALM_EXPORT uint64_t realm_syncsession_register_progress_notifier(const SharedS : SyncSession::ProgressDirection::download; return session->register_progress_notifier([managed_state](uint64_t transferred, uint64_t transferable, double progress_estimate) { - s_progress_callback(managed_state, progress_estimate); + s_progress_callback(managed_state, transferred, transferable, progress_estimate); }, notifier_direction, is_streaming); }); } From f24e587488cabc5dc44eb892179e2dc5fe8dcaa0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 16 Feb 2024 09:56:56 +0100 Subject: [PATCH 16/24] Updated to latest commit --- Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs | 1 - wrappers/realm-core | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index 2ef5dd63fb..73e63776d3 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -42,7 +42,6 @@ public readonly struct SyncProgress [Obsolete("Not accurate, use ProgressEstimate instead.")] public ulong TransferableBytes { get; } - /// /// Gets the percentage estimate of the current progress, expressed as a double between 0.0 and 1.0. /// diff --git a/wrappers/realm-core b/wrappers/realm-core index 9a556398d1..e5a654eed3 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 9a556398d1699788aa46995d263cc7bd9e2e044a +Subproject commit e5a654eed3ff8890c741bde0ca003123eea3017a From d86c69fcd7f4e8fc5e16103a45f436452cd978e0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:34:11 +0100 Subject: [PATCH 17/24] Removed ignored --- Tests/Realm.Tests/Sync/SessionTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 3415d9af49..178a5c7816 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -761,7 +761,6 @@ public void Session_OnSessionError() // These tests would all pass now, but they're a deceiving, as for FLX with ReportIndefinitely, it would call it first with ProgressEstimate = 1 // and then go to the "actual" progress notification [Test] - [Ignore("Ignored for now, until we have a better idea of what we expect from tests")] public void SessionIntegrationTest_ProgressObservable( [ValueSource(nameof(AppTypes))] string appType, [ValueSource(nameof(ProgressModeTypes))] ProgressMode mode) From 1c4d11259a9d2f9b278c3f017d36fd967f71fbfc Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 16 Feb 2024 14:14:58 +0100 Subject: [PATCH 18/24] Fixed test [skip-ci] --- Tests/Realm.Tests/Sync/SessionTests.cs | 29 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 178a5c7816..f108463b0f 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -757,9 +757,8 @@ public void Session_OnSessionError() }); } - - // These tests would all pass now, but they're a deceiving, as for FLX with ReportIndefinitely, it would call it first with ProgressEstimate = 1 - // and then go to the "actual" progress notification + // This whole test needs to be redone when the core PR on which it is based it's merged. + // Need also to add tests for notifications created when opening an async realm. [Test] public void SessionIntegrationTest_ProgressObservable( [ValueSource(nameof(AppTypes))] string appType, @@ -803,12 +802,18 @@ public void SessionIntegrationTest_ProgressObservable( var lastReportedProgress = 0.0d; + var progressList = new List(); + + bool alreadyCompleted = false; + using var token = observable.Subscribe(p => { try { callbacksInvoked++; + progressList.Add(p); + if (p.ProgressEstimate < 0.0 || p.ProgressEstimate > 1.0) { throw new Exception($"Expected progress estimate to be between 0.0 and 1.0, but was {p.ProgressEstimate}"); @@ -816,7 +821,7 @@ public void SessionIntegrationTest_ProgressObservable( if (p.ProgressEstimate < lastReportedProgress) { - throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); + //throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); } if (p.IsComplete) @@ -826,7 +831,19 @@ public void SessionIntegrationTest_ProgressObservable( throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); } - completionTcs.TrySetResult(); + if (appType == "flx" && mode == ProgressMode.ReportIndefinitely) + { + if (alreadyCompleted) + { + completionTcs.TrySetResult(); + } + + alreadyCompleted = true; + } + else + { + completionTcs.TrySetResult(); + } } lastReportedProgress = p.ProgressEstimate; @@ -842,7 +859,7 @@ public void SessionIntegrationTest_ProgressObservable( realm.Add(new HugeSyncObject(objectSize)); }); - await Task.Delay(15000); + await completionTcs.Task; Assert.That(callbacksInvoked, Is.GreaterThanOrEqualTo(1)); }, timeout: 120_000); From 1c80ff1491ef5795461064fa9ec8cd364d154b24 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:36:02 +0200 Subject: [PATCH 19/24] Various fixes --- Realm/Realm/Handles/SessionHandle.cs | 2 +- .../ProgressNotificationToken.cs | 4 ++-- .../ProgressNotifications/SyncProgress.cs | 23 +------------------ wrappers/realm-core | 2 +- 4 files changed, 5 insertions(+), 26 deletions(-) diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs index 458c208fc4..4fb85bcdd2 100644 --- a/Realm/Realm/Handles/SessionHandle.cs +++ b/Realm/Realm/Handles/SessionHandle.cs @@ -408,7 +408,7 @@ private static IntPtr NotifyAfterClientReset(IntPtr beforeFrozen, IntPtr after, private static void HandleSessionProgress(IntPtr tokenPtr, ulong transferredBytes, ulong transferableBytes, double progressEstimate) { var token = (ProgressNotificationToken?)GCHandle.FromIntPtr(tokenPtr).Target; - token?.Notify(transferredBytes, transferableBytes, progressEstimate); + token?.Notify(progressEstimate); } [MonoPInvokeCallback(typeof(NativeMethods.SessionWaitCallback))] diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs index 922bdfc2c7..57674fc30a 100644 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs +++ b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs @@ -48,13 +48,13 @@ public ProgressNotificationToken(Action observer, Func { try { - _observer(new SyncProgress(transferredBytes, transferableBytes, progressEstimate)); + _observer(new SyncProgress(progressEstimate)); } catch (Exception ex) { diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index 73e63776d3..a639c13b82 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -25,35 +25,14 @@ namespace Realms.Sync /// public readonly struct SyncProgress { - /// - /// Gets the number of bytes that have been transferred since subscribing for progress notifications. - /// - /// The number of transferred bytes. - [Obsolete("Not accurate, use ProgressEstimate instead.")] - public ulong TransferredBytes { get; } - - /// - /// Gets the total number of bytes that have to be transferred since subscribing for progress notifications. - /// The difference between that number and gives you the number of bytes not yet - /// transferred. If the difference is 0, then all changes at the instant the callback fires have been - /// successfully transferred. - /// - /// The number of transferable bytes. - [Obsolete("Not accurate, use ProgressEstimate instead.")] - public ulong TransferableBytes { get; } - /// /// Gets the percentage estimate of the current progress, expressed as a double between 0.0 and 1.0. /// /// A percentage estimate of the progress. public double ProgressEstimate { get; } - internal SyncProgress(ulong transferred, ulong transferable, double progressEstimate) + internal SyncProgress(double progressEstimate) { -#pragma warning disable CS0618 // Type or member is obsolete - TransferredBytes = transferred; - TransferableBytes = transferable; -#pragma warning restore CS0618 // Type or member is obsolete ProgressEstimate = progressEstimate; } diff --git a/wrappers/realm-core b/wrappers/realm-core index e5a654eed3..d91b05ae22 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit e5a654eed3ff8890c741bde0ca003123eea3017a +Subproject commit d91b05ae22cb0ede3909f34b2952f7a933b9f5ee From c1ed213cfa0839b9963687d4d0eef086c5133952 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:40:46 +0200 Subject: [PATCH 20/24] Added ignore --- Tests/Realm.Tests/Sync/SessionTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 29f301fd0c..110fbc30f2 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -757,9 +757,8 @@ public void Session_OnSessionError() }); } - // This whole test needs to be redone when the core PR on which it is based it's merged. - // Need also to add tests for notifications created when opening an async realm. [Test] + [Ignore("This whole test needs to be redone when the work on progress notification is finished.")] public void SessionIntegrationTest_ProgressObservable( [ValueSource(nameof(AppTypes))] string appType, [ValueSource(nameof(ProgressModeTypes))] ProgressMode mode) @@ -821,7 +820,7 @@ public void SessionIntegrationTest_ProgressObservable( if (p.ProgressEstimate < lastReportedProgress) { - //throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); + throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); } if (p.IsComplete) From 42cec9fe1273333be942b824034a139a619bf23d Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:41:41 +0200 Subject: [PATCH 21/24] Removed unused --- Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs index a639c13b82..20ddfa8379 100644 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs @@ -16,8 +16,6 @@ // //////////////////////////////////////////////////////////////////////////// -using System; - namespace Realms.Sync { /// From a81770597f8e9b0c8589c81fd57e367874ffd6f1 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:44:52 +0200 Subject: [PATCH 22/24] Removed unused test --- Tests/Realm.Tests/Sync/SessionTests.cs | 103 ------------------------- 1 file changed, 103 deletions(-) diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 110fbc30f2..16de51e86c 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -864,109 +864,6 @@ public void SessionIntegrationTest_ProgressObservable( }, timeout: 120_000); } - [Test] - public void SessionIntegrationTest_ProgressObservable2() - { - var appType = AppConfigType.FlexibleSync; - var mode = ProgressMode.ReportIndefinitely; - - Realms.Logging.Logger.Default = Realms.Logging.Logger.Function((w) => TestContext.Out.WriteLine(w)); - Realms.Logging.Logger.LogLevel = Logging.LogLevel.Debug; - const int objectSize = 1_000_000; - const int objectsToRecord = 2; - SyncTestHelpers.RunBaasTestAsync(async () => - { - Realm realm; - if (appType == AppConfigType.Default) - { - var config = await GetIntegrationConfigAsync(Guid.NewGuid().ToString()); - realm = GetRealm(config); - } - else - { - var config = await GetFLXIntegrationConfigAsync(); - config.PopulateInitialSubscriptions = (r) => - { - r.Subscriptions.Add(r.All()); - }; - realm = await GetRealmAsync(config); - await realm.SyncSession.WaitForDownloadAsync(); - await realm.SyncSession.WaitForUploadAsync(); - } - - var completionTcs = new TaskCompletionSource(); - var callbacksInvoked = 0; - - var session = GetSession(realm); - - var observable = session.GetProgressObservable(ProgressDirection.Upload, mode); - - for (var i = 0; i < objectsToRecord; i++) - { - realm.Write(() => - { - realm.Add(new HugeSyncObject(objectSize)); - }); - } - - var lastReportedProgress = 0.0d; - - var estimates = new List { }; - - using var token = observable.Subscribe(p => - { - try - { - callbacksInvoked++; - estimates.Add(p.ProgressEstimate); - if (p.ProgressEstimate < 0.0 || p.ProgressEstimate > 1.0) - { - throw new Exception($"Expected progress estimate to be between 0.0 and 1.0, but was {p.ProgressEstimate}"); - } - - if (mode == ProgressMode.ForCurrentlyOutstandingWork) - { - if (p.ProgressEstimate < lastReportedProgress) - { - throw new Exception($"Expected progress estimate is expected to be monotonically increasing, but it wasn't."); - } - - if (p.IsComplete) - { - if (p.ProgressEstimate != 1.0) - { - throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); - } - - completionTcs.TrySetResult(); - } - } - else if (mode == ProgressMode.ReportIndefinitely && callbacksInvoked == 3) - { - completionTcs.TrySetResult(); - } - - lastReportedProgress = p.ProgressEstimate; - } - catch (Exception e) - { - completionTcs.TrySetException(e); - } - }); - - //realm.Write(() => - //{ - // realm.Add(new HugeSyncObject(objectSize)); - //}); - - await completionTcs.Task; - - TestContext.Out.WriteLine(string.Join(", ", estimates)); - - Assert.That(callbacksInvoked, Is.GreaterThanOrEqualTo(1)); - }, timeout: 120_000); - } - [Test] public void Session_Stop_StopsSession() { From 3a2ddcd4e6f0179e5cb2794c4e4236c294406fd5 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:46:36 +0200 Subject: [PATCH 23/24] Fixes --- Realm/Realm/Handles/SessionHandle.cs | 4 +++- .../ObjectWithPartitionValue_generated.cs | 1 - ...cObjectWithRequiredStringList_generated.cs | 1 - Tests/Realm.Tests/Sync/SessionTests.cs | 19 ++----------------- 4 files changed, 5 insertions(+), 20 deletions(-) diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs index 4fb85bcdd2..a3dd2f051b 100644 --- a/Realm/Realm/Handles/SessionHandle.cs +++ b/Realm/Realm/Handles/SessionHandle.cs @@ -408,7 +408,9 @@ private static IntPtr NotifyAfterClientReset(IntPtr beforeFrozen, IntPtr after, private static void HandleSessionProgress(IntPtr tokenPtr, ulong transferredBytes, ulong transferableBytes, double progressEstimate) { var token = (ProgressNotificationToken?)GCHandle.FromIntPtr(tokenPtr).Target; - token?.Notify(progressEstimate); + // This is used to provide a reasonable progress estimate until the core work is done + double managedProgressEstimate = transferableBytes > 0.0 ? transferredBytes / transferableBytes : 0.0; + token?.Notify(managedProgressEstimate); } [MonoPInvokeCallback(typeof(NativeMethods.SessionWaitCallback))] diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/ObjectWithPartitionValue_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/ObjectWithPartitionValue_generated.cs index 04f6ab2e9d..b1b2795ff5 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/ObjectWithPartitionValue_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/ObjectWithPartitionValue_generated.cs @@ -4,7 +4,6 @@ using Baas; using MongoDB.Bson.Serialization; using NUnit.Framework; -using NUnit.Framework.Internal; using Realms; using Realms.Exceptions.Sync; using Realms.Schema; diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/SyncObjectWithRequiredStringList_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/SyncObjectWithRequiredStringList_generated.cs index f28eb56af7..4d82bc34b0 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/SyncObjectWithRequiredStringList_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/SyncObjectWithRequiredStringList_generated.cs @@ -4,7 +4,6 @@ using Baas; using MongoDB.Bson.Serialization; using NUnit.Framework; -using NUnit.Framework.Internal; using Realms; using Realms.Exceptions.Sync; using Realms.Schema; diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs index 16de51e86c..0c85fb6800 100644 --- a/Tests/Realm.Tests/Sync/SessionTests.cs +++ b/Tests/Realm.Tests/Sync/SessionTests.cs @@ -27,7 +27,6 @@ using System.Threading.Tasks; using Baas; using NUnit.Framework; -using NUnit.Framework.Internal; using Realms.Exceptions.Sync; using Realms.Sync; using Realms.Sync.ErrorHandling; @@ -757,8 +756,8 @@ public void Session_OnSessionError() }); } + // This test needs to be revisited when the work on progress notification is finished. [Test] - [Ignore("This whole test needs to be redone when the work on progress notification is finished.")] public void SessionIntegrationTest_ProgressObservable( [ValueSource(nameof(AppTypes))] string appType, [ValueSource(nameof(ProgressModeTypes))] ProgressMode mode) @@ -803,8 +802,6 @@ public void SessionIntegrationTest_ProgressObservable( var progressList = new List(); - bool alreadyCompleted = false; - using var token = observable.Subscribe(p => { try @@ -830,19 +827,7 @@ public void SessionIntegrationTest_ProgressObservable( throw new Exception($"Expected progress estimate to be complete if and only if ProgressEstimate == 1.0"); } - if (appType == "flx" && mode == ProgressMode.ReportIndefinitely) - { - if (alreadyCompleted) - { - completionTcs.TrySetResult(); - } - - alreadyCompleted = true; - } - else - { - completionTcs.TrySetResult(); - } + completionTcs.TrySetResult(); } lastReportedProgress = p.ProgressEstimate; From ae1db72db2a677a692bb547f71310870c16cfac3 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:47:21 +0200 Subject: [PATCH 24/24] Corrected documentation and fixed linting --- Realm/Realm/Handles/SessionHandle.cs | 3 ++- Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs index a3dd2f051b..849abbbf64 100644 --- a/Realm/Realm/Handles/SessionHandle.cs +++ b/Realm/Realm/Handles/SessionHandle.cs @@ -408,8 +408,9 @@ private static IntPtr NotifyAfterClientReset(IntPtr beforeFrozen, IntPtr after, private static void HandleSessionProgress(IntPtr tokenPtr, ulong transferredBytes, ulong transferableBytes, double progressEstimate) { var token = (ProgressNotificationToken?)GCHandle.FromIntPtr(tokenPtr).Target; + // This is used to provide a reasonable progress estimate until the core work is done - double managedProgressEstimate = transferableBytes > 0.0 ? transferredBytes / transferableBytes : 0.0; + double managedProgressEstimate = transferableBytes > 0.0 ? transferredBytes / transferableBytes : 1.0; token?.Notify(managedProgressEstimate); } diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs index 145c70d969..9fd170fa54 100644 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs +++ b/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs @@ -29,9 +29,11 @@ public enum ProgressMode ReportIndefinitely, /// - /// The callback will be active until the current transferable bytes are transferred. When invoked, it will always report the - /// progress estimate as a percentage related to the initial transferable bytes. When those bytes are transfered, the callback will - /// be unregistered. + /// The callback will be active: + /// + /// For uploads, until all the unsynced data available at the moment of the registration of the callback is sent to the server. + /// For downloads, until the client catches up to the current data (available at the moment of callback registration) or the next batch of data sent from the server. + /// /// ForCurrentlyOutstandingWork }