forked from gitea/gitea
141 lines
3.0 KiB
Go
141 lines
3.0 KiB
Go
|
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
|
||
|
}
|