⚠️ Project Renamed: This project was previously
net.clojars.bru/vector-clock. That artifact is deprecated. Please migrate tonet.clojars.bru/version-vector.
Version vector implementation for Clojure and ClojureScript.
This library provides version vectors for building distributed systems in both Clojure (JVM) and ClojureScript.
Version Vectors - A mechanism for determining causality and detecting conflicts in distributed systems.
This implementation uses a map where keys represent devices in a distributed db, and values represent the version last contributed by that device.
Add to your deps.edn:
{:deps {net.clojars.bru/version-vector {:mvn/version "0.2.0"}}}
This library is written in .cljc (cross-platform Clojure) and works in both:
The core algorithm uses only platform-agnostic Clojure functions with no dependencies.
Testing: Tests run on Clojure only. Since the implementation is 100% platform-agnostic with no JavaScript interop or ClojureScript-specific features, Clojure testing should provide sufficient coverage.
(ns my-app.sync
(:require [version-vector.core :as vv]))
;; Create a clock for this device
(def my-clock (vv/make-clock "device-1"))
;; Increment the clock when making changes
(def updated-clock (vv/tick my-clock "device-1"))
; => {"device-1" 1}
;; Compare with another device's clock
(def other-clock {"device-2" 1})
(vv/check updated-clock other-clock)
; => :concurrent (both devices made independent changes)
;; Merge clocks when syncing
(def merged (vv/merge-clocks updated-clock other-clock))
; => {"device-1" 1 "device-2" 1}
make-clock(make-clock device-id)
Create a new vector clock for the given device ID.
tick(tick clock device-id)
Increment the counter for the specified device. Returns a new clock.
check(check this-clock other-clock)
Compare two clocks and return their causal relationship:
:same - Clocks are identical:ancestor - this-clock causally precedes other-clock:descendant - this-clock causally follows other-clock:concurrent - Neither causally precedes the other (conflict!)merge-clocks(merge-clocks clock1 clock2)
Merge two clocks by taking the maximum value for each device. Used when synchronizing state across devices.
(require '[version-vector.core :as vv])
;; Device 1 makes a change
(def device1-clock (vv/tick (vv/make-clock "d1") "d1"))
; => {"d1" 1}
;; Device 2 makes an independent change
(def device2-clock (vv/tick (vv/make-clock "d2") "d2"))
; => {"d2" 1}
;; Check for conflicts
(vv/check device1-clock device2-clock)
; => :concurrent (conflict detected!)
Example: distributed note taking app
(defrecord Note [id content clock])
(defn update-note [note new-content device-id]
(->Note (:id note)
new-content
(vv/tick (:clock note) device-id)))
(defn merge-notes [note1 note2]
(case (vv/check (:clock note1) (:clock note2))
(:same :ancestor) note2 ; Keep newer version
:descendant note1 ; Keep newer version
:concurrent ; Conflict - needs resolution strategy
(->Note (:id note1)
(str (:content note1) "\n---CONFLICT---\n" (:content note2))
(vv/merge-clocks (:clock note1) (:clock note2)))))
Run tests on Clojure:
clojure -X:test
All tests run on the JVM. The library works in ClojureScript projects but is only tested on Clojure since the implementation is platform-agnostic.
Copyright © 2025
Distributed under the Eclipse Public License version 1.0.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |