persisted_clock.go

  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}