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

Support opening a synchronized realm without synchronizing #621

Merged
merged 10 commits into from
Jun 13, 2022
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ jobs:
run: echo "::set-output name=commit::$(git rev-parse HEAD)"

- name: Setup Vcpkg
continue-on-error: true
if: contains(github.head_ref, 'release/') || steps.check-cache.outputs.cache-hit != 'true'
uses: friendlyanon/setup-vcpkg@v1
with:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## vNext

**This project is in the Beta stage. The API should be quite stable, but occasional breaking changes may be made.**
### Enhancements
* Added `DisconnectedSyncConfiguration` for opening a synchronized realm in a disconnected state. This configuration allows a synchronized realm to be opened by a secondary process, while a primary process handles synchronization. ([#621](https://github.com/realm/realm-dart/pull/621))

## 0.3.1+beta (2022-06-07)

**This project is in the Beta stage. The API should be quite stable, but occasional breaking changes may be made.**
Expand Down
44 changes: 36 additions & 8 deletions lib/src/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ abstract class Configuration {
syncErrorHandler: syncErrorHandler,
syncClientResetErrorHandler: syncClientResetErrorHandler,
);

/// Constructs a [DisconnectedSyncConfiguration]
static DisconnectedSyncConfiguration disconnectedSync(
List<SchemaObject> schemaObjects, {
String? fifoFilesFallbackPath,
String? path,
}) =>
DisconnectedSyncConfiguration._(
schemaObjects,
fifoFilesFallbackPath: fifoFilesFallbackPath,
path: path,
);
}

extension ConfigurationInternal on Configuration {
Expand Down Expand Up @@ -214,32 +226,35 @@ enum SessionStopPolicy {
typedef SyncErrorHandler = void Function(SyncError);

void defaultSyncErrorHandler(SyncError e) {
Realm.logger.log(RealmLogLevel.error, e);
Realm.logger.log(RealmLogLevel.error, e);
}

void _defaultSyncClientResetHandler(SyncError e) {
Realm.logger.log(RealmLogLevel.error, "A client reset error occurred but no handler was supplied. "
"Synchronization is now paused and will resume automatically once the app is restarted and "
"the server data is redownloaded. Any unsynchronized changes the client has made or will "
"make will be lost. To handle that scenario, pass in a non-null value to "
"syncClientResetErrorHandler when constructing Configuration.flexibleSync.");
Realm.logger.log(
RealmLogLevel.error,
"A client reset error occurred but no handler was supplied. "
"Synchronization is now paused and will resume automatically once the app is restarted and "
"the server data is re-downloaded. Any un-synchronized changes the client has made or will "
"make will be lost. To handle that scenario, pass in a non-null value to "
"syncClientResetErrorHandler when constructing Configuration.flexibleSync.");
}

/// [FlexibleSyncConfiguration] is used to open [Realm] instances that are synchronized
/// with MongoDB Atlas.
/// {@category Configuration}
class FlexibleSyncConfiguration extends Configuration {
/// The [User] used to created this [FlexibleSyncConfiguration]
final User user;

SessionStopPolicy _sessionStopPolicy = SessionStopPolicy.afterChangesUploaded;

/// Called when a [SyncError] occurs for this synchronized [Realm].
///
///
/// The default [SyncErrorHandler] prints to the console
final SyncErrorHandler syncErrorHandler;

/// Called when a [SyncClientResetError] occurs for this synchronized [Realm]
///
///
/// The default [SyncClientResetErrorHandler] logs a message using the current Realm.logger
final SyncClientResetErrorHandler syncClientResetErrorHandler;

Expand All @@ -263,6 +278,19 @@ extension FlexibleSyncConfigurationInternal on FlexibleSyncConfiguration {
set sessionStopPolicy(SessionStopPolicy value) => _sessionStopPolicy = value;
}

/// [DisconnectedSyncConfiguration] is used to open [Realm] instances that are synchronized
/// with MongoDB Atlas, without establishing a connection to Atlas App Services. This allows
/// for the synchronized realm to be opened in multiple processes concurrently, as long as
/// only one of them uses a [FlexibleSyncConfiguration] to sync changes.
/// {@category Configuration}
class DisconnectedSyncConfiguration extends Configuration {
DisconnectedSyncConfiguration._(
super.schemaObjects, {
super.fifoFilesFallbackPath,
super.path,
nielsenko marked this conversation as resolved.
Show resolved Hide resolved
}) : super._();
}

/// [InMemoryConfiguration] is used to open [Realm] instances that
/// are temporary to running process.
/// {@category Configuration}
Expand Down
2 changes: 2 additions & 0 deletions lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ class _RealmCore {
} finally {
_realmLib.realm_release(syncConfigPtr.cast());
}
} else if (config is DisconnectedSyncConfiguration) {
_realmLib.realm_config_set_force_sync_history(configPtr, true);
}

return configHandle;
Expand Down
23 changes: 23 additions & 0 deletions test/configuration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import 'dart:io';
import 'dart:math';
import 'package:test/test.dart' hide test, throws;
import 'package:path/path.dart' as path;
import '../lib/realm.dart';
import 'test.dart';

Expand Down Expand Up @@ -412,4 +413,26 @@ Future<void> main([List<String>? args]) async {

expect(config.path, 'my-custom-path.realm');
});

baasTest('Configuration.disconnectedSync', (appConfig) async {
final app = App(appConfig);
final user = await app.logIn(Credentials.emailPassword(testUsername, testPassword));

final dir = await Directory.systemTemp.createTemp();
final realmPath = path.join(dir.path, 'test.realm');

final schema = [Task.schema];
final flexibleSyncConfig = Configuration.flexibleSync(user, schema, path: realmPath);
final realm = Realm(flexibleSyncConfig);
final oid = ObjectId();
realm.subscriptions.update((mutableSubscriptions) {
mutableSubscriptions.add(realm.query<Task>(r'_id == $0', [oid]));
});
realm.write(() => realm.add(Task(oid)));
realm.close();

final disconnectedSyncConfig = Configuration.disconnectedSync(schema, path: realmPath);
final disconnectedRealm = Realm(disconnectedSyncConfig);
expect(disconnectedRealm.find<Task>(oid), isNotNull);
});
}
2 changes: 1 addition & 1 deletion test/subscription_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import 'package:meta/meta.dart';
import 'package:test/expect.dart';

import '../lib/realm.dart';
import '../lib/src/configuration.dart';
import '../lib/src/native/realm_core.dart';
import '../lib/src/subscription.dart';

import 'test.dart';

@isTest
Expand Down