1package lamport
2
3import (
4 "errors"
5 "fmt"
6 "io/ioutil"
7 "os"
8
9 "github.com/go-git/go-billy/v5"
10 "github.com/go-git/go-billy/v5/util"
11)
12
13var ErrClockNotExist = errors.New("clock doesn't exist")
14
15type PersistedClock struct {
16 *MemClock
17 root billy.Filesystem
18 filePath string
19}
20
21// NewPersistedClock create a new persisted Lamport clock
22func NewPersistedClock(root billy.Filesystem, filePath string) (*PersistedClock, error) {
23 clock := &PersistedClock{
24 MemClock: NewMemClock(),
25 root: root,
26 filePath: filePath,
27 }
28
29 err := clock.Write()
30 if err != nil {
31 return nil, err
32 }
33
34 return clock, nil
35}
36
37// LoadPersistedClock load a persisted Lamport clock from a file
38func LoadPersistedClock(root billy.Filesystem, filePath string) (*PersistedClock, error) {
39 clock := &PersistedClock{
40 root: root,
41 filePath: filePath,
42 }
43
44 err := clock.read()
45 if err != nil {
46 return nil, err
47 }
48
49 return clock, nil
50}
51
52// Increment is used to return the value of the lamport clock and increment it afterwards
53func (pc *PersistedClock) Increment() (Time, error) {
54 time, err := pc.MemClock.Increment()
55 if err != nil {
56 return 0, err
57 }
58 return time, pc.Write()
59}
60
61// Witness is called to update our local clock if necessary after
62// witnessing a clock value received from another process
63func (pc *PersistedClock) Witness(time Time) error {
64 // TODO: rework so that we write only when the clock was actually updated
65 err := pc.MemClock.Witness(time)
66 if err != nil {
67 return err
68 }
69 return pc.Write()
70}
71
72func (pc *PersistedClock) read() error {
73 f, err := pc.root.Open(pc.filePath)
74 if os.IsNotExist(err) {
75 return ErrClockNotExist
76 }
77 if err != nil {
78 return err
79 }
80 defer f.Close()
81
82 content, err := ioutil.ReadAll(f)
83 if err != nil {
84 return err
85 }
86
87 var value uint64
88 n, err := fmt.Sscanf(string(content), "%d", &value)
89 if err != nil {
90 return err
91 }
92
93 if n != 1 {
94 return fmt.Errorf("could not read the clock")
95 }
96
97 pc.MemClock = NewMemClockWithTime(value)
98
99 return nil
100}
101
102func (pc *PersistedClock) Write() error {
103 data := []byte(fmt.Sprintf("%d", pc.counter))
104 return util.WriteFile(pc.root, pc.filePath, data, 0644)
105}