From 8f3a9f1e2f0c9e066d2e3894b9fc2d054d2f148e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Sat, 22 Aug 2020 14:26:29 +0200 Subject: Lock cache --- go.mod | 1 + go.sum | 2 ++ internal/feed/cache.go | 68 +++++++++++++++++++++++++++++++++++++++-------- internal/feed/cache_v1.go | 2 +- internal/feed/state.go | 2 +- 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 2f65b85..748258b 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/google/go-cmp v0.5.1 github.com/google/uuid v1.1.1 github.com/mmcdole/gofeed v1.0.0 + github.com/nightlyone/lockfile v1.0.0 golang.org/x/net v0.0.0-20200506145744-7e3656a0809f gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86 ) diff --git a/go.sum b/go.sum index 0ddbdb1..5d6567d 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/mmcdole/gofeed v1.0.0 h1:PHqwr8fsEm8xarj9s53XeEAFYhRM3E9Ib7Ie766/LTE= github.com/mmcdole/gofeed v1.0.0/go.mod h1:tkVcyzS3qVMlQrQxJoEH1hkTiuo9a8emDzkMi7TZBu0= github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI= github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8= +github.com/nightlyone/lockfile v1.0.0 h1:RHep2cFKK4PonZJDdEl4GmkabuhbsRMgk/k3uAmxBiA= +github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/internal/feed/cache.go b/internal/feed/cache.go index 45cefd0..2d8f9aa 100644 --- a/internal/feed/cache.go +++ b/internal/feed/cache.go @@ -8,6 +8,8 @@ import ( "os" "time" + "github.com/nightlyone/lockfile" + "github.com/Necoro/feed2imap-go/pkg/log" ) @@ -17,12 +19,18 @@ const ( currentVersion Version = 1 ) -type Cache interface { +type CacheImpl interface { findItem(*Feed) CachedFeed Version() Version Info() string SpecificInfo(interface{}) string - transformToCurrent() (Cache, error) + transformToCurrent() (CacheImpl, error) +} + +type Cache struct { + CacheImpl + lock lockfile.Lockfile + locked bool } type CachedFeed interface { @@ -34,7 +42,7 @@ type CachedFeed interface { Commit() } -func cacheForVersion(version Version) (Cache, error) { +func cacheForVersion(version Version) (CacheImpl, error) { switch version { case v1Version: return newV1Cache(), nil @@ -43,8 +51,29 @@ func cacheForVersion(version Version) (Cache, error) { } } +func lockName(fileName string) string { + return fileName + ".lck" +} + +func lock(fileName string) (lock lockfile.Lockfile, err error) { + lockFile := lockName(fileName) + log.Debugf("Handling lock file '%s'", lockFile) + + if lock, err = lockfile.New(lockFile); err != nil { + err = fmt.Errorf("Creating lock file: %w", err) + return + } + + if err = lock.TryLock(); err != nil { + err = fmt.Errorf("Locking: %w", err) + return + } + + return +} + func storeCache(cache Cache, fileName string) error { - if cache == nil { + if cache.CacheImpl == nil { return fmt.Errorf("trying to store nil cache") } if cache.Version() != currentVersion { @@ -70,11 +99,23 @@ func storeCache(cache Cache, fileName string) error { writer.Flush() log.Printf("Stored cache to '%s'.", fileName) + if cache.locked { + if err = cache.lock.Unlock(); err != nil { + return fmt.Errorf("Unlocking cache: %w", err) + } + } return nil } func newCache() (Cache, error) { - return cacheForVersion(currentVersion) + cache, err := cacheForVersion(currentVersion) + if err != nil { + return Cache{}, err + } + return Cache{ + CacheImpl: cache, + locked: false, + }, nil } func LoadCache(fileName string) (Cache, error) { @@ -84,33 +125,38 @@ func LoadCache(fileName string) (Cache, error) { // no cache there yet -- make new return newCache() } - return nil, fmt.Errorf("opening cache at '%s': %w", fileName, err) + return Cache{}, fmt.Errorf("opening cache at '%s': %w", fileName, err) } defer f.Close() + lock, err := lock(fileName) + if err != nil { + return Cache{}, err + } + log.Printf("Loading cache from '%s'", fileName) reader := bufio.NewReader(f) version, err := reader.ReadByte() if err != nil { - return nil, fmt.Errorf("reading from '%s': %w", fileName, err) + return Cache{}, fmt.Errorf("reading from '%s': %w", fileName, err) } cache, err := cacheForVersion(Version(version)) if err != nil { - return nil, err + return Cache{}, err } decoder := gob.NewDecoder(reader) if err = decoder.Decode(cache); err != nil { - return nil, fmt.Errorf("decoding for version '%d' from '%s': %w", version, fileName, err) + return Cache{}, fmt.Errorf("decoding for version '%d' from '%s': %w", version, fileName, err) } if cache, err = cache.transformToCurrent(); err != nil { - return nil, fmt.Errorf("cannot transform from version %d to %d: %w", version, currentVersion, err) + return Cache{}, fmt.Errorf("cannot transform from version %d to %d: %w", version, currentVersion, err) } log.Printf("Loaded cache (version %d), transformed to version %d.", version, currentVersion) - return cache, nil + return Cache{cache, lock, true}, nil } diff --git a/internal/feed/cache_v1.go b/internal/feed/cache_v1.go index 656c133..a80e81c 100644 --- a/internal/feed/cache_v1.go +++ b/internal/feed/cache_v1.go @@ -157,7 +157,7 @@ func newV1Cache() *v1Cache { return &cache } -func (cache *v1Cache) transformToCurrent() (Cache, error) { +func (cache *v1Cache) transformToCurrent() (CacheImpl, error) { return cache, nil } diff --git a/internal/feed/state.go b/internal/feed/state.go index dae0917..f2eff72 100644 --- a/internal/feed/state.go +++ b/internal/feed/state.go @@ -154,7 +154,7 @@ func (state *State) Filter() { func NewState(cfg *config.Config) (*State, error) { state := State{ feeds: map[string]*Feed{}, - cache: nil, // loaded later on + cache: Cache{}, // loaded later on cfg: cfg, } -- cgit v1.2.3