Skip to content

Commit

Permalink
Isolate refactor (#21)
Browse files Browse the repository at this point in the history
* cleanup

* refactor

* fix web test
  • Loading branch information
danReynolds authored Nov 12, 2024
1 parent 079ca7c commit 97b90a7
Show file tree
Hide file tree
Showing 35 changed files with 851 additions and 625 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 5.1.0

* Creates generic persistor worker isolate interface.
* Move SQLite operations to a worker isolate using new interface.
* Refactor logging for global enable/disable support.

## 5.0.0

* Adds web persistence support using the IndexedDBPersistor.
Expand Down
4 changes: 1 addition & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ final logger = Logger('Playground');
void main() async {
WidgetsFlutterBinding.ensureInitialized();

Loon.configure(
persistor: Persistor.current(),
);
Loon.configure(persistor: Persistor.current(), enableLogging: true);

await logger.measure('Hydrate', () => Loon.hydrate());

Expand Down
18 changes: 17 additions & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ packages:
path: ".."
relative: true
source: path
version: "4.0.1"
version: "5.0.0"
matcher:
dependency: transitive
description:
Expand Down Expand Up @@ -396,6 +396,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.5.4+5"
sqflite_common_ffi:
dependency: transitive
description:
name: sqflite_common_ffi
sha256: d316908f1537725427ff2827a5c5f3b2c1bc311caed985fe3c9b10939c9e11ca
url: "https://pub.dev"
source: hosted
version: "2.3.4"
sqflite_darwin:
dependency: transitive
description:
Expand All @@ -412,6 +420,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: "45f168ae2213201b54e09429ed0c593dc2c88c924a1488d6f9c523a255d567cb"
url: "https://pub.dev"
source: hosted
version: "2.4.6"
stack_trace:
dependency: transitive
description:
Expand Down
15 changes: 12 additions & 3 deletions lib/collection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Collection<T> implements Queryable<T>, StoreReference {
final String name;
final FromJson<T>? fromJson;
final ToJson<T>? toJson;
final PersistorSettings? persistorSettings;
late final PersistorSettings? persistorSettings;

/// Returns the set of documents that the document associated with the given
/// [DocumentSnapshot] is dependent on.
Expand All @@ -28,8 +28,17 @@ class Collection<T> implements Queryable<T>, StoreReference {
this.fromJson,
this.toJson,
this.dependenciesBuilder,
this.persistorSettings,
});
PersistorSettings? persistorSettings,
}) {
this.persistorSettings = switch (persistorSettings) {
PathPersistorSettings _ => persistorSettings,
// If the persistor settings are not yet associated with a path, then if a value key
// is provided, then the settings are updated to having been applied at the collection's path.
PersistorSettings(key: final PersistorValueKey _) =>
PathPersistorSettings(settings: persistorSettings, ref: this),
_ => persistorSettings,
};
}

