From 0a4929876c1606abf2a599c6561affba368ab5fb Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Tue, 28 Nov 2017 22:38:21 +0100 Subject: Add caching --- README.md | 3 ++ cache/cache.go | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ config/config.go | 4 ++ main.go | 23 +++++++++++ snapshot.go | 3 +- write_dir.go | 3 +- 6 files changed, 146 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index db923fc..330e913 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ Here is a commented example config. For more details, consult the documentation # This config key defines the default storage backend, as defined below default_storage = "local_encrypted" + # This defines the location of the cache file (can speed up creating backups; can be left out) + cache_path = "~/.cache/petrific.cache" + [signing] # Use this GPG key to sign snapshots key = "0123456789ABCDEF0123456789ABCDEF01234567" diff --git a/cache/cache.go b/cache/cache.go index 32b8325..2d20087 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -1,13 +1,20 @@ package cache import ( + "bufio" "code.laria.me/petrific/objects" + "fmt" + "io" + "os" + "strconv" + "strings" "time" ) type Cache interface { PathUpdated(path string) (mtime time.Time, id objects.ObjectId, ok bool) SetPathUpdated(path string, mtime time.Time, id objects.ObjectId) + Close() error } type NopCache struct{} @@ -18,3 +25,110 @@ func (NopCache) PathUpdated(_ string) (_ time.Time, _ objects.ObjectId, ok bool) } func (NopCache) SetPathUpdated(_ string, _ time.Time, _ objects.ObjectId) {} + +func (NopCache) Close() error { return nil } + +type fileCacheEntry struct { + mtime time.Time + id objects.ObjectId +} + +type FileCache struct { + cache map[string]fileCacheEntry + location string +} + +func (fc FileCache) PathUpdated(path string) (time.Time, objects.ObjectId, bool) { + entry, ok := fc.cache[path] + return entry.mtime, entry.id, ok +} + +func (fc FileCache) SetPathUpdated(path string, mtime time.Time, id objects.ObjectId) { + fc.cache[path] = fileCacheEntry{mtime, id} +} + +func NewFileCache(location string) FileCache { + return FileCache{make(map[string]fileCacheEntry), location} +} + +func escapeName(name string) string { + name = strings.Replace(name, "\\", "\\\\", -1) + name = strings.Replace(name, "\n", "\\n", -1) + return name +} + +func unescapeName(name string) string { + name = strings.Replace(name, "\\n", "\n", -1) + name = strings.Replace(name, "\\\\", "\\", -1) + return name +} + +func (fc FileCache) dump(w io.Writer) error { + for path, entry := range fc.cache { + if _, err := fmt.Fprintf( + w, + "%s %d %d %s\n", + entry.id, + entry.mtime.Unix(), + entry.mtime.Nanosecond(), + escapeName(path), + ); err != nil { + return err + } + } + + return nil +} + +func (fc FileCache) load(r io.Reader) error { + scanner := bufio.NewScanner(r) + for scanner.Scan() { + parts := strings.SplitN(scanner.Text(), " ", 4) + if len(parts) != 4 { + return fmt.Errorf("Could not load FileCache: Expected 4 entries, got %d", len(parts)) + } + + id, err := objects.ParseObjectId(parts[0]) + if err != nil { + return err + } + + sec, err := strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return err + } + + nsec, err := strconv.ParseInt(parts[2], 10, 64) + if err != nil { + return err + } + + fc.cache[unescapeName(parts[3])] = fileCacheEntry{time.Unix(sec, nsec), id} + } + + return scanner.Err() +} + +func (fc FileCache) Load() error { + f, err := os.Open(fc.location) + switch { + case os.IsNotExist(err): + return nil + case err != nil: + return err + default: + } + defer f.Close() + + return fc.load(f) +} + +func (fc FileCache) Close() error { + f, err := os.Create(fc.location) + if err != nil { + return err + } + defer f.Close() + + return fc.dump(f) +} diff --git a/config/config.go b/config/config.go index b643477..7deef69 100644 --- a/config/config.go +++ b/config/config.go @@ -11,6 +11,9 @@ // # This config key defines the default storage backend, as defined below ([storage.local_compressed]) // default_storage = "local_compressed" // +// # This defines the location of the cache file (can speed up creating backups; can be left out) +// cache_path = "~/.cache/petrific.cache" +// // [signing] // # Use this GPG key to sign snapshots // key = "0123456789ABCDEF0123456789ABCDEF01234567" @@ -55,6 +58,7 @@ var ( ) type Config struct { + CachePath string `toml:"cache_path,omitempty"` DefaultStorage string `toml:"default_storage"` Signing struct { Key string diff --git a/main.go b/main.go index 11202f3..5ea627f 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "code.laria.me/petrific/cache" "code.laria.me/petrific/config" "code.laria.me/petrific/storage" "code.laria.me/petrific/storage/registry" @@ -44,6 +45,7 @@ var ( var conf config.Config var objectstore storage.Storage +var id_cache cache.Cache = cache.NopCache{} func main() { os.Exit(Main()) @@ -64,6 +66,11 @@ func Main() int { } defer objectstore.Close() + if !loadCache(conf) { + return 1 + } + defer id_cache.Close() + remaining := make([]string, 0) for _, arg := range flag.Args() { if arg != "" { @@ -84,6 +91,22 @@ func Main() int { return cmd(remaining[1:]) } +func loadCache(conf config.Config) bool { + if conf.CachePath == "" { + return true + } + + file_cache := cache.NewFileCache(config.ExpandTilde(conf.CachePath)) + if err := file_cache.Load(); err != nil { + fmt.Fprintf(os.Stderr, "Loading cache %s: %s", conf.CachePath, err) + return false + } + + id_cache = file_cache + + return true +} + func loadConfig() bool { var err error conf, err = config.LoadConfig(*flagConfPath) diff --git a/snapshot.go b/snapshot.go index 844a85a..db181ba 100644 --- a/snapshot.go +++ b/snapshot.go @@ -2,7 +2,6 @@ package main import ( "code.laria.me/petrific/backup" - "code.laria.me/petrific/cache" "code.laria.me/petrific/fs" "code.laria.me/petrific/gpg" "code.laria.me/petrific/objects" @@ -115,7 +114,7 @@ func TakeSnapshot(args []string) int { return 1 } - tree_id, err := backup.WriteDir(objectstore, dir_path, d, cache.NopCache{}) + tree_id, err := backup.WriteDir(objectstore, dir_path, d, id_cache) if err != nil { errout(err) return 1 diff --git a/write_dir.go b/write_dir.go index 34abcd1..3ee5a97 100644 --- a/write_dir.go +++ b/write_dir.go @@ -2,7 +2,6 @@ package main import ( "code.laria.me/petrific/backup" - "code.laria.me/petrific/cache" "code.laria.me/petrific/fs" "fmt" "os" @@ -46,7 +45,7 @@ func WriteDir(args []string) int { return 1 } - id, err := backup.WriteDir(objectstore, dir_path, d, cache.NopCache{}) + id, err := backup.WriteDir(objectstore, dir_path, d, id_cache) if err != nil { errout(err) return 1 -- cgit v1.2.3-54-g00ecf