This repository has been archived by the owner on Sep 30, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
/
G-Counter.js
64 lines (52 loc) · 1.48 KB
/
G-Counter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import { deepEqual } from './utils.js'
const sum = (acc, val) => acc + val
/**
* G-Counter
*
* Operation-based Increment-Only Counter CRDT
*
* Sources:
* "A comprehensive study of Convergent and Commutative Replicated Data Types"
* http://hal.upmc.fr/inria-00555588/document, "3.1.1 Op-based counter and 3.1.2 State-based increment-only Counter (G-Counter)"
*/
export default class GCounter {
constructor (id, counter) {
this.id = id
this._counters = counter ? counter : {}
this._counters[this.id] = this._counters[this.id] ? this._counters[this.id] : 0
}
get value () {
return Object.values(this._counters).reduce(sum, 0)
}
increment (amount) {
if (amount && amount < 1)
return
if (amount === undefined || amount === null)
amount = 1
this._counters[this.id] = this._counters[this.id] + amount
}
merge (other) {
// Go through each counter in the other counter
Object.entries(other._counters).forEach(([id, value]) => {
// Take the maximum of the counter value we have or the counter value they have
this._counters[id] = Math.max(this._counters[id] || 0, value)
})
}
toJSON () {
return {
id: this.id,
counters: this._counters
}
}
isEqual (other) {
return GCounter.isEqual(this, other)
}
static from (json) {
return new GCounter(json.id, json.counters)
}
static isEqual (a, b) {
if(a.id !== b.id)
return false
return deepEqual(a._counters, b._counters)
}
}