Skip to content

Commit

Permalink
Add session.stop/start methods (#1839)
Browse files Browse the repository at this point in the history
  • Loading branch information
nirinchev committed Mar 7, 2019
1 parent 71a7994 commit f8be6fa
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 5 deletions.
18 changes: 14 additions & 4 deletions CHANGELOG.md
@@ -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
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
Expand Up @@ -69,6 +69,12 @@ private static class NativeMethods

[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
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
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
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
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();
});
}

}

0 comments on commit f8be6fa

Please sign in to comment.