diff --git a/sui/api/request.go b/sui/api/request.go index 93550fb9..5e3e0b17 100644 --- a/sui/api/request.go +++ b/sui/api/request.go @@ -107,6 +107,7 @@ func (r *Request) Render() (string, int, error) { configText := "" cacheStore := "" cacheTime := 0 + dateCacheTime := 0 configSel := doc.Find("script[name=config]") if configSel != nil && configSel.Length() > 0 { @@ -132,6 +133,7 @@ func (r *Request) Render() (string, int, error) { // Cache store cacheStore = conf.CacheStore cacheTime = conf.Cache + dateCacheTime = conf.DataCache } dataText := "" @@ -163,6 +165,7 @@ func (r *Request) Render() (string, int, error) { Config: configText, CacheStore: cacheStore, CacheTime: time.Duration(cacheTime) * time.Second, + DataCacheTime: time.Duration(dateCacheTime) * time.Second, } go core.SetCache(r.File, c) go log.Trace("[SUI] The page %s is cached file=%s", r.Request.URL.Path, r.File) @@ -174,28 +177,49 @@ func (r *Request) Render() (string, int, error) { return "", code, err } - data := r.Request.NewData() - if c.Data != "" { - err = r.Request.ExecStringMerge(data, c.Data) - if err != nil { - return "", 500, fmt.Errorf("data error, please re-complie the page %s", err.Error()) + requestHash := r.Hash() + data := core.Data{} + dataCacheKey := fmt.Sprintf("data:%s", requestHash) + dataHitCache := false + + // Read from data cache directly + if !r.Request.DisableCache() && c.DataCacheTime > 0 && c.CacheStore != "" { + data, dataHitCache = c.GetData(dataCacheKey) + if dataHitCache { + log.Trace("[SUI] The page %s data is cached %v file=%s key=%s", r.Request.URL.Path, c.DataCacheTime, r.File, dataCacheKey) } } - if c.Global != "" { - global, err := r.Request.ExecString(c.Global) - if err != nil { - return "", 500, fmt.Errorf("global data error, please re-complie the page %s", err.Error()) + if !dataHitCache { + // Request the data + data = r.Request.NewData() + if c.Data != "" { + err = r.Request.ExecStringMerge(data, c.Data) + if err != nil { + return "", 500, fmt.Errorf("data error, please re-complie the page %s", err.Error()) + } + } + + if c.Global != "" { + global, err := r.Request.ExecString(c.Global) + if err != nil { + return "", 500, fmt.Errorf("global data error, please re-complie the page %s", err.Error()) + } + data["$global"] = global + } + + // Save to The Cache + if c.DataCacheTime > 0 && c.CacheStore != "" { + go c.SetData(dataCacheKey, data, c.DataCacheTime) } - data["$global"] = global } // Read from cache directly - key := fmt.Sprintf("page:%s:%s", r.Hash(), data.Hash()) + key := fmt.Sprintf("page:%s:%s", requestHash, data.Hash()) if !r.Request.DisableCache() && c.CacheTime > 0 && c.CacheStore != "" { html, exists := c.GetHTML(key) if exists { - log.Trace("[SUI] The page %s is cached %v file=%s", r.Request.URL.Path, c.CacheTime, r.File) + log.Trace("[SUI] The page %s is cached %v file=%s key=%s", r.Request.URL.Path, c.CacheTime, r.File, key) return html, 200, nil } } diff --git a/sui/core/cache.go b/sui/core/cache.go index f7f1cac0..bb9a4b38 100644 --- a/sui/core/cache.go +++ b/sui/core/cache.go @@ -3,6 +3,7 @@ package core import ( "time" + jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou/store" "github.com/yaoapp/kun/log" ) @@ -17,6 +18,7 @@ type Cache struct { HTML string CacheStore string CacheTime time.Duration + DataCacheTime time.Duration } const ( @@ -92,6 +94,46 @@ func (c *Cache) GetHTML(hash string) (string, bool) { return v.(string), true } +// GetData get the data +func (c *Cache) GetData(hash string) (Data, bool) { + store, has := store.Pools[c.CacheStore] + if !has { + log.Warn(`[SUI] The cache store "%s" is not found`, c.CacheStore) + return Data{}, false + } + + v, has := store.Get(hash) + if !has { + return Data{}, false + } + + data := Data{} + err := jsoniter.Unmarshal(v.([]byte), &data) + if err != nil { + log.Error(`[SUI] The data is not a valid json: %s`, err.Error()) + return Data{}, false + } + + return data, true +} + +// SetData set the data +func (c *Cache) SetData(hash string, data Data, ttl time.Duration) { + store, has := store.Pools[c.CacheStore] + if !has { + log.Warn(`[SUI] The cache store "%s" is not found`, c.CacheStore) + return + } + + raw, err := jsoniter.Marshal(data) + if err != nil { + log.Error(`[SUI] The data is not a valid json: %s`, err.Error()) + return + } + + store.Set(hash, raw, ttl) +} + // SetHTML set the html func (c *Cache) SetHTML(hash, html string, ttl time.Duration) { store, has := store.Pools[c.CacheStore] diff --git a/sui/core/page.go b/sui/core/page.go index 41a64f47..c8ec548b 100644 --- a/sui/core/page.go +++ b/sui/core/page.go @@ -68,6 +68,7 @@ func (page *Page) ExportConfig() string { "guard": page.Config.Guard, "cache_store": page.CacheStore, "cache": page.Config.Cache, + "data_cache": page.Config.DataCache, }) if err != nil { diff --git a/sui/core/types.go b/sui/core/types.go index e4ce05cb..73fd7e00 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -298,6 +298,7 @@ type PageSetting struct { Guard string `json:"guard,omitempty"` CacheStore string `json:"cache_store,omitempty"` Cache int `json:"cache,omitempty"` + DataCache int `json:"data_cache,omitempty"` Description string `json:"description,omitempty"` SEO *PageSEO `json:"seo,omitempty"` }