Add TM2 CRConfig API caching

Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/84750094
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/84750094
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/84750094

Branch: refs/heads/master
Commit: 84750094adcfbda77705ab56806a10a468cc2be8
Parents: ea5a046
Author: Robert Butts <robert.o.bu...@gmail.com>
Authored: Thu Feb 16 14:57:29 2017 -0700
Committer: Dave Neuman <neu...@apache.org>
Committed: Sun Feb 19 18:56:45 2017 -0700

----------------------------------------------------------------------
 .../traffic_monitor/manager/datarequest.go      | 21 ++++++--
 .../trafficopswrapper/trafficopswrapper.go      | 56 ++++++++++++++++++--
 .../traffic_monitor/version.go                  |  2 +-
 3 files changed, 68 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/84750094/traffic_monitor_golang/traffic_monitor/manager/datarequest.go
----------------------------------------------------------------------
diff --git a/traffic_monitor_golang/traffic_monitor/manager/datarequest.go 
b/traffic_monitor_golang/traffic_monitor/manager/datarequest.go
index fd6ad9e..86ebe01 100644
--- a/traffic_monitor_golang/traffic_monitor/manager/datarequest.go
+++ b/traffic_monitor_golang/traffic_monitor/manager/datarequest.go
@@ -547,15 +547,26 @@ func WrapParams(f SrvFunc, contentType string) 
http.HandlerFunc {
        }
 }
 
