Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add session.stop/start methods #1839

Merged
merged 1 commit into from
Mar 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
3.5.0 (TBD)
------------------

### Enhancements
* Added `Session.Start()` and `Session.Stop()` methods that allow you to pause/resume synchronization with the Realm Object Server. ([Issue #138](https://github.com/realm/realm-dotnet-private/issues/138))

### Fixed
* None

### Compatibility
* Realm Object Server: 3.11.0 or later.

3.4.0 (2019-01-09)
------------------

Expand All @@ -7,6 +19,8 @@ NOTE!!! You will need to upgrade your Realm Object Server to at least version 3.
* Download progress is now reported to the server, even when there are no local changes. This allows the server to do history compaction much more aggressively, especially when there are many clients that rarely or never make local changes. ([#1772](https://github.com/realm/realm-dotnet/pull/1772))
* Reduce memory usage when integrating synchronized changes sent by ROS.
* Added ability to supply a custom log function for handling logs emitted by Sync by specifying `SyncConfigurationBase.CustomLogger`. It must be set before opening a synchronized Realm. ([#1824](https://github.com/realm/realm-dotnet/pull/1824))
* Clients using protocol 25 now report download progress to the server, even when they make no local changes. This allows the server to do history compaction much more aggressively, especially when there are many clients that rarely or never make local changes. ([#1772](https://github.com/realm/realm-dotnet/pull/1772))
* Add a User-Agent header to HTTP requests made to the Realm Object Server. By default, this contains information about the Realm library version and .NET platform. Additional details may be provided (such as the application name/version) by setting `SyncConfigurationBase.UserAgent` prior to opening a synchronized Realm. If developing a Xamarin app, you can use the Xamarin.Essentials plugin to automate that: `SyncConfiguration.UserAgent = $"{AppInfo.Name} ({AppInfo.PackageName} {AppInfo.VersionString})"`.

### Fixed
* Fixed a bug that could lead to crashes with a message such as `Assertion failed: ndx < size() with (ndx, size()) = [742, 742]`.
Expand All @@ -17,10 +31,6 @@ NOTE!!! You will need to upgrade your Realm Object Server to at least version 3.
* Realm Object Server: 3.11.0 or later.
The sync protocol version has been bumped to version 25. The server is backwards-compatible with clients using protocol version 24 or below, but clients at version 25 are not backwards-compatible with a server at protocol version 24. The server must be upgraded before any clients are upgraded.

### Enahancements
* Clients using protocol 25 now report download progress to the server, even when they make no local changes. This allows the server to do history compaction much more aggressively, especially when there are many clients that rarely or never make local changes. ([#1772](https://github.com/realm/realm-dotnet/pull/1772))
* Add a User-Agent header to HTTP requests made to the Realm Object Server. By default, this contains information about the Realm library version and .NET platform. Additional details may be provided (such as the application name/version) by setting `SyncConfigurationBase.UserAgent` prior to opening a synchronized Realm. If developing a Xamarin app, you can use the Xamarin.Essentials plugin to automate that: `SyncConfiguration.UserAgent = $"{AppInfo.Name} ({AppInfo.PackageName} {AppInfo.VersionString})"`.


### Fixed
<!-- * <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-dotnet/issues/????), since v?.?.?) -->
Expand Down
26 changes: 26 additions & 0 deletions Platform.PCL/Realm.Sync.PCL/SessionPCL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,31 @@ public Task WaitForDownloadAsync()
RealmPCLHelpers.ThrowProxyShouldNeverBeUsed();
return null;
}

/// <summary>
/// Stops any synchronization with the Realm Object Server until the Realm is re-opened again
/// after fully closing it.
/// <br/>
/// Synchronization can be re-enabled by calling <see cref="Start"/> again.
/// </summary>
/// <remarks>
/// If the session is already stopped, calling this method will do nothing.
/// </remarks>
public void Stop()
{
RealmPCLHelpers.ThrowProxyShouldNeverBeUsed();
}

/// <summary>
/// Attempts to resume the session and enable synchronization with the Realm Object Server.
/// </summary>
/// <remarks>
/// All sessions will be active by default and calling this method only makes sense if
/// <see cref="Stop"/> was called before that.
/// </remarks>
public void Start()
{
RealmPCLHelpers.ThrowProxyShouldNeverBeUsed();
}
}
}
18 changes: 18 additions & 0 deletions Realm/Realm.Sync/Handles/SessionHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ public static extern ulong register_progress_notifier(SessionHandle session,

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_report_error_for_testing", CallingConvention = CallingConvention.Cdecl)]
public static extern void report_error_for_testing(SessionHandle session, int error_code, [MarshalAs(UnmanagedType.LPWStr)] string message, IntPtr message_len, [MarshalAs(UnmanagedType.I1)] bool is_fatal);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_stop", CallingConvention = CallingConvention.Cdecl)]
public static extern void stop(SessionHandle session, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_start", CallingConvention = CallingConvention.Cdecl)]
public static extern void start(SessionHandle session, out NativeException ex);
}

static SessionHandle()
Expand Down Expand Up @@ -157,6 +163,18 @@ public void ReportErrorForTesting(int errorCode, string errorMessage, bool isFat
NativeMethods.report_error_for_testing(this, errorCode, errorMessage, (IntPtr)errorMessage.Length, isFatal);
}

public void Stop()
{
NativeMethods.stop(this, out var ex);
ex.ThrowIfNecessary();
}

public void Start()
{
NativeMethods.start(this, out var ex);
ex.ThrowIfNecessary();
}

public static SessionHandle GetSessionForPath(string path)
{
var ptr = NativeMethods.get_from_path(path, (IntPtr)path.Length, out var ex);
Expand Down
26 changes: 26 additions & 0 deletions Realm/Realm.Sync/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,32 @@ public Task WaitForDownloadAsync()
return tcs.Task;
}

/// <summary>
/// Stops any synchronization with the Realm Object Server until the Realm is re-opened again
/// after fully closing it.
/// <br/>
/// Synchronization can be re-enabled by calling <see cref="Start"/> again.
/// </summary>
/// <remarks>
/// If the session is already stopped, calling this method will do nothing.
/// </remarks>
public void Stop()
{
Handle.Stop();
}

/// <summary>
/// Attempts to resume the session and enable synchronization with the Realm Object Server.
/// </summary>
/// <remarks>
/// All sessions will be active by default and calling this method only makes sense if
/// <see cref="Stop"/> was called before that.
/// </remarks>
public void Start()
{
Handle.Start();
}

internal readonly SessionHandle Handle;

internal Session(SessionHandle handle)
Expand Down
70 changes: 70 additions & 0 deletions Tests/Tests.Sync.Shared/SessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,57 @@ public void Session_ProgressObservable_IntegrationTests(ProgressMode mode)
});
}

[Test]
public void Session_Stop_StopsSession()
{
AsyncContext.Run(async () =>
{
// OpenRealmAndStopSession will call Stop and assert the state changed
await OpenRealmAndStopSession();
});
}

[Test]
public void Session_Start_ResumesSession()
{
AsyncContext.Run(async () =>
{
var session = await OpenRealmAndStopSession();

session.Start();
Assert.That(session.State, Is.EqualTo(SessionState.Active));
});
}

[Test]
public void Session_Stop_IsIdempotent()
{
AsyncContext.Run(async () =>
{
var session = await OpenRealmAndStopSession();

// Stop it again
session.Stop();
Assert.That(session.State, Is.EqualTo(SessionState.Inactive));
});
}

[Test]
public void Session_Start_IsIdempotent()
{
AsyncContext.Run(async () =>
{
var session = await OpenRealmAndStopSession();

session.Start();
Assert.That(session.State, Is.EqualTo(SessionState.Active));

// Start it again
session.Start();
Assert.That(session.State, Is.EqualTo(SessionState.Active));
});
}

[TestCase(ProgressDirection.Upload, ProgressMode.ReportIndefinitely)]
[TestCase(ProgressDirection.Download, ProgressMode.ReportIndefinitely)]
[TestCase(ProgressDirection.Upload, ProgressMode.ForCurrentlyOutstandingWork)]
Expand Down Expand Up @@ -519,5 +570,24 @@ public void Session_RXThrottleTests()
}
});
}

