snapshotter.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package snap
  2. import (
  3. "errors"
  4. "fmt"
  5. "hash/crc32"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "path"
  10. "sort"
  11. "strings"
  12. "github.com/coreos/etcd/raft"
  13. "github.com/coreos/etcd/raft/raftpb"
  14. "github.com/coreos/etcd/snap/snappb"
  15. )
  16. const (
  17. snapSuffix = ".snap"
  18. )
  19. var (
  20. ErrNoSnapshot = errors.New("snap: no available snapshot")
  21. ErrCRCMismatch = errors.New("snap: crc mismatch")
  22. crcTable = crc32.MakeTable(crc32.Castagnoli)
  23. )
  24. type Snapshotter struct {
  25. dir string
  26. }
  27. func New(dir string) *Snapshotter {
  28. return &Snapshotter{
  29. dir: dir,
  30. }
  31. }
  32. func (s *Snapshotter) SaveSnap(snapshot raftpb.Snapshot) {
  33. if raft.IsEmptySnap(snapshot) {
  34. return
  35. }
  36. s.save(&snapshot)
  37. }
  38. func (s *Snapshotter) save(snapshot *raftpb.Snapshot) error {
  39. fname := fmt.Sprintf("%016x-%016x%s", snapshot.Term, snapshot.Index, snapSuffix)
  40. b, err := snapshot.Marshal()
  41. if err != nil {
  42. panic(err)
  43. }
  44. crc := crc32.Update(0, crcTable, b)
  45. snap := snappb.Snapshot{Crc: crc, Data: b}
  46. d, err := snap.Marshal()
  47. if err != nil {
  48. return err
  49. }
  50. return ioutil.WriteFile(path.Join(s.dir, fname), d, 0666)
  51. }
  52. func (s *Snapshotter) Load() (*raftpb.Snapshot, error) {
  53. names, err := s.snapNames()
  54. if err != nil {
  55. return nil, err
  56. }
  57. var snap *raftpb.Snapshot
  58. for _, name := range names {
  59. if snap, err = loadSnap(s.dir, name); err == nil {
  60. break
  61. }
  62. }
  63. return snap, err
  64. }
  65. func loadSnap(dir, name string) (*raftpb.Snapshot, error) {
  66. var err error
  67. var b []byte
  68. fpath := path.Join(dir, name)
  69. defer func() {
  70. if err != nil {
  71. renameBroken(fpath)
  72. }
  73. }()
  74. b, err = ioutil.ReadFile(fpath)
  75. if err != nil {
  76. log.Printf("Snapshotter cannot read file %v: %v", name, err)
  77. return nil, err
  78. }
  79. var serializedSnap snappb.Snapshot
  80. if err = serializedSnap.Unmarshal(b); err != nil {
  81. log.Printf("Corrupted snapshot file %v: %v", name, err)
  82. return nil, err
  83. }
  84. crc := crc32.Update(0, crcTable, serializedSnap.Data)
  85. if crc != serializedSnap.Crc {
  86. log.Printf("Corrupted snapshot file %v: crc mismatch", name)
  87. err = ErrCRCMismatch
  88. return nil, err
  89. }
  90. var snap raftpb.Snapshot
  91. if err = snap.Unmarshal(serializedSnap.Data); err != nil {
  92. log.Printf("Corrupted snapshot file %v: %v", name, err)
  93. return nil, err
  94. }
  95. return &snap, nil
  96. }
  97. // snapNames returns the filename of the snapshots in logical time order (from newest to oldest).
  98. // If there is no avaliable snapshots, an ErrNoSnapshot will be returned.
  99. func (s *Snapshotter) snapNames() ([]string, error) {
  100. dir, err := os.Open(s.dir)
  101. if err != nil {
  102. return nil, err
  103. }
  104. defer dir.Close()
  105. names, err := dir.Readdirnames(-1)
  106. if err != nil {
  107. return nil, err
  108. }
  109. snaps := checkSuffix(names)
  110. if len(snaps) == 0 {
  111. return nil, ErrNoSnapshot
  112. }
  113. sort.Sort(sort.Reverse(sort.StringSlice(snaps)))
  114. return snaps, nil
  115. }
  116. func checkSuffix(names []string) []string {
  117. snaps := []string{}
  118. for i := range names {
  119. if strings.HasSuffix(names[i], snapSuffix) {
  120. snaps = append(snaps, names[i])
  121. } else {
  122. log.Printf("Unexpected non-snap file %v", names[i])
  123. }
  124. }
  125. return snaps
  126. }
  127. func renameBroken(path string) {
  128. brokenPath := path + ".broken"
  129. if err := os.Rename(path, brokenPath); err != nil {
  130. log.Printf("Cannot rename broken snapshot file %v to %v: %v", path, brokenPath, err)
  131. }
  132. }