Skip to content

Commit

Permalink
refactor(CRDT): introduce CrdtNodeId type alias
Browse files Browse the repository at this point in the history
Replace ulong with CrdtNodeId for node identifiers throughout
the CRDT implementation. Update method signatures, comparisons,
and related logic to use the new type alias for clarity.
  • Loading branch information
sinkingsugar committed Oct 15, 2024
1 parent a909d53 commit 9dc5818
Showing 1 changed file with 21 additions and 18 deletions.
39 changes: 21 additions & 18 deletions CRDT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#nullable enable

using CrdtNodeId = ulong;

// Namespace for CRDT implementation
namespace ForSync.CRDT {
/// <summary>
Expand Down Expand Up @@ -52,10 +54,10 @@ public void SetTime(ulong t) {
public struct ColumnVersion {
public ulong ColVersion { get; set; }
public ulong DbVersion { get; set; }
public ulong NodeId { get; set; }
public CrdtNodeId NodeId { get; set; }
public ulong LocalDbVersion { get; set; }

public ColumnVersion(ulong c, ulong d, ulong n, ulong ldbVer = 0) {
public ColumnVersion(ulong c, ulong d, CrdtNodeId n, ulong ldbVer = 0) {
ColVersion = c;
DbVersion = d;
NodeId = n;
Expand Down Expand Up @@ -151,14 +153,15 @@ public class Change<K, V> {
public V? Value { get; set; } // null represents deletion of the column, not the record
public ulong ColVersion { get; set; }
public ulong DbVersion { get; set; }
public ulong NodeId { get; set; }
public CrdtNodeId NodeId { get; set; }
public ulong LocalDbVersion { get; set; }

public Change() {
RecordId = default!;
Value = default!;
}

public Change(K recordId, ColName<K>? colName, V? value, ulong colVersion, ulong dbVersion, ulong nodeId, ulong localDbVersion = 0) {
public Change(K recordId, ColName<K>? colName, V? value, ulong colVersion, ulong dbVersion, CrdtNodeId nodeId, ulong localDbVersion = 0) {
RecordId = recordId;
ColName = colName;
Value = value;
Expand Down Expand Up @@ -200,7 +203,7 @@ public int Compare(Change<K, V>? a, Change<K, V>? b) {
return a.DbVersion > b.DbVersion ? -1 : 1;

if (a.NodeId != b.NodeId)
return a.NodeId > b.NodeId ? -1 : 1;
return a.NodeId.CompareTo(b.NodeId);

return 0; // Consider equal if all fields match
}
Expand All @@ -210,19 +213,19 @@ public int Compare(Change<K, V>? a, Change<K, V>? b) {
/// Represents the CRDT structure, generic over key (K) and value (V) types.
/// </summary>
public class CRDT<K, V> {
private ulong _nodeId;
private CrdtNodeId _nodeId;
private LogicalClock _clock;
private Dictionary<K, Record<K, V>> _data;

Check warning on line 218 in CRDT.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows

The type 'K' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'K' doesn't match 'notnull' constraint.

Check warning on line 218 in CRDT.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu

The type 'K' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'K' doesn't match 'notnull' constraint.
private HashSet<K> _tombstones;

private CRDT<K, V>? _parent;
private ulong _baseVersion;

public ulong NodeId => _nodeId;
public CrdtNodeId NodeId => _nodeId;
public ulong Clock => _clock.CurrentTime;
public ulong BaseVersion => _baseVersion;

public CRDT(ulong nodeId, CRDT<K, V>? parent = null) {
public CRDT(CrdtNodeId nodeId, CRDT<K, V>? parent = null) {
_nodeId = nodeId;
_clock = new LogicalClock();
_data = new Dictionary<K, Record<K, V>>();

Check warning on line 231 in CRDT.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows

The type 'K' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'K' doesn't match 'notnull' constraint.

Check warning on line 231 in CRDT.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu

The type 'K' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'K' doesn't match 'notnull' constraint.
Expand All @@ -245,7 +248,7 @@ public CRDT(ulong nodeId, CRDT<K, V>? parent = null) {
/// <summary>
/// Creates a CRDT from a list of changes (e.g., loaded from disk).
/// </summary>
public CRDT(ulong nodeId, List<Change<K, V>> changes) {
public CRDT(CrdtNodeId nodeId, List<Change<K, V>> changes) {
_nodeId = nodeId;
_clock = new LogicalClock();
_data = new Dictionary<K, Record<K, V>>();

Check warning on line 254 in CRDT.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows

The type 'K' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'K' doesn't match 'notnull' constraint.

Check warning on line 254 in CRDT.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu

The type 'K' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'K' doesn't match 'notnull' constraint.
Expand Down Expand Up @@ -278,7 +281,7 @@ public List<Change<K, V>> InvertChanges(List<Change<K, V>> changes, bool usePare

if (colName == null) {
// The change was a record deletion (tombstone)
Record<K, V>? recordPtr = useParent ? GetRecordPtr(recordId, true) : GetRecordPtr(recordId);
Record<K, V>? recordPtr = useParent ? GetRecord(recordId, true) : GetRecord(recordId);

if (recordPtr != null) {
// Restore all fields from the record
Expand All @@ -304,7 +307,7 @@ public List<Change<K, V>> InvertChanges(List<Change<K, V>> changes, bool usePare
} else {
// The change was an insertion or update of a column
ColName<K> col = colName.Value;
Record<K, V>? recordPtr = useParent ? GetRecordPtr(recordId, true) : GetRecordPtr(recordId);
Record<K, V>? recordPtr = useParent ? GetRecord(recordId, true) : GetRecord(recordId);

if (recordPtr != null) {
if (recordPtr.Fields.TryGetValue(col, out V existingValue)) {

Check warning on line 313 in CRDT.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows

Converting null literal or possible null value to non-nullable type.

Check warning on line 313 in CRDT.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu

Converting null literal or possible null value to non-nullable type.
Expand Down Expand Up @@ -497,14 +500,14 @@ public List<Change<K, V>> MergeChanges(List<Change<K, V>> changes, bool ignorePa
ColName<K>? colName = change.ColName;
ulong remoteColVersion = change.ColVersion;
ulong remoteDbVersion = change.DbVersion;
ulong remoteNodeId = change.NodeId;
CrdtNodeId remoteNodeId = change.NodeId;
V? remoteValue = change.Value;

// Update the logical clock
ulong newLocalDbVersion = _clock.Update(remoteDbVersion);

// Retrieve local column version information
Record<K, V>? recordPtr = GetRecordPtr(recordId, ignoreParent);
Record<K, V>? recordPtr = GetRecord(recordId, ignoreParent);
ColumnVersion? localColInfo = null;

if (recordPtr != null) {
Expand Down Expand Up @@ -535,7 +538,7 @@ public List<Change<K, V>> MergeChanges(List<Change<K, V>> changes, bool ignorePa
shouldAccept = false;
} else {
// db_version is equal; use node_id for final tiebreaking
shouldAccept = remoteNodeId > localColInfo.Value.NodeId;
shouldAccept = remoteNodeId.CompareTo(localColInfo.Value.NodeId) > 0;
}
}
}
Expand Down Expand Up @@ -704,7 +707,7 @@ private void ApplyChanges(List<Change<K, V>> changes) {
ColName<K>? colName = change.ColName;
ulong remoteColVersion = change.ColVersion;
ulong remoteDbVersion = change.DbVersion;
ulong remoteNodeId = change.NodeId;
CrdtNodeId remoteNodeId = change.NodeId;
ulong remoteLocalDbVersion = change.LocalDbVersion;
V? remoteValue = change.Value;

Expand Down Expand Up @@ -757,12 +760,12 @@ private bool IsRecordTombstoned(K recordId, bool ignoreParent = false) {
/// <summary>
/// Retrieves a record pointer.
/// </summary>
private Record<K, V>? GetRecordPtr(K recordId, bool ignoreParent = false) {
public Record<K, V>? GetRecord(K recordId, bool ignoreParent = false) {
if (_data.TryGetValue(recordId, out Record<K, V>? record))
return record;

if (_parent != null && !ignoreParent)
return _parent.GetRecordPtr(recordId);
return _parent.GetRecord(recordId);

return null;
}
Expand All @@ -775,7 +778,7 @@ private Record<K, V> GetOrCreateRecordUnchecked(K recordId, bool ignoreParent =
_data[recordId] = new Record<K, V>();

if (_parent != null && !ignoreParent) {
Record<K, V>? parentRecord = _parent.GetRecordPtr(recordId);
Record<K, V>? parentRecord = _parent.GetRecord(recordId);
if (parentRecord != null)
_data[recordId] = new Record<K, V>(new Dictionary<ColName<K>, V>(parentRecord.Fields),
new Dictionary<ColName<K>, ColumnVersion>(parentRecord.ColumnVersions));
Expand Down

0 comments on commit 9dc5818

Please sign in to comment.