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