Skip to content

Commit

Permalink
Merge pull request #94 from segmentio/MichaelGH/syncfilewrites
Browse files Browse the repository at this point in the history
Removing async code from file writes
  • Loading branch information
MichaelGHSeg authored Aug 14, 2024
2 parents e9c9cc3 + 4c7b470 commit 7a8a1bf
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 27 deletions.
41 changes: 22 additions & 19 deletions packages/core/lib/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -165,23 +165,34 @@ abstract class PersistedState<T> implements AsyncStateNotifier<T> {
if (_persistance != null) {
_hasUpdated = true;
} else {
_persistance = storageJson
_persistance = storageJson
? _store
.setPersisted(_key, toJson(state))
.whenComplete(_whenPersistenceComplete)
.setPersisted(_key, toJson(state))
.whenComplete(_whenPersistenceComplete)
: null;
}
});
_store.ready.then<void>((_) async {
final rawV = await _store.getPersisted(_key);
Map<String, dynamic>? rawV;
try {
rawV = await _store.getPersisted(_key);
} on FormatException catch (e) {
// Addressing https://github.com/segmentio/analytics_flutter/issues/74
// File corruption should be less likely with removal of async code in writes
// Existing corrupted files are cleaned up here without failing initialization
_store.setPersisted(_key, {});
log("Clean file $_key with format error", kind: LogFilterKind.warning);
final wrappedError = ErrorLoadingStorage(e);
errorHandler(wrappedError);
}
T v;

if (rawV == null) {
final init = await _initialiser();
_persistance = storageJson
_persistance = storageJson
? _store
.setPersisted(_key, toJson(init))
.whenComplete(_whenPersistenceComplete)
.setPersisted(_key, toJson(init))
.whenComplete(_whenPersistenceComplete)
: null;
_notifier.nonNullState = init;
v = init;
Expand All @@ -201,16 +212,9 @@ abstract class PersistedState<T> implements AsyncStateNotifier<T> {
return;
}).catchError((e) {
_error = e;
// Clean file if exist a format error
if(_error.toString().contains("FormatException")) {
_store.setPersisted(_key, {});
log("Clean file $_key with format error",
kind: LogFilterKind.warning);
} else {
final wrappedError = ErrorLoadingStorage(e);
errorHandler(wrappedError);
throw wrappedError;
}
final wrappedError = ErrorLoadingStorage(e);
errorHandler(wrappedError);
throw wrappedError;
});
}

Expand Down Expand Up @@ -552,8 +556,7 @@ class Configuration {
this.debug = false,
this.maxBatchSize,
this.storageJson = true,
this.token
});
this.token});
}

typedef ErrorHandler = void Function(Exception);
Expand Down
20 changes: 12 additions & 8 deletions packages/core/lib/utils/store/io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,26 @@ class StoreImpl with Store {
final serialized = json.encode(data);
final buffer = utf8.encode(serialized);

file = await file.lock();
file = await file.setPosition(0);
file = await file.writeFrom(buffer);
file = await file.truncate(buffer.length);
await file.unlock();
file.lockSync(FileLock.blockingExclusive);
file.setPositionSync(0);
file.writeFromSync(buffer);
file.truncateSync(buffer.length);
file.unlockSync();
file.closeSync();
}

Future<Map<String, dynamic>?> _readFile(String fileKey) async {
RandomAccessFile? file = await _getFile(fileKey);
if (file == null) {
return null;
}
final length = await file.length();
file = await file.setPosition(0);
file = await file.lock(FileLock.blockingShared);
final length = file.lengthSync();
file.setPositionSync(0);
final buffer = Uint8List(length);
await file.readInto(buffer);
file.readIntoSync(buffer);
file.unlockSync();
file.closeSync();
final contentText = utf8.decode(buffer);
if (contentText == "{}") {
return null; // Prefer null to empty map, because we'll want to initialise a valid empty value.
Expand Down

0 comments on commit 7a8a1bf

Please sign in to comment.