You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

snapshot_rollback.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Copyright (c) 2017 Couchbase, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package scorch
  15. import (
  16. "fmt"
  17. "log"
  18. "os"
  19. "github.com/blevesearch/bleve/index/scorch/segment"
  20. bolt "go.etcd.io/bbolt"
  21. )
  22. type RollbackPoint struct {
  23. epoch uint64
  24. meta map[string][]byte
  25. }
  26. func (r *RollbackPoint) GetInternal(key []byte) []byte {
  27. return r.meta[string(key)]
  28. }
  29. // RollbackPoints returns an array of rollback points available for
  30. // the application to rollback to, with more recent rollback points
  31. // (higher epochs) coming first.
  32. func RollbackPoints(path string) ([]*RollbackPoint, error) {
  33. if len(path) == 0 {
  34. return nil, fmt.Errorf("RollbackPoints: invalid path")
  35. }
  36. rootBoltPath := path + string(os.PathSeparator) + "root.bolt"
  37. rootBoltOpt := &bolt.Options{
  38. ReadOnly: true,
  39. }
  40. rootBolt, err := bolt.Open(rootBoltPath, 0600, rootBoltOpt)
  41. if err != nil || rootBolt == nil {
  42. return nil, err
  43. }
  44. // start a read-only bolt transaction
  45. tx, err := rootBolt.Begin(false)
  46. if err != nil {
  47. return nil, fmt.Errorf("RollbackPoints: failed to start" +
  48. " read-only transaction")
  49. }
  50. // read-only bolt transactions to be rolled back
  51. defer func() {
  52. _ = tx.Rollback()
  53. _ = rootBolt.Close()
  54. }()
  55. snapshots := tx.Bucket(boltSnapshotsBucket)
  56. if snapshots == nil {
  57. return nil, nil
  58. }
  59. rollbackPoints := []*RollbackPoint{}
  60. c1 := snapshots.Cursor()
  61. for k, _ := c1.Last(); k != nil; k, _ = c1.Prev() {
  62. _, snapshotEpoch, err := segment.DecodeUvarintAscending(k)
  63. if err != nil {
  64. log.Printf("RollbackPoints:"+
  65. " unable to parse segment epoch %x, continuing", k)
  66. continue
  67. }
  68. snapshot := snapshots.Bucket(k)
  69. if snapshot == nil {
  70. log.Printf("RollbackPoints:"+
  71. " snapshot key, but bucket missing %x, continuing", k)
  72. continue
  73. }
  74. meta := map[string][]byte{}
  75. c2 := snapshot.Cursor()
  76. for j, _ := c2.First(); j != nil; j, _ = c2.Next() {
  77. if j[0] == boltInternalKey[0] {
  78. internalBucket := snapshot.Bucket(j)
  79. err = internalBucket.ForEach(func(key []byte, val []byte) error {
  80. copiedVal := append([]byte(nil), val...)
  81. meta[string(key)] = copiedVal
  82. return nil
  83. })
  84. if err != nil {
  85. break
  86. }
  87. }
  88. }
  89. if err != nil {
  90. log.Printf("RollbackPoints:"+
  91. " failed in fetching internal data: %v", err)
  92. continue
  93. }
  94. rollbackPoints = append(rollbackPoints, &RollbackPoint{
  95. epoch: snapshotEpoch,
  96. meta: meta,
  97. })
  98. }
  99. return rollbackPoints, nil
  100. }
  101. // Rollback atomically and durably brings the store back to the point
  102. // in time as represented by the RollbackPoint.
  103. // Rollback() should only be passed a RollbackPoint that came from the
  104. // same store using the RollbackPoints() API along with the index path.
  105. func Rollback(path string, to *RollbackPoint) error {
  106. if to == nil {
  107. return fmt.Errorf("Rollback: RollbackPoint is nil")
  108. }
  109. if len(path) == 0 {
  110. return fmt.Errorf("Rollback: index path is empty")
  111. }
  112. rootBoltPath := path + string(os.PathSeparator) + "root.bolt"
  113. rootBoltOpt := &bolt.Options{
  114. ReadOnly: false,
  115. }
  116. rootBolt, err := bolt.Open(rootBoltPath, 0600, rootBoltOpt)
  117. if err != nil || rootBolt == nil {
  118. return err
  119. }
  120. defer func() {
  121. err1 := rootBolt.Close()
  122. if err1 != nil && err == nil {
  123. err = err1
  124. }
  125. }()
  126. // pick all the younger persisted epochs in bolt store
  127. // including the target one.
  128. var found bool
  129. var eligibleEpochs []uint64
  130. err = rootBolt.View(func(tx *bolt.Tx) error {
  131. snapshots := tx.Bucket(boltSnapshotsBucket)
  132. if snapshots == nil {
  133. return nil
  134. }
  135. sc := snapshots.Cursor()
  136. for sk, _ := sc.Last(); sk != nil && !found; sk, _ = sc.Prev() {
  137. _, snapshotEpoch, err := segment.DecodeUvarintAscending(sk)
  138. if err != nil {
  139. continue
  140. }
  141. if snapshotEpoch == to.epoch {
  142. found = true
  143. }
  144. eligibleEpochs = append(eligibleEpochs, snapshotEpoch)
  145. }
  146. return nil
  147. })
  148. if len(eligibleEpochs) == 0 {
  149. return fmt.Errorf("Rollback: no persisted epochs found in bolt")
  150. }
  151. if !found {
  152. return fmt.Errorf("Rollback: target epoch %d not found in bolt", to.epoch)
  153. }
  154. // start a write transaction
  155. tx, err := rootBolt.Begin(true)
  156. if err != nil {
  157. return err
  158. }
  159. defer func() {
  160. if err == nil {
  161. err = tx.Commit()
  162. } else {
  163. _ = tx.Rollback()
  164. }
  165. if err == nil {
  166. err = rootBolt.Sync()
  167. }
  168. }()
  169. snapshots := tx.Bucket(boltSnapshotsBucket)
  170. if snapshots == nil {
  171. return nil
  172. }
  173. for _, epoch := range eligibleEpochs {
  174. k := segment.EncodeUvarintAscending(nil, epoch)
  175. if err != nil {
  176. continue
  177. }
  178. if epoch == to.epoch {
  179. // return here as it already processed until the given epoch
  180. return nil
  181. }
  182. err = snapshots.DeleteBucket(k)
  183. if err == bolt.ErrBucketNotFound {
  184. err = nil
  185. }
  186. }
  187. return err
  188. }