From 9def7e1a140150ae24d32ccb9548f674592924f2 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Fri, 28 Apr 2017 13:18:04 -0700 Subject: [PATCH] Don't deepcopy job when retrieving copy of Alloc This PR removes deepcopying of the job attached to the allocation in the alloc runner. This operation is called very often so removing reflect from the code path and the potentially large number of mallocs need to create a job reduced memory and cpu pressure. --- client/alloc_runner.go | 9 +++++++++ client/util.go | 12 ++++++++---- command/agent/fs_endpoint.go | 3 ++- command/agent/http.go | 16 ++-------------- nomad/structs/structs.go | 12 ++++++++++++ 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/client/alloc_runner.go b/client/alloc_runner.go index 8ca6f671e..1bb6d1a65 100644 --- a/client/alloc_runner.go +++ b/client/alloc_runner.go @@ -279,8 +279,17 @@ func copyTaskStates(states map[string]*structs.TaskState) map[string]*structs.Ta // Alloc returns the associated allocation func (r *AllocRunner) Alloc() *structs.Allocation { r.allocLock.Lock() + + // Clear the job before copying + job := r.alloc.Job + r.alloc.Job = nil + alloc := r.alloc.Copy() + // Restore + r.alloc.Job = job + alloc.Job = job + // The status has explicitly been set. if r.allocClientStatus != "" || r.allocClientDescription != "" { alloc.ClientStatus = r.allocClientStatus diff --git a/client/util.go b/client/util.go index 3f7cef981..ee78ebac2 100644 --- a/client/util.go +++ b/client/util.go @@ -1,6 +1,7 @@ package client import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -9,6 +10,7 @@ import ( "path/filepath" "github.com/hashicorp/nomad/nomad/structs" + "github.com/ugorji/go/codec" ) type allocTuple struct { @@ -78,15 +80,17 @@ func shuffleStrings(list []string) { // persistState is used to help with saving state func persistState(path string, data interface{}) error { - buf, err := json.Marshal(data) - if err != nil { - return fmt.Errorf("failed to encode state: %v", err) + var buf bytes.Buffer + enc := codec.NewEncoder(&buf, structs.JsonHandlePretty) + if err := enc.Encode(data); err != nil { + return err } + if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { return fmt.Errorf("failed to make dirs for %s: %v", path, err) } tmpPath := path + ".tmp" - if err := ioutil.WriteFile(tmpPath, buf, 0600); err != nil { + if err := ioutil.WriteFile(tmpPath, buf.Bytes(), 0600); err != nil { return fmt.Errorf("failed to save state to tmp: %v", err) } if err := os.Rename(tmpPath, path); err != nil { diff --git a/command/agent/fs_endpoint.go b/command/agent/fs_endpoint.go index 7658e6628..ad6847c4e 100644 --- a/command/agent/fs_endpoint.go +++ b/command/agent/fs_endpoint.go @@ -21,6 +21,7 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/hashicorp/nomad/client/allocdir" + "github.com/hashicorp/nomad/nomad/structs" "github.com/hpcloud/tail/watch" "github.com/ugorji/go/codec" ) @@ -290,7 +291,7 @@ func NewStreamFramer(out io.WriteCloser, plainTxt bool, heartbeatRate, batchWindow time.Duration, frameSize int) *StreamFramer { // Create a JSON encoder - enc := codec.NewEncoder(out, jsonHandle) + enc := codec.NewEncoder(out, structs.JsonHandle) // Create the heartbeat and flush ticker heartbeat := time.NewTicker(heartbeatRate) diff --git a/command/agent/http.go b/command/agent/http.go index 8dbfca78e..147a9df08 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -29,18 +29,6 @@ const ( scadaHTTPAddr = "SCADA" ) -var ( - // jsonHandle and jsonHandlePretty are the codec handles to JSON encode - // structs. The pretty handle will add indents for easier human consumption. - jsonHandle = &codec.JsonHandle{ - HTMLCharsAsIs: true, - } - jsonHandlePretty = &codec.JsonHandle{ - HTMLCharsAsIs: true, - Indent: 4, - } -) - // HTTPServer is used to wrap an Agent and expose it over an HTTP interface type HTTPServer struct { agent *Agent @@ -248,13 +236,13 @@ func (s *HTTPServer) wrap(handler func(resp http.ResponseWriter, req *http.Reque if obj != nil { var buf bytes.Buffer if prettyPrint { - enc := codec.NewEncoder(&buf, jsonHandlePretty) + enc := codec.NewEncoder(&buf, structs.JsonHandlePretty) err = enc.Encode(obj) if err == nil { buf.Write([]byte("\n")) } } else { - enc := codec.NewEncoder(&buf, jsonHandle) + enc := codec.NewEncoder(&buf, structs.JsonHandle) err = enc.Encode(obj) } if err != nil { diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 924c39009..3826ed784 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -4231,6 +4231,18 @@ var MsgpackHandle = func() *codec.MsgpackHandle { return h }() +var ( + // JsonHandle and JsonHandlePretty are the codec handles to JSON encode + // structs. The pretty handle will add indents for easier human consumption. + JsonHandle = &codec.JsonHandle{ + HTMLCharsAsIs: true, + } + JsonHandlePretty = &codec.JsonHandle{ + HTMLCharsAsIs: true, + Indent: 4, + } +) + var HashiMsgpackHandle = func() *hcodec.MsgpackHandle { h := &hcodec.MsgpackHandle{RawToString: true}