From b30dd5c9a37f95ae61fd172e03d542a0f84aceac Mon Sep 17 00:00:00 2001 From: rkfg Date: Sat, 22 Oct 2022 15:21:47 +0300 Subject: [PATCH] Add node cache --- README.md | 3 ++- cache.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 ++- go.sum | 4 ++++ main.go | 14 +++++++++++++ 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 cache.go diff --git a/README.md b/README.md index 5beb6bc..d58370c 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ rebalance-lnd](https://github.com/accumulator/rebalance-lnd). --allow-unbalance-from let the source channel go below 50% local liquidity, use if you want to drain a channel; you should also set --pfrom to >50 --allow-unbalance-to let the target channel go above 50% local liquidity, use if you want to refill a channel; you should also set --pto to >50 -s, --stat= save successful rebalance information to the specified CSV file - + --node-cache= save and load other nodes information to this file, improves cold start performance + --node-cache-lifetime= the cache file will not be loaded if it's older than this time (in minutes) (default: 1440) ``` Look in `config.json.sample` or `config.toml.sample` for corresponding keys, diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..2210ccd --- /dev/null +++ b/cache.go @@ -0,0 +1,60 @@ +package main + +import ( + "encoding/gob" + "log" + "os" + "path/filepath" + "time" + + "github.com/gofrs/flock" +) + +func lock() *flock.Flock { + return flock.New(filepath.Join(os.TempDir(), "regolancer.lock")) +} + +func (r *regolancer) loadNodeCache(filename string, exp int) { + if filename == "" { + return + } + l := lock() + l.RLock() + defer l.Unlock() + f, err := os.Open(filename) + if err != nil { + if !os.IsNotExist(err) { + logErrorF("Error opening node cache file: %s", err) + } + return + } + defer f.Close() + fi, err := f.Stat() + if err != nil { + logErrorF("Error getting node cache information: %s", err) + return + } + if time.Since(fi.ModTime()) > time.Minute*time.Duration(exp) { + log.Print("Node cache expired, not loading") + return + } + log.Printf("Loading node cache from %s", filename) + gob.NewDecoder(f).Decode(&r.nodeCache) +} + +func (r *regolancer) saveNodeCache(filename string) { + if filename == "" { + return + } + log.Printf("Saving node cache to %s", filename) + l := lock() + l.Lock() + defer l.Unlock() + f, err := os.Create(filename) + if err != nil { + logErrorF("Error creating node cache file %s: %s", filename, err) + return + } + defer f.Close() + gob.NewEncoder(f).Encode(r.nodeCache) +} diff --git a/go.mod b/go.mod index 8feb241..2a531b5 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/fergusstrange/embedded-postgres v1.10.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/go-errors/errors v1.0.1 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -128,7 +129,7 @@ require ( go.uber.org/zap v1.17.0 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/sys v0.1.0 // indirect golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect diff --git a/go.sum b/go.sum index de4dc0a..b1edc89 100644 --- a/go.sum +++ b/go.sum @@ -204,6 +204,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -858,6 +860,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/main.go b/main.go index 3a30b5a..bb203d2 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "log" "math/rand" "os" + "os/signal" "strconv" "strings" "time" @@ -45,6 +46,8 @@ type configParams struct { AllowUnbalanceFrom bool `long:"allow-unbalance-from" description:"let the source channel go below 50% local liquidity, use if you want to drain a channel; you should also set --pfrom to >50" json:"allow_unbalance_from" toml:"allow_unbalance_from"` AllowUnbalanceTo bool `long:"allow-unbalance-to" description:"let the target channel go above 50% local liquidity, use if you want to refill a channel; you should also set --pto to >50" json:"allow_unbalance_to" toml:"allow_unbalance_to"` StatFilename string `short:"s" long:"stat" description:"save successful rebalance information to the specified CSV file" json:"stat" toml:"stat"` + NodeCacheFilename string `long:"node-cache" description:"save and load other nodes information to this file, improves cold start performance" json:"node_cache_filename" toml:"node_cache_filename"` + NodeCacheLifetime int `long:"node-cache-lifetime" description:"the cache file will not be loaded if it's older than this time (in minutes)" json:"node_cache_lifetime" toml:"node_cache_lifetime" default:"1440"` } var params, cfgParams configParams @@ -285,6 +288,17 @@ func main() { } infoCtxCancel() attempt := 1 + + r.loadNodeCache(params.NodeCacheFilename, params.NodeCacheLifetime) + defer r.saveNodeCache(params.NodeCacheFilename) + stopChan := make(chan os.Signal) + signal.Notify(stopChan, os.Interrupt) + go func() { + <-stopChan + r.saveNodeCache(params.NodeCacheFilename) + os.Exit(1) + }() + for { attemptCtx, attemptCancel := context.WithTimeout(mainCtx, time.Minute*5) _, retry := tryRebalance(attemptCtx, &r, &attempt)