From e65e5842f414ea83a6fc3c71073092b7580f2a16 Mon Sep 17 00:00:00 2001 From: w1n2k Date: Thu, 19 Jan 2017 11:23:42 +0300 Subject: [PATCH 1/4] Implementing autogc configuration support --- controller/config.go | 54 +++++++++++++++++++++++++++++----------- controller/controller.go | 27 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/controller/config.go b/controller/config.go index 240b51d5..3ca94d7c 100644 --- a/controller/config.go +++ b/controller/config.go @@ -14,7 +14,15 @@ import ( "github.com/tidwall/tile38/controller/server" ) -var validProperties = []string{"requirepass", "leaderauth", "protected-mode", "maxmemory"} +const ( + RequirePass = "requirepass" + LeaderAuth = "leaderauth" + ProtectedMode = "protected-mode" + MaxMemory = "maxmemory" + AutoGC = "autogc" +) + +var validProperties = []string{RequirePass, LeaderAuth, ProtectedMode, MaxMemory, AutoGC} // Config is a tile38 config type Config struct { @@ -34,6 +42,8 @@ type Config struct { ProtectedMode string `json:"-"` MaxMemoryP string `json:"maxmemory,omitempty"` MaxMemory int `json:"-"` + AutoGCP string `json:"autogc,omitempty"` + AutoGC uint64 `json:"-"` } func (c *Controller) loadConfig() error { @@ -49,16 +59,19 @@ func (c *Controller) loadConfig() error { return err } // load properties - if err := c.setConfigProperty("requirepass", c.config.RequirePassP, true); err != nil { + if err := c.setConfigProperty(RequirePass, c.config.RequirePassP, true); err != nil { return err } - if err := c.setConfigProperty("leaderauth", c.config.LeaderAuthP, true); err != nil { + if err := c.setConfigProperty(LeaderAuth, c.config.LeaderAuthP, true); err != nil { return err } - if err := c.setConfigProperty("protected-mode", c.config.ProtectedModeP, true); err != nil { + if err := c.setConfigProperty(ProtectedMode, c.config.ProtectedModeP, true); err != nil { return err } - if err := c.setConfigProperty("maxmemory", c.config.MaxMemoryP, true); err != nil { + if err := c.setConfigProperty(MaxMemory, c.config.MaxMemoryP, true); err != nil { + return err + } + if err := c.setConfigProperty(AutoGC, c.config.AutoGCP, true); err != nil { return err } return nil @@ -115,17 +128,27 @@ func (c *Controller) setConfigProperty(name, value string, fromLoad bool) error switch name { default: return fmt.Errorf("Unsupported CONFIG parameter: %s", name) - case "requirepass": + case RequirePass: c.config.RequirePass = value - case "leaderauth": + case LeaderAuth: c.config.LeaderAuth = value - case "maxmemory": + case AutoGC: + if value == "" { + c.config.AutoGC = 0 + } else { + gc, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + c.config.AutoGC = gc + } + case MaxMemory: sz, ok := parseMemSize(value) if !ok { return fmt.Errorf("Invalid argument '%s' for CONFIG SET '%s'", value, name) } c.config.MaxMemory = sz - case "protected-mode": + case ProtectedMode: switch strings.ToLower(value) { case "": if fromLoad { @@ -159,13 +182,15 @@ func (c *Controller) getConfigProperty(name string) string { switch name { default: return "" - case "requirepass": + case AutoGC: + return strconv.FormatUint(c.config.AutoGC, 10) + case RequirePass: return c.config.RequirePass - case "leaderauth": + case LeaderAuth: return c.config.LeaderAuth - case "protected-mode": + case ProtectedMode: return c.config.ProtectedMode - case "maxmemory": + case MaxMemory: return formatMemSize(c.config.MaxMemory) } } @@ -190,6 +215,7 @@ func (c *Controller) writeConfig(writeProperties bool) error { c.config.LeaderAuthP = c.config.LeaderAuth c.config.ProtectedModeP = c.config.ProtectedMode c.config.MaxMemoryP = formatMemSize(c.config.MaxMemory) + c.config.AutoGCP = strconv.FormatUint(c.config.AutoGC, 10) } var data []byte data, err = json.MarshalIndent(c.config, "", "\t") @@ -242,7 +268,7 @@ func (c *Controller) cmdConfigSet(msg *server.Message) (res string, err error) { } var value string if vs, value, ok = tokenval(vs); !ok { - if strings.ToLower(name) != "requirepass" { + if strings.ToLower(name) != RequirePass { return "", errInvalidNumberOfArguments } } diff --git a/controller/controller.go b/controller/controller.go index 6b2d68bc..096ac171 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -97,6 +97,7 @@ type Controller struct { stopBackgroundExpiring bool stopWatchingMemory bool + stopWatchingAutoGC bool outOfMemory bool } @@ -178,11 +179,13 @@ func ListenAndServeEx(host string, port int, dir string, ln *net.Listener, http }() go c.processLives() go c.watchMemory() + go c.watchGC() go c.backgroundExpiring() defer func() { c.mu.Lock() c.stopBackgroundExpiring = true c.stopWatchingMemory = true + c.stopWatchingAutoGC = true c.mu.Unlock() }() handler := func(conn *server.Conn, msg *server.Message, rd *server.AnyReaderWriter, w io.Writer, websocket bool) error { @@ -226,6 +229,30 @@ func ListenAndServeEx(host string, port int, dir string, ln *net.Listener, http return server.ListenAndServe(host, port, protected, handler, opened, closed, ln, http) } +func (c *Controller) watchGC() { + autoGC := c.config.AutoGC + + if autoGC == 0 { + return + } + + t := time.NewTicker(time.Second * time.Duration(autoGC)) + defer t.Stop() + + for range t.C { + func() { + c.mu.RLock() + if c.stopWatchingAutoGC { + c.mu.RUnlock() + return + } + + runtime.GC() + debug.FreeOSMemory() + }() + } +} + func (c *Controller) watchMemory() { t := time.NewTicker(time.Second * 2) defer t.Stop() From f0fb28c68dfff4979113c1037e877ae57c64f9f6 Mon Sep 17 00:00:00 2001 From: w1n2k Date: Thu, 19 Jan 2017 19:00:14 +0300 Subject: [PATCH 2/4] Implemented hot swap of autogc --- controller/controller.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index 096ac171..98d486af 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -230,25 +230,34 @@ func ListenAndServeEx(host string, port int, dir string, ln *net.Listener, http } func (c *Controller) watchGC() { - autoGC := c.config.AutoGC - - if autoGC == 0 { - return - } - - t := time.NewTicker(time.Second * time.Duration(autoGC)) + var elapsed uint64 + t := time.NewTicker(time.Second) defer t.Stop() for range t.C { + elapsed++ + c.mu.RLock() + autoGC := c.config.AutoGC + c.mu.RUnlock() + func() { - c.mu.RLock() - if c.stopWatchingAutoGC { - c.mu.RUnlock() + if autoGC == 0 { return } + if c.stopWatchingAutoGC { + return + } + + if elapsed < autoGC { + return + } + + c.mu.RLock() runtime.GC() debug.FreeOSMemory() + c.mu.RUnlock() + elapsed = 0 }() } } From 69b62e97e312247e68774b39c5357724ef81118f Mon Sep 17 00:00:00 2001 From: w1n2k Date: Fri, 20 Jan 2017 12:09:39 +0300 Subject: [PATCH 3/4] Lock refactoring --- controller/controller.go | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index 98d486af..f415bae7 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -230,35 +230,32 @@ func ListenAndServeEx(host string, port int, dir string, ln *net.Listener, http } func (c *Controller) watchGC() { - var elapsed uint64 t := time.NewTicker(time.Second) defer t.Stop() + s := time.Now() for range t.C { - elapsed++ c.mu.RLock() + + if c.stopWatchingAutoGC { + c.mu.RUnlock() + return + } + autoGC := c.config.AutoGC c.mu.RUnlock() - func() { - if autoGC == 0 { - return - } + if autoGC == 0 { + continue + } - if c.stopWatchingAutoGC { - return - } + if time.Now().Sub(s) < time.Second*time.Duration(autoGC) { + continue + } - if elapsed < autoGC { - return - } - - c.mu.RLock() - runtime.GC() - debug.FreeOSMemory() - c.mu.RUnlock() - elapsed = 0 - }() + runtime.GC() + debug.FreeOSMemory() + s = time.Now() } } From 6b92d923d84ba603ef7b37a48c1b75665b0fbfc6 Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Sat, 21 Jan 2017 17:10:43 -0700 Subject: [PATCH 4/4] added debug log --- controller/controller.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/controller/controller.go b/controller/controller.go index f415bae7..709c9499 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -253,8 +253,19 @@ func (c *Controller) watchGC() { continue } + var mem1, mem2 runtime.MemStats + runtime.ReadMemStats(&mem1) + log.Debugf("autogc(before): "+ + "alloc: %v, heap_alloc: %v, heap_released: %v", + mem1.Alloc, mem1.HeapAlloc, mem1.HeapReleased) + runtime.GC() debug.FreeOSMemory() + + runtime.ReadMemStats(&mem2) + log.Debugf("autogc(after): "+ + "alloc: %v, heap_alloc: %v, heap_released: %v", + mem2.Alloc, mem2.HeapAlloc, mem2.HeapReleased) s = time.Now() } }