diff --git a/go.mod b/go.mod index d69e1bc..33b0c02 100644 --- a/go.mod +++ b/go.mod @@ -9,5 +9,6 @@ require ( github.com/hashicorp/go-hclog v0.9.2 github.com/hashicorp/go-plugin v1.0.1 github.com/kr/pretty v0.1.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible golang.org/x/net v0.0.0-20180826012351-8a410e7b638d ) diff --git a/go.sum b/go.sum index f6cbd91..b206609 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1: github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/pkg/cache.go b/pkg/cache.go new file mode 100644 index 0000000..3fc136c --- /dev/null +++ b/pkg/cache.go @@ -0,0 +1,38 @@ +package main + +import ( + "crypto/sha1" + "encoding/hex" + "time" + + cache "github.com/patrickmn/go-cache" +) + +// Cache is a abstraction over go-cache. +type Cache struct { + cache *cache.Cache +} + +// NewCache creates a go-cache with expiration(ttl) time and cleanupInterval. +func NewCache(ttl time.Duration, cleanupInterval time.Duration) *Cache { + return &Cache{ + cache.New(ttl, cleanupInterval), + } +} + +// Set the value of the key "request" to "rersponse" with default expiration time. +func (c *Cache) Set(request string, response interface{}) { + c.cache.SetDefault(request, response) +} + +// Get the value associated with request from the cache +func (c *Cache) Get(request string) (interface{}, bool) { + return c.cache.Get(request) +} + +// Hash converts the given text string to hash string +func Hash(text string) string { + hash := sha1.New() + hash.Write([]byte(text)) + return hex.EncodeToString(hash.Sum(nil)) +} diff --git a/pkg/zabbix_api.go b/pkg/zabbix_api.go index 0e98b47..0f3da0d 100644 --- a/pkg/zabbix_api.go +++ b/pkg/zabbix_api.go @@ -38,59 +38,66 @@ var httpClient = &http.Client{ Timeout: time.Duration(time.Second * 30), } +var queryCache = NewCache(10*time.Minute, 10*time.Minute) + var zabbixAuth string = "" func (ds *ZabbixDatasource) ZabbixAPIQuery(ctx context.Context, tsdbReq *datasource.DatasourceRequest) (*datasource.DatasourceResponse, error) { - dsInfo := tsdbReq.GetDatasource() - zabbixUrlStr := dsInfo.GetUrl() - zabbixUrl, err := url.Parse(zabbixUrlStr) - if err != nil { - return nil, err - } - - jsonDataStr := dsInfo.GetJsonData() - jsonData, err := simplejson.NewJson([]byte(jsonDataStr)) - if err != nil { - return nil, err - } - - zabbixLogin := jsonData.Get("username").MustString() - // zabbixPassword := jsonData.Get("password").MustString() - ds.logger.Debug("ZabbixAPIQuery", "url", zabbixUrl, "user", zabbixLogin) - - jsonQueries := make([]*simplejson.Json, 0) - for _, query := range tsdbReq.Queries { - json, err := simplejson.NewJson([]byte(query.ModelJson)) - apiMethod := json.GetPath("target", "method").MustString() - apiParams := json.GetPath("target", "params") + result, queryExistInCache := queryCache.Get(Hash(tsdbReq.String())) + if !queryExistInCache { + dsInfo := tsdbReq.GetDatasource() + zabbixUrlStr := dsInfo.GetUrl() + zabbixUrl, err := url.Parse(zabbixUrlStr) if err != nil { return nil, err } - ds.logger.Debug("ZabbixAPIQuery", "method", apiMethod, "params", apiParams) + jsonDataStr := dsInfo.GetJsonData() + jsonData, err := simplejson.NewJson([]byte(jsonDataStr)) + if err != nil { + return nil, err + } - jsonQueries = append(jsonQueries, json) + zabbixLogin := jsonData.Get("username").MustString() + // zabbixPassword := jsonData.Get("password").MustString() + ds.logger.Debug("ZabbixAPIQuery", "url", zabbixUrl, "user", zabbixLogin) + + jsonQueries := make([]*simplejson.Json, 0) + for _, query := range tsdbReq.Queries { + json, err := simplejson.NewJson([]byte(query.ModelJson)) + apiMethod := json.GetPath("target", "method").MustString() + apiParams := json.GetPath("target", "params") + + if err != nil { + return nil, err + } + + ds.logger.Debug("ZabbixAPIQuery", "method", apiMethod, "params", apiParams) + + jsonQueries = append(jsonQueries, json) + } + + if len(jsonQueries) == 0 { + return nil, errors.New("At least one query should be provided") + } + + jsonQuery := jsonQueries[0].Get("target") + apiMethod := jsonQuery.Get("method").MustString() + apiParams := jsonQuery.Get("params") + + result, err = ds.ZabbixRequest(ctx, dsInfo, apiMethod, apiParams) + queryCache.Set(Hash(tsdbReq.String()), result) + if err != nil { + ds.logger.Debug("ZabbixAPIQuery", "error", err) + return nil, errors.New("ZabbixAPIQuery is not implemented yet") + } } - if len(jsonQueries) == 0 { - return nil, errors.New("At least one query should be provided") - } - - jsonQuery := jsonQueries[0].Get("target") - apiMethod := jsonQuery.Get("method").MustString() - apiParams := jsonQuery.Get("params") - - result, err := ds.ZabbixRequest(ctx, dsInfo, apiMethod, apiParams) - if err != nil { - ds.logger.Debug("ZabbixAPIQuery", "error", err) - return nil, errors.New("ZabbixAPIQuery is not implemented yet") - } - - resultByte, err := result.MarshalJSON() + resultByte, _ := result.(*simplejson.Json).MarshalJSON() ds.logger.Debug("ZabbixAPIQuery", "result", string(resultByte)) - return ds.BuildResponse(result) + return ds.BuildResponse(result.(*simplejson.Json)) } func (ds *ZabbixDatasource) BuildResponse(result *simplejson.Json) (*datasource.DatasourceResponse, error) {