-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This version introduces a major refactor which results in multiple breaking changes. This was done with the intention to make this package the basis for a family of CRDT libraries. Another motivation was to make this package compatible with [crdt_sync](https://github.com/cachapa/crdt_sync), thereby abstracting the communication protocol and network management for real-time remote synchronization. Changes: - Simplified API - Removed insert and get operations to make package more storage-agnostic - Made most methods optionally async - Re-implemented CrdtMap as a zero-effort ephemeral implementation
- Loading branch information
Showing
15 changed files
with
717 additions
and
664 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,22 @@ | ||
import 'package:crdt/crdt.dart'; | ||
import 'package:crdt/src/map_crdt/map_crdt.dart'; | ||
|
||
void main() { | ||
var crdt = MapCrdt('node_id'); | ||
var crdt1 = MapCrdt(['table']); | ||
var crdt2 = MapCrdt(['table']); | ||
|
||
// Insert a record | ||
crdt.put('a', 1); | ||
// Read the record | ||
print('Record: ${crdt.get('a')}'); | ||
print('Inserting 2 records in crdt1…'); | ||
crdt1.put('table', 'a', 1); | ||
crdt1.put('table', 'b', 1); | ||
|
||
// Export the CRDT as Json | ||
final json = crdt.toJson(); | ||
// Send to remote node | ||
final remoteJson = sendToRemote(json); | ||
// Merge remote CRDT with local | ||
crdt.mergeJson(remoteJson); | ||
// Verify updated record | ||
print('Record after merging: ${crdt.get('a')}'); | ||
} | ||
print('crdt1: ${crdt1.getMap('table')}'); | ||
|
||
print('\nInserting a conflicting record in crdt2…'); | ||
crdt2.put('table', 'a', 2); | ||
|
||
print('crdt2: ${crdt2.getMap('table')}'); | ||
|
||
print('\nMerging crdt2 into crdt1…'); | ||
crdt1.merge(crdt2.getChangeset()); | ||
|
||
// Mock sending the CRDT to a remote node and getting an updated one back | ||
String sendToRemote(String json) { | ||
final hlc = Hlc.now('another_nodeId'); | ||
return '{"a":{"hlc":"$hlc","value":2}}'; | ||
print('crdt1: ${crdt1.getMap('table')}'); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,5 @@ | ||
library crdt; | ||
|
||
export 'src/crdt.dart'; | ||
export 'src/crdt_json.dart'; | ||
export 'src/hlc.dart'; | ||
export 'src/map_crdt.dart'; | ||
export 'src/record.dart'; | ||
export 'src/types.dart'; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,5 @@ | ||
import 'dart:async'; | ||
library map_crdt; | ||
|
||
import 'crdt.dart'; | ||
import 'hlc.dart'; | ||
import 'record.dart'; | ||
|
||
/// A CRDT backed by a in-memory map. | ||
/// Useful for testing, or for applications which only require temporary datasets. | ||
class MapCrdt<K, V> extends Crdt<K, V> { | ||
final _map = <K, Record<V>>{}; | ||
final _controller = StreamController<MapEntry<K, V?>>.broadcast(); | ||
|
||
@override | ||
final String nodeId; | ||
|
||
MapCrdt(this.nodeId, [Map<K, Record<V>> seed = const {}]) { | ||
_map.addAll(seed); | ||
} | ||
|
||
@override | ||
bool containsKey(K key) => _map.containsKey(key); | ||
|
||
@override | ||
Record<V>? getRecord(K key) => _map[key]; | ||
|
||
@override | ||
void putRecord(K key, Record<V> value) { | ||
_map[key] = value; | ||
_controller.add(MapEntry(key, value.value)); | ||
} | ||
|
||
@override | ||
void putRecords(Map<K, Record<V>> recordMap) { | ||
_map.addAll(recordMap); | ||
recordMap | ||
.map((key, value) => MapEntry(key, value.value)) | ||
.entries | ||
.forEach(_controller.add); | ||
} | ||
|
||
@override | ||
Map<K, Record<V>> recordMap({Hlc? modifiedSince}) => | ||
Map<K, Record<V>>.from(_map) | ||
..removeWhere((_, record) => | ||
record.modified.logicalTime < (modifiedSince?.logicalTime ?? 0)); | ||
|
||
@override | ||
Stream<MapEntry<K, V?>> watch({K? key}) => | ||
_controller.stream.where((event) => key == null || key == event.key); | ||
|
||
@override | ||
void purge() => _map.clear(); | ||
} | ||
export 'src/map_crdt/map_crdt.dart'; | ||
export 'src/map_crdt/map_crdt_base.dart'; | ||
export 'src/map_crdt/record.dart'; |
Oops, something went wrong.