wrap cache with loadingCache

This commit is contained in:
Umputun
2018-01-07 20:51:02 -06:00
parent 1c696f5a38
commit 18a24ca738
3 changed files with 79 additions and 64 deletions
+5 -6
View File
@@ -10,7 +10,6 @@ import (
"github.com/go-chi/chi"
"github.com/go-chi/render"
cache "github.com/patrickmn/go-cache"
"github.com/umputun/remark/app/migrator"
"github.com/umputun/remark/app/rest/auth"
@@ -23,7 +22,7 @@ type admin struct {
dataService store.Service
exporter migrator.Exporter
importer migrator.Importer
respCache *cache.Cache
respCache *loadingCache
}
func (a *admin) routes() chi.Router {
@@ -49,7 +48,7 @@ func (a *admin) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't delete comment")
return
}
a.respCache.Flush()
a.respCache.flush()
render.Status(r, http.StatusOK)
render.JSON(w, r, JSON{"id": id, "loc": locator})
}
@@ -64,7 +63,7 @@ func (a *admin) setBlockCtrl(w http.ResponseWriter, r *http.Request) {
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set blocking status")
return
}
a.respCache.Flush()
a.respCache.flush()
render.JSON(w, r, JSON{"user_id": userID, "site_id": siteID, "block": blockStatus})
}
@@ -79,7 +78,7 @@ func (a *admin) setPinCtrl(w http.ResponseWriter, r *http.Request) {
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set pin status")
return
}
a.respCache.Flush()
a.respCache.flush()
render.JSON(w, r, JSON{"id": commentID, "loc": locator, "pin": pinStatus})
}
@@ -108,7 +107,7 @@ func (a *admin) importCtrl(w http.ResponseWriter, r *http.Request) {
if err := a.importer.Import(r.Body, siteID); err != nil {
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "import failed")
}
a.respCache.Flush()
a.respCache.flush()
}
func (a *admin) checkBlocked(locator store.Locator, user store.User) bool {
+31
View File
@@ -0,0 +1,31 @@
package rest
import (
"time"
cache "github.com/patrickmn/go-cache"
)
type loadingCache struct {
bytesCache *cache.Cache
}
func newLoadingCache(defaultExpiration, cleanupInterval time.Duration) *loadingCache {
return &loadingCache{bytesCache: cache.New(defaultExpiration, cleanupInterval)}
}
func (lc *loadingCache) get(key string, ttl time.Duration, fn func() ([]byte, error)) (data []byte, err error) {
if b, ok := lc.bytesCache.Get(key); ok {
return b.([]byte), nil
}
if data, err = fn(); err != nil {
return data, err
}
lc.bytesCache.Set(key, data, ttl)
return data, nil
}
func (lc *loadingCache) flush() {
lc.bytesCache.Flush()
}
+43 -58
View File
@@ -15,7 +15,6 @@ import (
"github.com/go-chi/render"
"github.com/gorilla/context"
"github.com/gorilla/sessions"
"github.com/patrickmn/go-cache"
"github.com/pkg/errors"
"github.com/umputun/remark/app/migrator"
@@ -38,7 +37,7 @@ type Server struct {
DevMode bool
mod admin
respCache *cache.Cache
respCache *loadingCache
}
// Run the lister and request's router, activate rest server
@@ -59,7 +58,7 @@ func (s *Server) Run() {
}
// cache for responses. Flushes completely on any modification
s.respCache = cache.New(4*time.Hour, 15*time.Minute)
s.respCache = newLoadingCache(4*time.Hour, 15*time.Minute)
router := chi.NewRouter()
router.Use(middleware.RealIP, Recoverer)
@@ -151,7 +150,7 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
return
}
s.respCache.Flush() // reset all caches
s.respCache.flush() // reset all caches
render.Status(r, http.StatusAccepted)
render.JSON(w, r, JSON{"id": id, "loc": comment.Locator})
@@ -196,7 +195,7 @@ func (s *Server) updateCommentCtrl(w http.ResponseWriter, r *http.Request) {
return
}
s.respCache.Flush() // reset all caches
s.respCache.flush() // reset all caches
render.JSON(w, r, res)
}
@@ -213,7 +212,7 @@ func (s *Server) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
return
}
s.respCache.Flush()
s.respCache.flush()
render.Status(r, http.StatusOK)
render.JSON(w, r, JSON{"id": id, "loc": locator})
@@ -225,62 +224,54 @@ func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
log.Printf("[DEBUG] get comments for %+v", locator)
cacheKey := r.URL.String()
if b, ok := s.respCache.Get(cacheKey); ok {
renderJSONFromBytes(w, r, b.([]byte))
return
}
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
comments, err := s.DataService.Find(store.Request{Locator: locator, Sort: r.URL.Query().Get("sort")})
if err != nil {
return nil, err
}
comments = s.mod.maskBlockedUsers(comments)
var b []byte
switch r.URL.Query().Get("format") {
case "tree":
b, err = encodeJSONWithHTML(format.MakeTree(comments, r.URL.Query().Get("sort")))
default:
b, err = encodeJSONWithHTML(comments)
}
return b, err
})
comments, err := s.DataService.Find(store.Request{Locator: locator, Sort: r.URL.Query().Get("sort")})
if err != nil {
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't load comments comment")
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't find comments")
return
}
comments = s.mod.maskBlockedUsers(comments)
var bdata []byte
switch r.URL.Query().Get("format") {
case "tree":
bdata, err = encodeJSONWithHTML(format.MakeTree(comments, r.URL.Query().Get("sort")))
default:
bdata, err = encodeJSONWithHTML(comments)
}
if err != nil {
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't encode comments")
return
}
s.respCache.Set(cacheKey, bdata, time.Hour)
renderJSONFromBytes(w, r, bdata)
renderJSONFromBytes(w, r, data)
}
// GET /last/{max}?site=siteID - last comments for the siteID, across all posts, sorted by time
func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] get last comments for %s", r.URL.Query().Get("site"))
max, err := strconv.Atoi(chi.URLParam(r, "max"))
if err != nil {
max = 0
}
cacheKey := r.URL.String()
if b, ok := s.respCache.Get(cacheKey); ok {
renderJSONFromBytes(w, r, b.([]byte))
return
}
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
comments, err := s.DataService.Last(store.Locator{SiteID: r.URL.Query().Get("site")}, max)
if err != nil {
return nil, err
}
comments = s.mod.maskBlockedUsers(comments)
return encodeJSONWithHTML(comments)
})
comments, err := s.DataService.Last(store.Locator{SiteID: r.URL.Query().Get("site")}, max)
if err != nil {
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't get last comments")
return
}
comments = s.mod.maskBlockedUsers(comments)
b, err := encodeJSONWithHTML(comments)
if err != nil {
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't encode comments")
return
}
s.respCache.Set(cacheKey, b, time.Hour)
renderJSONFromBytes(w, r, b)
renderJSONFromBytes(w, r, data)
return
}
// GET /id/{id}?site=siteID&url=post-url - gets a comment by id
@@ -307,25 +298,19 @@ func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] get comments by userID %s", userID)
cacheKey := r.URL.String()
if b, ok := s.respCache.Get(cacheKey); ok {
renderJSONFromBytes(w, r, b.([]byte))
return
}
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
comments, err := s.DataService.GetByUser(store.Locator{SiteID: r.URL.Query().Get("site")}, userID)
if err != nil {
return nil, err
}
return encodeJSONWithHTML(comments)
})
comments, err := s.DataService.GetByUser(store.Locator{SiteID: r.URL.Query().Get("site")}, userID)
if err != nil {
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by user id")
return
}
b, err := encodeJSONWithHTML(comments)
if err != nil {
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't encode comments")
return
}
s.respCache.Set(cacheKey, b, time.Hour)
renderJSONFromBytes(w, r, b)
renderJSONFromBytes(w, r, data)
}
// GET /user - returns user info
@@ -379,7 +364,7 @@ func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) {
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't vote for comment")
return
}
s.respCache.Flush()
s.respCache.flush()
render.JSON(w, r, JSON{"id": comment.ID, "score": comment.Score})
}