crdt-lite
1. Rust implementation of a generic CRDT.
This implementation includes:
- Logical Clock: To manage causality.
- CRDT Structure: Generic over key (
K) and value (V) types. - Record Management: Handling inserts, updates, deletes with tombstones.
- Conflict Resolution: Based on column versions, site IDs, and sequence numbers.
- Merging Mechanism: To synchronize state across nodes.
- Comprehensive Tests: Using Rust's
#[test]framework to ensure correctness.
2. Detailed Explanation
2.1. LogicalClock
The LogicalClock maintains a monotonically increasing counter to preserve causality across events.
- tick(): Increments the clock for each local event.
- update(): Updates the clock based on incoming changes to maintain causality.
- current_time(): Retrieves the current clock time.
2.2. ColumnVersion
ColumnVersion tracks the versioning information for each column within a record, enabling fine-grained conflict resolution.
- col_version: Number of times the column has been updated.
- db_version: Logical clock time when the column was last updated.
- site_id: Identifier of the node where the update originated.
- seq: Sequence number for ordering updates from the same node.
2.3. Record
Record represents an individual record in the CRDT, containing field values and their corresponding version information.
2.4. CRDT
The CRDT struct manages the overall state, including data records, tombstones for deletions, and methods to manipulate and merge data.
- node_id: Unique identifier for the node (site).
- clock: Instance of
LogicalClock. - data: Stores records, keyed by their unique identifier.
- tombstones: Tracks deleted records to prevent resurrection.
2.5. Methods
2.5.1. insert
Inserts a new record if it's not already tombstoned.
- Checks: Prevents re-insertion of tombstoned records.
- Initialization: Sets
col_versionto 1 for all columns.
2.5.2. update
Updates existing record fields if the record isn't tombstoned.
- Checks: Ignores updates on tombstoned or non-existent records.
- Versioning: Increments
col_versionandseqfor updated columns.
2.5.3. delete
Deletes a record by marking it as tombstoned.
- Checks: Prevents duplicate deletions.
- Tombstone: Inserts into
tombstonesand removes fromdata.
2.5.4. get_changes_since
Retrieves all changes since a specified last_db_version.
- Returns: A vector of
Changestructs representing the modifications.
2.5.5. merge_changes
Merges incoming changes into the CRDT, resolving conflicts based on versioning and site IDs.
- Conflict Resolution:
- Higher
col_version: Accepts the change. - Equal
col_version:- Deletion Prioritized: Deletions take precedence over insertions/updates.
- Site ID & Seq: If both changes are deletions, use
site_idandseqas tie-breakers.
- Higher
2.5.6. print_data
Prints the current state for debugging.
2.6. Change Struct
Represents a single change in the CRDT.
- record_id: Identifier of the record.
- col_name: Name of the column.
- value: New value for the column (None for deletions).
- col_version: Version of the column.
- db_version: Logical clock time of the change.
- site_id: Originating node's ID.
- seq: Sequence number for ordering.
2.7. Tests
Comprehensive tests ensure the correctness of the CRDT implementation. They cover scenarios like:
- Basic insertions and merges.
- Concurrent updates with conflict resolution.
- Deletions and tombstone handling.
- Preventing re-insertion after deletion.
- Logical clock updates.
Running Tests
-
Ensure Rust is Installed: If not, install it from rustup.rs.
-
Save the Code: Save the above code in a file named
crdt.rs. -
Create a New Cargo Project:
-
Replace
src/lib.rswithcrdt.rs: Alternatively, placecrdt.rsinsrc/and adjust accordingly. -
Add Dependencies: Add
uuidtoCargo.tomlfor generating unique identifiers.[] = { = "1.2", = ["v4"] } -
Run Tests:
All tests should pass, indicating that the CRDT behaves as expected.
License
This project is licensed under the MIT License - see the LICENSE file for details.