Last active
January 26, 2024 13:34
-
-
Save sma/acddf5dd33b5fcfbd2a239504ad05b29 to your computer and use it in GitHub Desktop.
A simple read-optimized in-memory key-value store w/persistent file-based redo log.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// A simple read-optimized in-memory key-value store w/persistent file-based redo log. | |
class KV { | |
KV._(this._file, this._data); | |
final File _file; | |
final Map<String, dynamic> _data; | |
static Future<KV> open(File file) async { | |
final data = <String, dynamic>{}; | |
if (!file.existsSync()) return KV._(file, data); | |
await for (final line in file // | |
.openRead() | |
.transform(utf8.decoder) | |
.transform(const LineSplitter()) | |
.where((line) => line.isNotEmpty) | |
.map(json.decode)) { | |
if (line case [String key, String value]) { | |
data[key] = value; | |
} else if (line case [String key]) { | |
data.remove(key); | |
} | |
} | |
return KV._(file, data); | |
} | |
Future<Object?> get(String key) async => _data[key]; | |
Future<Object?> set(String key, Object? value) async { | |
final old = _data[key]; | |
if (old == value) return value; | |
if (value != null) { | |
await _append([key, value]); | |
_data[key] = value; | |
} else { | |
await _append([key]); | |
_data.remove(key); | |
} | |
return old; | |
} | |
Stream<(String, Object)> list(String prefix) { | |
return Stream.fromIterable(_data.entries.where((e) => e.key.startsWith(prefix)).map((e) => (e.key, e.value))); | |
} | |
Future<void> snapshot() async { | |
final file = File('${_file.path},$hashCode'); | |
await file.writeAsString(_data.entries.map((e) => '${json.encode([e.key, e.value])}\n').join()); | |
await file.rename(_file.path); | |
} | |
Future<void> _append(List<Object> values) async { | |
final sink = _file.openWrite(mode: FileMode.append); | |
sink.writeln(json.encode(values)); | |
await sink.flush(); | |
await sink.close(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment