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