{"id":20710309,"url":"https://github.com/kavirajk/clock","last_synced_at":"2025-04-23T05:17:35.877Z","repository":{"id":62442442,"uuid":"217360171","full_name":"kavirajk/clock","owner":"kavirajk","description":"Logical clocks implementation in Rust","archived":false,"fork":false,"pushed_at":"2019-10-26T02:02:35.000Z","size":13,"stargazers_count":44,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-23T05:17:29.571Z","etag":null,"topics":["clock","distributed-systems","dotted-version-vector","rust","vector-clock","version-vector"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kavirajk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-24T17:51:14.000Z","updated_at":"2024-07-10T18:03:02.000Z","dependencies_parsed_at":"2022-11-01T22:15:29.174Z","dependency_job_id":null,"html_url":"https://github.com/kavirajk/clock","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavirajk%2Fclock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavirajk%2Fclock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavirajk%2Fclock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kavirajk%2Fclock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kavirajk","download_url":"https://codeload.github.com/kavirajk/clock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250372945,"owners_count":21419724,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clock","distributed-systems","dotted-version-vector","rust","vector-clock","version-vector"],"created_at":"2024-11-17T02:11:07.207Z","updated_at":"2025-04-23T05:17:35.848Z","avatar_url":"https://github.com/kavirajk.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Logical clocks\n\n[![Package version](https://img.shields.io/crates/v/logical_clock.svg)](https://crates.io/crates/logical_clock)\n[![Package docs](https://docs.rs/logical_clock/badge.svg)](https://docs.rs/logical_clock)\n[![License](https://img.shields.io/badge/license-MIT%20License-blue.svg)](https://github.com/kavirajk/clock/blob/master/LICENSE)\n\n\n`clocks` implements some of the modern logical clocks (vector clocks and dotted version vector).\n\nA logical clock is a mechanism for capturing chronological and causal relationships(cause and effect, Event A caused event B, also called as *happened-before* relation) in a distributed system.\n\nGiven any two events across multiple nodes in the distributed system, logical clocks help in answering queries like \"Does event A *happened-before* B\" or \"Is event B *concurrent* to event A\"\n\nImplementation of dotted version vector is based on the paper [Scalable and Accurate Causality Tracking for Eventually Consistent Stores](https://haslab.uminho.pt/tome/files/dvvset-dais.pdf)\n\n## Vector clocks vs Version Vectors\nAlthough they both have same data structure representation, they solve different problems.\n\nVector clocks are used to partial order between any two events in the distributed systems, where as Version Vectors are used to partial order only events that changes datum(say you want to keep multiple versions of same key that are updated concurrently).\n\nFor more details about the differences, there good article [here](https://haslab.wordpress.com/2011/07/08/version-vectors-are-not-vector-clocks/)\n\n## Usage (A simple Key Value Store, simulating multiple clients)\n```rust\nuse logical_clock::{VersionVector, Dot};\nuse std::collections::HashMap;\n\ntype Key = String;\n\n#[derive(Clone, Debug)]\nstruct Value{\n    val:i64,\n    dot:Dot\n}\n\nstruct KVStore {\n    store:HashMap\u003cKey, Vec\u003cValue\u003e\u003e,\n    vv:VersionVector,\n\t\n}\n\nimpl KVStore {\n    fn new() -\u003e KVStore {\n\tKVStore{\n\t    store: HashMap::new(),\n\t    vv: VersionVector::new(),\n\t}\n    }\n\n    fn get(\u0026self, key: \u0026str) -\u003e (Option\u003cVec\u003cValue\u003e\u003e, VersionVector) {\n\tmatch self.store.get(key) {\n\t    None =\u003e (None, self.vv.clone()),\n\t    Some(v) =\u003e (Some(v.clone()), self.vv.clone())\n\t}\n    }\n\n    fn set(mut self, client_id:\u0026str, context: \u0026VersionVector, key: \u0026str, val: i64) -\u003e Self{\n\t// if incoming request context descends from local clock, just overwrite.\n\tif context.descends(\u0026self.vv) {\n\t    self.vv = self.vv.inc(client_id);\n\t    let dot = self.vv.get_dot(client_id);\n\t    let new_obj = Value{val: val, dot: dot};\n\t    \n\t    // overwrite all the siblings\n\t    self.store.insert(key.to_string(), vec![new_obj]);\n\t    return self\n\t}\n\n\tlet mut frontier = self.vv.merge(\u0026context);\n\tfrontier = frontier.inc(client_id);\n\tlet dot = frontier.get_dot(client_id);\n\tlet new_obj = Value{val: val, dot: dot};\n\tself.vv = frontier;\n\treturn self.merge_siblings(key, new_obj)\n    }\n\n    fn merge_siblings(mut self, key: \u0026str, new_val: Value) -\u003e Self{\n\t// replace values that dominated by given value's dot\n\tlet (old, _) = self.get(key);\n\n\tmatch old {\n\t    None =\u003e {\n\t\tself.store.insert(key.to_string(), vec![new_val]);\n\t\treturn self\n\t    },\n\t    Some(values) =\u003e {\n\t\tlet mut updated = Vec::new();\n\t\tfor v in values {\n\t\t    if new_val.dot.descends(\u0026v.dot) {\n\t\t\tcontinue;\n\t\t    }\n\t\t    updated.push(v);\n\t\t}\n\t\tupdated.push(new_val);\n\t\tself.store.insert(key.to_string(), updated);\n\t\treturn self\n\t    }\n\t}\n    }\n}\n\nfn main() {\n    let mut kv = KVStore::new();\n\n    // always get before put - Semantics followed in any High Available Key value store\n\n    // Client A and Client B\n    let (_, ctx_a) = kv.get(\"x\");\n    let (_, ctx_b) = kv.get(\"x\");\n\n    \n    kv = kv.set(\"A\", \u0026ctx_a, \"x\", 10); // A try to write x=10 with empty context\n    kv = kv.set(\"B\", \u0026ctx_b, \"x\", 15); // B try to write x=12 with same empty context\n\n    // both are concurrent from the client views, so both values should be kept\n    assert_eq!(2, kv.store[\"x\"].len());\n\n    // Client C comes in.\n    let (_, ctx_c) = kv.get(\"x\");\n    // now client C knows all the causal contex, so it replaces the key with all causal past.\n    kv = kv.set(\"C\", \u0026ctx_c, \"x\", 20);\n    assert_eq!(1, kv.store[\"x\"].len());\n    \n    // now B set with old empty context.\n    kv = kv.set(\"B\", \u0026ctx_b, \"x\", 30); // I know contex is empty just set it as 30.\n\n    // From client views latest B write is concurrent to C latest write. so both should be kept.\n    assert_eq!(2, kv.store[\"x\"].len());\n    \n    for (k, v) in kv.store {\n\tprintln!(\"key: {}, values: {:?}\", k, v)\n\t    // val: {}, dot: {:?}\", k, v, v.dot);\n    }\n    println!(\"vv: {:?}\", kv.vv);\n\n}\n\n```\n\n## Logical clocks in Real time\n1. Go race detector uses vector clocks to detect data race between go routines. Basic idea is every go routine have its own vector clock and when a shared memory is accessed by multiple goroutines, their vector clocks are compared to find if they are concurrent!\nhttps://www.slideshare.net/InfoQ/looking-inside-a-race-detector\n\n2. Riak Key Value store uses Dotted Version Vector to track concurrent versions of same key in multiple replica.\nhttps://riak.com/posts/technical/vector-clocks-revisited-part-2-dotted-version-vectors/index.html?p=9929.html\n\n## References\n1. https://haslab.uminho.pt/tome/files/dvvset-dais.pdf\n2. https://github.com/ricardobcl/Dotted-Version-Vectors\n3. https://lamport.azurewebsites.net/pubs/time-clocks.pdf\n\n## Licence\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkavirajk%2Fclock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkavirajk%2Fclock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkavirajk%2Fclock/lists"}