github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/checkpoint.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 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 15 package bitalosdb 16 17 import ( 18 "os" 19 20 "github.com/cockroachdb/errors/oserror" 21 "github.com/zuoyebang/bitalosdb/internal/base" 22 "github.com/zuoyebang/bitalosdb/internal/vfs" 23 ) 24 25 type checkpointOptions struct { 26 flushWAL bool 27 } 28 29 type CheckpointOption func(*checkpointOptions) 30 31 func WithFlushedWAL() CheckpointOption { 32 return func(opt *checkpointOptions) { 33 opt.flushWAL = true 34 } 35 } 36 37 func mkdirAllAndSyncParents(fs vfs.FS, destDir string) (vfs.File, error) { 38 var parentPaths []string 39 if err := fs.MkdirAll(destDir, 0755); err != nil { 40 return nil, err 41 } 42 43 foundExistingAncestor := false 44 for parentPath := fs.PathDir(destDir); parentPath != "."; parentPath = fs.PathDir(parentPath) { 45 parentPaths = append(parentPaths, parentPath) 46 _, err := fs.Stat(parentPath) 47 if err == nil { 48 foundExistingAncestor = true 49 break 50 } 51 if !oserror.IsNotExist(err) { 52 return nil, err 53 } 54 } 55 56 if !foundExistingAncestor { 57 parentPaths = append(parentPaths, "") 58 } 59 60 for _, parentPath := range parentPaths { 61 parentDir, err := fs.OpenDir(parentPath) 62 if err != nil { 63 return nil, err 64 } 65 err = parentDir.Sync() 66 if err != nil { 67 _ = parentDir.Close() 68 return nil, err 69 } 70 err = parentDir.Close() 71 if err != nil { 72 return nil, err 73 } 74 } 75 return fs.OpenDir(destDir) 76 } 77 78 func (d *DB) Checkpoint(destDir string, opts ...CheckpointOption) (ckErr error) { 79 d.opts.Logger.Info("checkpoint start to run") 80 defer d.opts.Logger.Cost("checkpoint done")() 81 82 opt := &checkpointOptions{} 83 for _, fn := range opts { 84 fn(opt) 85 } 86 87 if _, err := d.opts.FS.Stat(destDir); !oserror.IsNotExist(err) { 88 if err == nil { 89 return &os.PathError{ 90 Op: "checkpoint", 91 Path: destDir, 92 Err: oserror.ErrExist, 93 } 94 } 95 return err 96 } 97 98 isSync := opt.flushWAL && !d.opts.DisableWAL 99 100 fs := vfs.WithSyncingFS(d.opts.FS, vfs.SyncingFileOptions{ 101 BytesPerSync: d.opts.BytesPerSync, 102 }) 103 104 var dir vfs.File 105 defer func() { 106 if dir != nil { 107 _ = dir.Close() 108 } 109 if ckErr != nil { 110 paths, _ := fs.List(destDir) 111 for _, path := range paths { 112 _ = fs.Remove(path) 113 } 114 _ = fs.Remove(destDir) 115 } 116 }() 117 dir, ckErr = mkdirAllAndSyncParents(fs, destDir) 118 if ckErr != nil { 119 return ckErr 120 } 121 122 srcPath := base.MakeFilepath(fs, d.dirname, fileTypeMeta, 0) 123 destFile := fs.PathJoin(destDir, fs.PathBase(srcPath)) 124 if ckErr = vfs.Copy(fs, srcPath, destFile); ckErr != nil { 125 return ckErr 126 } 127 128 for i := range d.bitowers { 129 if ckErr = d.bitowers[i].checkpoint(fs, destDir, isSync); ckErr != nil { 130 return ckErr 131 } 132 } 133 134 if ckErr = dir.Sync(); ckErr != nil { 135 return ckErr 136 } 137 ckErr = dir.Close() 138 dir = nil 139 return ckErr 140 } 141 142 func (d *DB) SetCheckpointLock(lock bool) { 143 if lock { 144 d.LockTask() 145 d.dbState.LockDbWrite() 146 } else { 147 d.dbState.UnlockDbWrite() 148 d.UnlockTask() 149 } 150 } 151 152 func (d *DB) SetCheckpointHighPriority(v bool) { 153 d.dbState.SetDbHighPriority(v) 154 }