123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- // Copyright (c) 2017 Couchbase, Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package scorch
-
- import (
- "fmt"
- "log"
- "os"
-
- "github.com/blevesearch/bleve/index/scorch/segment"
- bolt "go.etcd.io/bbolt"
- )
-
- type RollbackPoint struct {
- epoch uint64
- meta map[string][]byte
- }
-
- func (r *RollbackPoint) GetInternal(key []byte) []byte {
- return r.meta[string(key)]
- }
-
- // RollbackPoints returns an array of rollback points available for
- // the application to rollback to, with more recent rollback points
- // (higher epochs) coming first.
- func RollbackPoints(path string) ([]*RollbackPoint, error) {
- if len(path) == 0 {
- return nil, fmt.Errorf("RollbackPoints: invalid path")
- }
-
- rootBoltPath := path + string(os.PathSeparator) + "root.bolt"
- rootBoltOpt := &bolt.Options{
- ReadOnly: true,
- }
- rootBolt, err := bolt.Open(rootBoltPath, 0600, rootBoltOpt)
- if err != nil || rootBolt == nil {
- return nil, err
- }
-
- // start a read-only bolt transaction
- tx, err := rootBolt.Begin(false)
- if err != nil {
- return nil, fmt.Errorf("RollbackPoints: failed to start" +
- " read-only transaction")
- }
-
- // read-only bolt transactions to be rolled back
- defer func() {
- _ = tx.Rollback()
- _ = rootBolt.Close()
- }()
-
- snapshots := tx.Bucket(boltSnapshotsBucket)
- if snapshots == nil {
- return nil, nil
- }
-
- rollbackPoints := []*RollbackPoint{}
-
- c1 := snapshots.Cursor()
- for k, _ := c1.Last(); k != nil; k, _ = c1.Prev() {
- _, snapshotEpoch, err := segment.DecodeUvarintAscending(k)
- if err != nil {
- log.Printf("RollbackPoints:"+
- " unable to parse segment epoch %x, continuing", k)
- continue
- }
-
- snapshot := snapshots.Bucket(k)
- if snapshot == nil {
- log.Printf("RollbackPoints:"+
- " snapshot key, but bucket missing %x, continuing", k)
- continue
- }
-
- meta := map[string][]byte{}
- c2 := snapshot.Cursor()
- for j, _ := c2.First(); j != nil; j, _ = c2.Next() {
- if j[0] == boltInternalKey[0] {
- internalBucket := snapshot.Bucket(j)
- err = internalBucket.ForEach(func(key []byte, val []byte) error {
- copiedVal := append([]byte(nil), val...)
- meta[string(key)] = copiedVal
- return nil
- })
- if err != nil {
- break
- }
- }
- }
-
- if err != nil {
- log.Printf("RollbackPoints:"+
- " failed in fetching internal data: %v", err)
- continue
- }
-
- rollbackPoints = append(rollbackPoints, &RollbackPoint{
- epoch: snapshotEpoch,
- meta: meta,
- })
- }
-
- return rollbackPoints, nil
- }
-
- // Rollback atomically and durably brings the store back to the point
- // in time as represented by the RollbackPoint.
- // Rollback() should only be passed a RollbackPoint that came from the
- // same store using the RollbackPoints() API along with the index path.
- func Rollback(path string, to *RollbackPoint) error {
- if to == nil {
- return fmt.Errorf("Rollback: RollbackPoint is nil")
- }
- if len(path) == 0 {
- return fmt.Errorf("Rollback: index path is empty")
- }
-
- rootBoltPath := path + string(os.PathSeparator) + "root.bolt"
- rootBoltOpt := &bolt.Options{
- ReadOnly: false,
- }
- rootBolt, err := bolt.Open(rootBoltPath, 0600, rootBoltOpt)
- if err != nil || rootBolt == nil {
- return err
- }
- defer func() {
- err1 := rootBolt.Close()
- if err1 != nil && err == nil {
- err = err1
- }
- }()
-
- // pick all the younger persisted epochs in bolt store
- // including the target one.
- var found bool
- var eligibleEpochs []uint64
- err = rootBolt.View(func(tx *bolt.Tx) error {
- snapshots := tx.Bucket(boltSnapshotsBucket)
- if snapshots == nil {
- return nil
- }
- sc := snapshots.Cursor()
- for sk, _ := sc.Last(); sk != nil && !found; sk, _ = sc.Prev() {
- _, snapshotEpoch, err := segment.DecodeUvarintAscending(sk)
- if err != nil {
- continue
- }
- if snapshotEpoch == to.epoch {
- found = true
- }
- eligibleEpochs = append(eligibleEpochs, snapshotEpoch)
- }
- return nil
- })
-
- if len(eligibleEpochs) == 0 {
- return fmt.Errorf("Rollback: no persisted epochs found in bolt")
- }
- if !found {
- return fmt.Errorf("Rollback: target epoch %d not found in bolt", to.epoch)
- }
-
- // start a write transaction
- tx, err := rootBolt.Begin(true)
- if err != nil {
- return err
- }
-
- defer func() {
- if err == nil {
- err = tx.Commit()
- } else {
- _ = tx.Rollback()
- }
- if err == nil {
- err = rootBolt.Sync()
- }
- }()
-
- snapshots := tx.Bucket(boltSnapshotsBucket)
- if snapshots == nil {
- return nil
- }
- for _, epoch := range eligibleEpochs {
- k := segment.EncodeUvarintAscending(nil, epoch)
- if err != nil {
- continue
- }
- if epoch == to.epoch {
- // return here as it already processed until the given epoch
- return nil
- }
- err = snapshots.DeleteBucket(k)
- if err == bolt.ErrBucketNotFound {
- err = nil
- }
- }
-
- return err
- }
|