-
Notifications
You must be signed in to change notification settings - Fork 5.9k
/
saver.go
140 lines (124 loc) · 3.04 KB
/
saver.go
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package uuid
/****************
* Date: 21/06/15
* Time: 5:48 PM
***************/
import (
"encoding/gob"
"log"
"os"
"time"
)
func init() {
gob.Register(stateEntity{})
}
func SetupFileSystemStateSaver(pConfig StateSaverConfig) {
saver := &FileSystemSaver{}
saver.saveReport = pConfig.SaveReport
saver.saveSchedule = int64(pConfig.SaveSchedule)
SetupCustomStateSaver(saver)
}
// A wrapper for default setup of the FileSystemStateSaver
type StateSaverConfig struct {
// Print save log
SaveReport bool
// Save every x nanoseconds
SaveSchedule time.Duration
}
// *********************************************** StateEntity
// StateEntity acts as a marshaller struct for the state
type stateEntity struct {
Past Timestamp
Node []byte
Sequence uint16
}
// This implements the StateSaver interface for UUIDs
type FileSystemSaver struct {
cache *os.File
saveState uint64
saveReport bool
saveSchedule int64
}
// Saves the current state of the generator
// If the scheduled file save is reached then the file is synced
func (o *FileSystemSaver) Save(pState *State) {
if pState.past >= pState.next {
err := o.open()
defer o.cache.Close()
if err != nil {
log.Println("uuid.State.save:", err)
return
}
// do the save
o.encode(pState)
// a tick is 100 nano seconds
pState.next = pState.past + Timestamp(o.saveSchedule / 100)
if o.saveReport {
log.Printf("UUID STATE: SAVED %d", pState.past)
}
}
}
func (o *FileSystemSaver) Init(pState *State) {
pState.saver = o
err := o.open()
defer o.cache.Close()
if err != nil {
if os.IsNotExist(err) {
log.Printf("'%s' created\n", "uuid.SaveState")
var err error
o.cache, err = os.Create(os.TempDir() + "/state.unique")
if err != nil {
log.Println("uuid.State.init: SaveState error:", err)
goto pastInit
}
o.encode(pState)
} else {
log.Println("uuid.State.init: SaveState error:", err)
goto pastInit
}
}
err = o.decode(pState)
if err != nil {
goto pastInit
}
pState.randomSequence = false
pastInit:
if timestamp() <= pState.past {
pState.sequence++
}
pState.next = pState.past
}
func (o *FileSystemSaver) reset() {
o.cache.Seek(0, 0)
}
func (o *FileSystemSaver) open() error {
var err error
o.cache, err = os.OpenFile(os.TempDir()+"/state.unique", os.O_RDWR, os.ModeExclusive)
return err
}
// Encodes State generator data into a saved file
func (o *FileSystemSaver) encode(pState *State) {
// ensure reader state is ready for use
o.reset()
enc := gob.NewEncoder(o.cache)
// Wrap private State data into the StateEntity
err := enc.Encode(&stateEntity{pState.past, pState.node, pState.sequence})
if err != nil {
log.Panic("UUID.encode error:", err)
}
}
// Decodes StateEntity data into the main State
func (o *FileSystemSaver) decode(pState *State) error {
o.reset()
dec := gob.NewDecoder(o.cache)
entity := stateEntity{}
err := dec.Decode(&entity)
if err != nil {
log.Println("uuid.decode error:", err)
return err
}
pState.past = entity.Past
pState.node = entity.Node
pState.sequence = entity.Sequence
return nil
}