github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/ee/backup/file_handler.go (about) 1 // +build !oss 2 3 /* 4 * Copyright 2018 Dgraph Labs, Inc. and Contributors 5 * 6 * Licensed under the Dgraph Community License (the "License"); you 7 * may not use this file except in compliance with the License. You 8 * may obtain a copy of the License at 9 * 10 * https://github.com/dgraph-io/dgraph/blob/master/licenses/DCL.txt 11 */ 12 13 package backup 14 15 import ( 16 "encoding/json" 17 "fmt" 18 "io/ioutil" 19 "net/url" 20 "os" 21 "path/filepath" 22 "sort" 23 "strings" 24 25 "github.com/dgraph-io/dgraph/protos/pb" 26 "github.com/dgraph-io/dgraph/x" 27 28 "github.com/golang/glog" 29 "github.com/pkg/errors" 30 ) 31 32 // fileHandler is used for 'file:' URI scheme. 33 type fileHandler struct { 34 fp *os.File 35 } 36 37 // readManifest reads a manifest file at path using the handler. 38 // Returns nil on success, otherwise an error. 39 func (h *fileHandler) readManifest(path string, m *Manifest) error { 40 b, err := ioutil.ReadFile(path) 41 if err != nil { 42 return err 43 } 44 return json.Unmarshal(b, m) 45 } 46 47 func (h *fileHandler) createFiles(uri *url.URL, req *pb.BackupRequest, fileName string) error { 48 var dir, path string 49 50 dir = filepath.Join(uri.Path, fmt.Sprintf(backupPathFmt, req.UnixTs)) 51 err := os.Mkdir(dir, 0700) 52 if err != nil && !os.IsExist(err) { 53 return err 54 } 55 56 path = filepath.Join(dir, fileName) 57 h.fp, err = os.Create(path) 58 if err != nil { 59 return err 60 } 61 glog.V(2).Infof("Using file path: %q", path) 62 return nil 63 } 64 65 // GetLatestManifest reads the manifests at the given URL and returns the 66 // latest manifest. 67 func (h *fileHandler) GetLatestManifest(uri *url.URL) (*Manifest, error) { 68 if !pathExist(uri.Path) { 69 return nil, errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path) 70 } 71 72 // Find the max Since value from the latest backup. 73 var lastManifest string 74 suffix := filepath.Join(string(filepath.Separator), backupManifest) 75 _ = x.WalkPathFunc(uri.Path, func(path string, isdir bool) bool { 76 if !isdir && strings.HasSuffix(path, suffix) && path > lastManifest { 77 lastManifest = path 78 } 79 return false 80 }) 81 82 var m Manifest 83 if lastManifest == "" { 84 return &m, nil 85 } 86 87 if err := h.readManifest(lastManifest, &m); err != nil { 88 return nil, err 89 } 90 return &m, nil 91 } 92 93 // CreateBackupFile prepares the a path to save the backup file. 94 func (h *fileHandler) CreateBackupFile(uri *url.URL, req *pb.BackupRequest) error { 95 if !pathExist(uri.Path) { 96 return errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path) 97 } 98 99 fileName := backupName(req.ReadTs, req.GroupId) 100 return h.createFiles(uri, req, fileName) 101 } 102 103 // CreateManifest completes the backup by writing the manifest to a file. 104 func (h *fileHandler) CreateManifest(uri *url.URL, req *pb.BackupRequest) error { 105 if !pathExist(uri.Path) { 106 return errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path) 107 } 108 109 return h.createFiles(uri, req, backupManifest) 110 } 111 112 // Load uses tries to load any backup files found. 113 // Returns the maximum value of Since on success, error otherwise. 114 func (h *fileHandler) Load(uri *url.URL, backupId string, fn loadFn) (uint64, error) { 115 if !pathExist(uri.Path) { 116 return 0, errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path) 117 } 118 119 suffix := filepath.Join(string(filepath.Separator), backupManifest) 120 paths := x.WalkPathFunc(uri.Path, func(path string, isdir bool) bool { 121 return !isdir && strings.HasSuffix(path, suffix) 122 }) 123 if len(paths) == 0 { 124 return 0, errors.Errorf("No manifests found at path: %s", uri.Path) 125 } 126 sort.Strings(paths) 127 if glog.V(3) { 128 fmt.Printf("Found backup manifest(s): %v\n", paths) 129 } 130 131 // Read and filter the files to get the list of files to consider 132 // for this restore operation. 133 var manifests []*Manifest 134 for _, path := range paths { 135 var m Manifest 136 if err := h.readManifest(path, &m); err != nil { 137 return 0, errors.Wrapf(err, "While reading %q", path) 138 } 139 m.Path = path 140 manifests = append(manifests, &m) 141 } 142 manifests, err := filterManifests(manifests, backupId) 143 if err != nil { 144 return 0, err 145 } 146 147 // Process each manifest, first check that they are valid and then confirm the 148 // backup files for each group exist. Each group in manifest must have a backup file, 149 // otherwise this is a failure and the user must remedy. 150 var since uint64 151 for i, manifest := range manifests { 152 if manifest.Since == 0 || len(manifest.Groups) == 0 { 153 if glog.V(2) { 154 fmt.Printf("Restore: skip backup: %#v\n", manifest) 155 } 156 continue 157 } 158 159 path := filepath.Dir(manifests[i].Path) 160 for gid := range manifest.Groups { 161 file := filepath.Join(path, backupName(manifest.Since, gid)) 162 fp, err := os.Open(file) 163 if err != nil { 164 return 0, errors.Wrapf(err, "Failed to open %q", file) 165 } 166 defer fp.Close() 167 168 // Only restore the predicates that were assigned to this group at the time 169 // of the last backup. 170 predSet := manifests[len(manifests)-1].getPredsInGroup(gid) 171 if err = fn(fp, int(gid), predSet); err != nil { 172 return 0, err 173 } 174 } 175 since = manifest.Since 176 } 177 return since, nil 178 } 179 180 // ListManifests loads the manifests in the locations and returns them. 181 func (h *fileHandler) ListManifests(uri *url.URL) ([]string, error) { 182 if !pathExist(uri.Path) { 183 return nil, errors.Errorf("The path %q does not exist or it is inaccessible.", uri.Path) 184 } 185 186 suffix := filepath.Join(string(filepath.Separator), backupManifest) 187 manifests := x.WalkPathFunc(uri.Path, func(path string, isdir bool) bool { 188 return !isdir && strings.HasSuffix(path, suffix) 189 }) 190 if len(manifests) == 0 { 191 return nil, errors.Errorf("No manifests found at path: %s", uri.Path) 192 } 193 sort.Strings(manifests) 194 if glog.V(3) { 195 fmt.Printf("Found backup manifest(s): %v\n", manifests) 196 } 197 return manifests, nil 198 } 199 200 func (h *fileHandler) ReadManifest(path string, m *Manifest) error { 201 return h.readManifest(path, m) 202 } 203 204 func (h *fileHandler) Close() error { 205 if h.fp == nil { 206 return nil 207 } 208 if err := h.fp.Sync(); err != nil { 209 glog.Errorf("While closing file: %s. Error: %v", h.fp.Name(), err) 210 x.Ignore(h.fp.Close()) 211 return err 212 } 213 return h.fp.Close() 214 } 215 216 func (h *fileHandler) Write(b []byte) (int, error) { 217 return h.fp.Write(b) 218 } 219 220 // pathExist checks if a path (file or dir) is found at target. 221 // Returns true if found, false otherwise. 222 func pathExist(path string) bool { 223 _, err := os.Stat(path) 224 if err == nil { 225 return true 226 } 227 return !os.IsNotExist(err) && !os.IsPermission(err) 228 }