persisted_clock.go

  1package lamport
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"io"
  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
 81	content, err := io.ReadAll(f)
 82	if err != nil {
 83		_ = f.Close()
 84		return err
 85	}
 86
 87	err = f.Close()
 88	if err != nil {
 89		return err
 90	}
 91
 92	var value uint64
 93	n, err := fmt.Sscanf(string(content), "%d", &value)
 94	if err != nil {
 95		return err
 96	}
 97
 98	if n != 1 {
 99		return fmt.Errorf("could not read the clock")
100	}
101
102	pc.MemClock = NewMemClockWithTime(value)
103
104	return nil
105}
106
107func (pc *PersistedClock) Write() error {
108	data := []byte(fmt.Sprintf("%d", pc.counter))
109	return util.WriteFile(pc.root, pc.filePath, data, 0644)
110}