static Collection<S> fromPath<S>(
String path, {
Expand Down
17 changes: 13 additions & 4 deletions lib/document.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class Document<T> implements StoreReference {
final FromJson<T>? fromJson;
final ToJson<T>? toJson;
final DependenciesBuilder<T>? dependenciesBuilder;
late final DocumentPersistorSettings? persistorSettings;

late final PathPersistorSettings? persistorSettings;

Document(
this.parent,
Expand All @@ -17,10 +18,12 @@ class Document<T> implements StoreReference {
PersistorSettings? persistorSettings,
}) {
this.persistorSettings = switch (persistorSettings) {
DocumentPersistorSettings _ => persistorSettings,
PathPersistorSettings _ => persistorSettings,
// If the persistor settings are not yet associated with a path, then the settings
// are updated to having been applied at the document's path.
PersistorSettings _ =>
DocumentPersistorSettings(settings: persistorSettings, doc: this),
_ => null,
PathPersistorSettings(settings: persistorSettings, ref: this),
_ => persistorSettings,
};
}

Expand Down Expand Up @@ -195,6 +198,12 @@ class Document<T> implements StoreReference {
Loon._instance._isGlobalPersistenceEnabled;
}

bool get encrypted {
return persistorSettings?.encrypted ??
Loon.persistorSettings?.encrypted ??
false;
}

/// Returns the serialized document data.
dynamic getSerialized() {
final data = get()?.data;
Expand Down
22 changes: 5 additions & 17 deletions lib/loon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ library loon;
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart' hide Key;
import 'package:loon/persistor/data_store_encrypter.dart';
import 'package:loon/persistor/file_persistor/file_persistor.dart';
import 'package:loon/persistor/indexed_db_persistor/indexed_db_persistor.dart';
import 'package:uuid/uuid.dart';
Expand Down Expand Up @@ -38,14 +39,9 @@ part 'extensions/iterable.dart';
class Loon {
static final Loon _instance = Loon._();

Loon._() {
_logger = Logger('Loon', output: (message) {
if (_isLoggingEnabled && kDebugMode) {
// ignore: avoid_print
print(message);
}
});
}
static final Logger logger = Logger('Loon');

Loon._();

/// The store of document snapshots indexed by document path.
final documentStore = ValueStore<DocumentSnapshot>();
Expand All @@ -56,10 +52,6 @@ class Loon {

PersistManager? persistManager;

late final Logger _logger;

bool _isLoggingEnabled = false;

bool get _isGlobalPersistenceEnabled {
return persistManager?.settings.enabled ?? false;
}
Expand Down Expand Up @@ -222,7 +214,7 @@ class Loon {
Persistor? persistor,
bool enableLogging = false,
}) {
_instance._isLoggingEnabled = enableLogging;
logger.enabled = enableLogging;

if (persistor != null) {
_instance.persistManager = PersistManager(persistor: persistor);
Expand Down Expand Up @@ -318,10 +310,6 @@ class Loon {
_instance.broadcastManager.unsubscribe();
}

static Logger get logger {
return _instance._logger;
}

static PersistorSettings? get persistorSettings {
return _instance.persistManager?.settings;
}
Expand Down
22 changes: 15 additions & 7 deletions lib/persistor/data_store.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import 'package:loon/loon.dart';
import 'package:loon/persistor/data_store_encrypter.dart';

typedef DataStoreFactory = DataStore Function(String name, bool encrypted);
typedef DataStoreFactory = DataStore Function(
String name,
bool encrypted,
DataStoreEncrypter encrypter,
);

abstract class DataStoreConfig {
final String name;
Expand All @@ -17,8 +21,8 @@ abstract class DataStoreConfig {
required this.delete,
required bool encrypted,
required DataStoreEncrypter encrypter,
final Logger? logger,
}) : logger = logger ?? Logger('DataStore:$name');
required Logger logger,
}) : logger = logger.child('DataStore:$name');
}

class DataStore {
Expand Down Expand Up @@ -191,10 +195,14 @@ class DualDataStore {

DualDataStore(
this.name, {
required DataStore Function(String name, bool encrypted) factory,
}) : _plaintextStore = factory(name, false),
_encryptedStore =
factory('$name.${DataStoreEncrypter.encryptedName}', true);
required DataStoreFactory factory,
required DataStoreEncrypter encrypter,
}) : _plaintextStore = factory(name, false, encrypter),
_encryptedStore = factory(
'$name.${DataStoreEncrypter.encryptedName}',
true,
encrypter,
);

bool get isDirty {
return _plaintextStore.isDirty || _encryptedStore.isDirty;
Expand Down
34 changes: 25 additions & 9 deletions lib/persistor/data_store_encrypter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,52 @@ import 'package:encrypt/encrypt.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class DataStoreEncrypter {
late final Encrypter encrypter;
late final Encrypter _encrypter;

static const _secureStorageKey = 'loon_encrypted_file_persistor_key';
static const encryptedName = 'encrypted';

DataStoreEncrypter();
bool _isInitialized = false;

DataStoreEncrypter([Encrypter? encrypter]) {
if (encrypter != null) {
_encrypter = encrypter;
_isInitialized = true;
}
}

Future<void> init() async {
const storage = FlutterSecureStorage();
final base64Key = await storage.read(key: _secureStorageKey);
if (_isInitialized) {
return;
}

Key key;

const storage = FlutterSecureStorage();
final base64Key = await storage.read(key: _secureStorageKey);
if (base64Key != null) {
key = Key.fromBase64(base64Key);
} else {
key = Key.fromSecureRandom(32);
await storage.write(key: _secureStorageKey, value: key.base64);
}

encrypter = Encrypter(AES(key, mode: AESMode.cbc));
_encrypter = Encrypter(AES(key, mode: AESMode.cbc));
_isInitialized = true;
}

String encrypt(String plainText) {
Future<String> encrypt(String plainText) async {
await init();

final iv = IV.fromSecureRandom(16);
return iv.base64 + encrypter.encrypt(plainText, iv: iv).base64;
return iv.base64 + _encrypter.encrypt(plainText, iv: iv).base64;
}

String decrypt(String encrypted) {
Future<String> decrypt(String encrypted) async {
await init();

final iv = IV.fromBase64(encrypted.substring(0, 24));
return encrypter.decrypt64(
return _encrypter.decrypt64(
encrypted.substring(24),
iv: iv,
);
Expand Down
21 changes: 12 additions & 9 deletions lib/persistor/data_store_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:async';
import 'package:loon/loon.dart';
import 'package:loon/persistor/data_store.dart';
import 'package:loon/persistor/data_store_encrypter.dart';
import 'package:loon/persistor/data_store_persistence_payload.dart';
import 'package:loon/persistor/persist_payload.dart';
import 'package:loon/persistor/data_store_resolver.dart';
import 'package:loon/persistor/lock.dart';

Expand All @@ -15,8 +15,7 @@ class DataStoreManager {

final void Function()? onSync;

final void Function(String text) onLog;

final Logger logger;
final DataStoreFactory factory;

/// Persistors generally have more optimal ways of clearing all stores at once than just iterating
Expand All @@ -33,7 +32,7 @@ class DataStoreManager {
/// operation and conversely blocks a sync from starting until the ongoing operation holding the lock has finished.
final _syncLock = Lock();

late final _logger = Logger('DataStoreManager', output: onLog);
final DataStoreEncrypter encrypter;

/// The sync timer is used to throttle syncing changes to the file system using
/// the given [persistenceThrottle]. After an that mutates the file system operation runs, it schedules
Expand All @@ -51,15 +50,17 @@ class DataStoreManager {
DataStoreManager({
required this.persistenceThrottle,
required this.onSync,
required this.onLog,
required this.settings,
required this.factory,
required this.resolverConfig,
required this.encrypter,
required Future<void> Function() clearAll,
required Future<List<String>> Function() getAll,
required Logger logger,
}) : _clearAll = clearAll,
_getAll = getAll,
resolver = DataStoreResolver(resolverConfig);
logger = logger.child('DataStoreManager'),
resolver = DataStoreResolver(resolverConfig)..logger = logger;

void _cancelSync() {
_syncTimer?.cancel();
Expand All @@ -73,7 +74,7 @@ class DataStoreManager {
/// Syncs all data stores, persisting dirty ones and deleting ones that can now be removed.
Future<void> _sync() {
return _syncLock.run(() {
return _logger.measure('Sync', () async {
return logger.measure('Sync', () async {
final dirtyStores =
index.values.where((dataStore) => dataStore.isDirty);

Expand Down Expand Up @@ -134,7 +135,7 @@ class DataStoreManager {
.toSet();

for (final name in indexNames) {
index[name] = DualDataStore(name, factory: factory);
index[name] = DualDataStore(name, factory: factory, encrypter: encrypter);
}

await resolver.hydrate();
Expand Down Expand Up @@ -186,7 +187,7 @@ class DataStoreManager {
);
}

Future<void> persist(DataStorePersistencePayload payload) {
Future<void> persist(PersistPayload payload) {
return _syncLock.run(() async {
final localResolver = payload.resolver;
final docs = payload.persistenceDocs;
Expand All @@ -208,10 +209,12 @@ class DataStoreManager {
final prevDataStore = index[prevDataStoreName] ??= DualDataStore(
prevDataStoreName,
factory: factory,
encrypter: encrypter,
);
final nextDataStore = index[nextDataStoreName] ??= DualDataStore(
nextDataStoreName,
factory: factory,
encrypter: encrypter,
);

dataStores.add(prevDataStore);
Expand Down
Loading

0 comments on commit 97b90a7

Please sign in to comment.