/// <summary>
/// Opens a random realm and calls session.Stop(). It will assert state changes
/// to Inactive.
/// </summary>
/// <returns>The stopped session.</returns>
private async Task<Session> OpenRealmAndStopSession()
{
var config = await SyncTestHelpers.GetFakeConfigAsync();
var realm = GetRealm(config);
var session = GetSession(realm);

Assert.That(session.State, Is.EqualTo(SessionState.Active));

session.Stop();
Assert.That(session.State, Is.EqualTo(SessionState.Inactive));

return session;
}
}
}
2 changes: 1 addition & 1 deletion Tests/Tests.Sync.Shared/SyncTestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public static Task<User> GetAdminUserAsync()
public static async Task<FullSyncConfiguration> GetFakeConfigAsync(string userId = null, string optionalPath = null)
{
var user = await GetFakeUserAsync(userId);
var serverUri = new Uri("realm://localhost:9080/foobar");
var serverUri = new Uri($"realm://localhost:9080/{Guid.NewGuid().ToString()}");
return new FullSyncConfiguration(serverUri, user, optionalPath);
}

Expand Down
14 changes: 14 additions & 0 deletions wrappers/src/sync_session_cs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,19 @@ REALM_EXPORT void realm_syncsession_report_error_for_testing(const SharedSyncSes
SyncSession::OnlyForTesting::handle_error(*session, SyncError{error_code, std::move(message), is_fatal});
}

REALM_EXPORT void realm_syncsession_stop(const SharedSyncSession& session, NativeException::Marshallable& ex)
{
handle_errors(ex, [&] {
session->log_out();
});
}

REALM_EXPORT void realm_syncsession_start(const SharedSyncSession& session, NativeException::Marshallable& ex)
{
handle_errors(ex, [&] {
session->revive_if_needed();
});
}

}