-func srvTRConfig(opsConfig OpsConfigThreadsafe, toSession 
towrap.ITrafficOpsSession) ([]byte, error) {
+func WrapAgeErr(errorCount threadsafe.Uint, f func() ([]byte, time.Time, 
error), contentType string) http.HandlerFunc {
+       return func(w http.ResponseWriter, r *http.Request) {
+               bytes, contentTime, err := f()
+               _, code := WrapErrCode(errorCount, r.URL.EscapedPath(), bytes, 
err)
+               w.Header().Set("Content-Type", contentType)
+               w.Header().Set("Age", fmt.Sprintf("%.0f", 
time.Since(contentTime).Seconds()))
+               w.WriteHeader(code)
+               log.Write(w, bytes, r.URL.EscapedPath())
+       }
+}
+
+func srvTRConfig(opsConfig OpsConfigThreadsafe, toSession 
towrap.ITrafficOpsSession) ([]byte, time.Time, error) {
        cdnName := opsConfig.Get().CdnName
        if toSession == nil {
-               return nil, fmt.Errorf("Unable to connect to Traffic Ops")
+               return nil, time.Time{}, fmt.Errorf("Unable to connect to 
Traffic Ops")
        }
        if cdnName == "" {
-               return nil, fmt.Errorf("No CDN Configured")
+               return nil, time.Time{}, fmt.Errorf("No CDN Configured")
        }
-       return toSession.CRConfigRaw(cdnName)
+       return toSession.LastCRConfig(cdnName)
 }
 
 func srvTRState(params url.Values, localStates peer.CRStatesThreadsafe, 
combinedStates peer.CRStatesThreadsafe) ([]byte, error) {
@@ -725,7 +736,7 @@ func MakeDispatchMap(
        }
 
        dispatchMap := map[string]http.HandlerFunc{
-               "/publish/CrConfig": wrap(WrapErr(errorCount, func() ([]byte, 
error) {
+               "/publish/CrConfig": wrap(WrapAgeErr(errorCount, func() 
([]byte, time.Time, error) {
                        return srvTRConfig(opsConfig, toSession)
                }, ContentTypeJSON)),
                "/publish/CrStates": wrap(WrapParams(func(params url.Values, 
path string) ([]byte, int) {

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/84750094/traffic_monitor_golang/traffic_monitor/trafficopswrapper/trafficopswrapper.go
----------------------------------------------------------------------
diff --git 
a/traffic_monitor_golang/traffic_monitor/trafficopswrapper/trafficopswrapper.go 
b/traffic_monitor_golang/traffic_monitor/trafficopswrapper/trafficopswrapper.go
index b86d844..a9d9e07 100644
--- 
a/traffic_monitor_golang/traffic_monitor/trafficopswrapper/trafficopswrapper.go
+++ 
b/traffic_monitor_golang/traffic_monitor/trafficopswrapper/trafficopswrapper.go
@@ -23,6 +23,7 @@ import (
        "encoding/json"
        "fmt"
        "sync"
+       "time"
 
        
"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
        
"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/crconfig"
@@ -32,6 +33,7 @@ import (
 // ITrafficOpsSession provides an interface to the Traffic Ops client, so it 
may be wrapped or mocked.
 type ITrafficOpsSession interface {
        CRConfigRaw(cdn string) ([]byte, error)
+       LastCRConfig(cdn string) ([]byte, time.Time, error)
        TrafficMonitorConfigMap(cdn string) (*to.TrafficMonitorConfigMap, error)
        Set(session *to.Session)
        URL() (string, error)
@@ -45,15 +47,46 @@ type ITrafficOpsSession interface {
 
 var ErrNilSession = fmt.Errorf("nil session")
 
+type ByteTime struct {
+       bytes []byte
+       time  time.Time
+}
+
+type ByteMapCache struct {
+       cache *map[string]ByteTime
+       m     *sync.RWMutex
+}
+
+func NewByteMapCache() ByteMapCache {
+       return ByteMapCache{m: &sync.RWMutex{}, cache: &map[string]ByteTime{}}
+}
+
+func (c ByteMapCache) Set(key string, newBytes []byte) {
+       c.m.Lock()
+       defer c.m.Unlock()
+       (*c.cache)[key] = ByteTime{bytes: newBytes, time: time.Now()}
+}
+
+func (c ByteMapCache) Get(key string) ([]byte, time.Time) {
+       c.m.RLock()
+       defer c.m.RUnlock()
+       if byteTime, ok := (*c.cache)[key]; !ok {
+               return nil, time.Time{}
+       } else {
+               return byteTime.bytes, byteTime.time
+       }
+}
+
 // TrafficOpsSessionThreadsafe provides access to the Traffic Ops client safe 
for multiple goroutines. This fulfills the ITrafficOpsSession interface.
 type TrafficOpsSessionThreadsafe struct {
-       session **to.Session // pointer-to-pointer, because we're given a 
pointer from the Traffic Ops package, and we don't want to copy it.
-       m       *sync.Mutex
+       session      **to.Session // pointer-to-pointer, because we're given a 
pointer from the Traffic Ops package, and we don't want to copy it.
+       m            *sync.Mutex
+       lastCRConfig ByteMapCache
 }
 
 // NewTrafficOpsSessionThreadsafe returns a new threadsafe 
TrafficOpsSessionThreadsafe wrapping the given `Session`.
 func NewTrafficOpsSessionThreadsafe(s *to.Session) TrafficOpsSessionThreadsafe 
{
-       return TrafficOpsSessionThreadsafe{&s, &sync.Mutex{}}
+       return TrafficOpsSessionThreadsafe{session: &s, m: &sync.Mutex{}, 
lastCRConfig: NewByteMapCache()}
 }
 
 // Set sets the internal Traffic Ops session. This is safe for multiple 
goroutines, being aware they will race.
@@ -95,8 +128,21 @@ func (s TrafficOpsSessionThreadsafe) CRConfigRaw(cdn 
string) ([]byte, error) {
        if ss == nil {
                return nil, ErrNilSession
        }
-       b, _, e := ss.GetCRConfig(cdn)
-       return b, e
+       b, _, err := ss.GetCRConfig(cdn)
+       if err == nil {
+               s.lastCRConfig.Set(cdn, b)
+       }
+       return b, err
+}
+
+// LastCRConfig returns the last CRConfig requested from CRConfigRaw, and the 
time it was returned. This is designed to be used in conjunction with a poller 
which regularly calls CRConfigRaw. If no last CRConfig exists, because 
CRConfigRaw has never been called successfully, this calls CRConfigRaw once to 
try to get the CRConfig from Traffic Ops.
+func (s TrafficOpsSessionThreadsafe) LastCRConfig(cdn string) ([]byte, 
time.Time, error) {
+       crConfig, crConfigTime := s.lastCRConfig.Get(cdn)
+       if crConfig == nil {
+               b, err := s.CRConfigRaw(cdn)
+               return b, time.Now(), err
+       }
+       return crConfig, crConfigTime, nil
 }
 
 // TrafficMonitorConfigMapRaw returns the Traffic Monitor config map from the 
Traffic Ops, directly from the monitoring.json endpoint. This is not usually 
what is needed, rather monitoring needs the snapshotted CRConfig data, which is 
filled in by `TrafficMonitorConfigMap`. This is safe for multiple goroutines.

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/84750094/traffic_monitor_golang/traffic_monitor/version.go
----------------------------------------------------------------------
diff --git a/traffic_monitor_golang/traffic_monitor/version.go 
b/traffic_monitor_golang/traffic_monitor/version.go
index 31b0e57..31569de 100644
--- a/traffic_monitor_golang/traffic_monitor/version.go
+++ b/traffic_monitor_golang/traffic_monitor/version.go
@@ -20,4 +20,4 @@ package main
  */
 
 // Version is the current version of the app, in string form.
-var Version = "2.0.4"
+var Version = "2.0.5"

Reply via email to