-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
83 lines (69 loc) · 3.47 KB
/
Copy pathmain.go
File metadata and controls
83 lines (69 loc) · 3.47 KB
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package main
import (
"fmt"
"github.com/iamseth/yin"
)
func main() {
// This example shows the delta path: instead of sending an entire document,
// a target replica sends a VersionVector cursor that says "this is what I
// already know", and the source returns the missing retained state.
//
// For LWWRegister, the delta is not a historical patch log. The register only
// retains the current winning value, so its catch-up delta carries that winner
// plus the timestamp that proves why it wins.
sourceReplica := yin.ReplicaID("replica-a")
targetReplica := yin.ReplicaID("replica-b")
source := yin.NewLWWRegister[string](sourceReplica)
// targetReplica would be used if target made its own local Set calls. In this
// walkthrough target only receives source's writes, so target's own counter
// stays at 0 even after it catches up.
target := yin.NewLWWRegister[string](targetReplica)
// Step 1: source creates the first value. This write is timestamped as
// replica-a counter 1.
source.Set("retained winner v1")
// Step 2: target starts empty, so its VersionVector is empty. Asking the
// source for everything since that empty cursor returns the current winner.
bootstrapDelta := source.ExtractDelta(target.Version())
if bootstrapDelta == nil {
panic("expected bootstrap delta for empty target")
}
if changed := target.ApplyDelta(bootstrapDelta); !changed {
panic("expected bootstrap delta to change target")
}
// The target now remembers a sync cursor. In a real sync layer this cursor is
// the kind of value you would keep per peer so the next request can ask only
// for what changed after this point.
targetCursor := target.Version()
// Step 3: source changes while target is behind. This advances source's
// Lamport counter to 2 and replaces the retained winner.
source.Set("retained winner v2")
// Step 4: target asks source for a catch-up delta from the saved cursor. The
// cursor knows replica-a counter 1, but source is now at replica-a counter 2,
// so ExtractDelta returns the retained winner v2.
delta := source.ExtractDelta(targetCursor)
if delta == nil {
panic("expected catch-up delta for target cursor behind source")
}
// ApplyDelta has the same idempotent changed/no-op signal as Merge. The first
// apply should advance target; applying the same delta again should do nothing.
firstChanged := target.ApplyDelta(delta)
duplicateChanged := target.ApplyDelta(delta)
sourceVersion := source.Version()
targetVersion := target.Version()
deltaVersion := delta.Version()
fmt.Println("scope: retained LWW winner catch-up")
fmt.Printf("target cursor for source before catch-up: %d\n", targetCursor.Get(sourceReplica))
fmt.Printf("delta kind: %s\n", delta.Kind())
fmt.Printf("delta version for source: %d\n", deltaVersion.Get(sourceReplica))
fmt.Printf("first ApplyDelta changed=%t\n", firstChanged)
fmt.Printf("source value: %s\n", source.Value())
fmt.Printf("target value: %s\n", target.Value())
fmt.Printf("source version for source: %d\n", sourceVersion.Get(sourceReplica))
fmt.Printf("target version for source: %d\n", targetVersion.Get(sourceReplica))
fmt.Printf("target version for its own replica: %d\n", targetVersion.Get(targetReplica))
// Equal is true because source and target now hold the same winning value and
// timestamp. Their local ReplicaIDs may differ; those ids only stamp future
// local Set calls.
fmt.Printf("target equals source: %t\n", target.Equal(source))
fmt.Printf("duplicate ApplyDelta changed=%t\n", duplicateChanged)
}