checkpoint.go raw
1 package store
2
3 import (
4 "encoding/binary"
5 "hash/crc32"
6 "io"
7 "os"
8 )
9
10 // The checkpoint names the WAL position up to which every index has been made
11 // durable, plus the next pubkey serial. Recovery replays the WAL from pos.
12 //
13 // "TCK1" | pos uint64 | nextPubSer uint64 | crc32(first 20) uint32
14 const (
15 ckptMagic = "TCK1"
16 ckptSize = 24
17 ckptCRCOff = 20
18 )
19
20 type checkpoint struct {
21 f *os.File
22 pos uint64
23 nextPub uint64
24 }
25
26 func openCheckpoint(path string) (*checkpoint, error) {
27 f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o644)
28 if err != nil {
29 return nil, err
30 }
31 c := &checkpoint{f: f}
32 buf := make([]byte, ckptSize)
33 n, err := f.ReadAt(buf, 0)
34 if err != nil && err != io.EOF {
35 f.Close()
36 return nil, err
37 }
38 if n == ckptSize && string(buf[0:4]) == ckptMagic &&
39 crc32.ChecksumIEEE(buf[:ckptCRCOff]) == binary.BigEndian.Uint32(buf[ckptCRCOff:]) {
40 c.pos = binary.BigEndian.Uint64(buf[4:12])
41 c.nextPub = binary.BigEndian.Uint64(buf[12:20])
42 }
43 return c, nil
44 }
45
46 func (c *checkpoint) Get() (pos, nextPub uint64) { return c.pos, c.nextPub }
47
48 func (c *checkpoint) Set(pos, nextPub uint64) error {
49 buf := make([]byte, ckptSize)
50 copy(buf, ckptMagic)
51 binary.BigEndian.PutUint64(buf[4:12], pos)
52 binary.BigEndian.PutUint64(buf[12:20], nextPub)
53 binary.BigEndian.PutUint32(buf[ckptCRCOff:], crc32.ChecksumIEEE(buf[:ckptCRCOff]))
54 if _, err := c.f.WriteAt(buf, 0); err != nil {
55 return err
56 }
57 c.pos = pos
58 c.nextPub = nextPub
59 return nil
60 }
61
62 func (c *checkpoint) Sync() error { return c.f.Sync() }
63
64 func (c *checkpoint) Close() error { return c.f.Close() }
65