Skip to content

Commit

Permalink
Merge pull request #305 from systemli/Clear-Response-Cache-when-Ticke…
Browse files Browse the repository at this point in the history
…r-or-Messages-change

⚡️Clear Response Cache when Ticker or Messages change
  • Loading branch information
0x46616c6b authored Apr 27, 2024
2 parents 6bb8df9 + 63df960 commit ced6edc
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 9 deletions.
6 changes: 4 additions & 2 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,19 @@ type handler struct {
config config.Config
storage storage.Storage
bridges bridge.Bridges
cache *cache.Cache
}

func API(config config.Config, store storage.Storage, log *logrus.Logger) *gin.Engine {
inMemoryCache := cache.NewCache(5 * time.Minute)

handler := handler{
config: config,
storage: store,
bridges: bridge.RegisterBridges(config, store),
cache: inMemoryCache,
}

inMemoryCache := cache.NewCache(5 * time.Minute)

gin.SetMode(gin.ReleaseMode)

r := gin.New()
Expand Down
15 changes: 15 additions & 0 deletions internal/api/messages.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package api

import (
"fmt"
"net/http"
"strings"

"github.com/gin-gonic/gin"
geojson "github.com/paulmach/go.geojson"
Expand Down Expand Up @@ -105,5 +107,18 @@ func (h *handler) DeleteMessage(c *gin.Context) {
return
}

h.ClearMessagesCache(&ticker)

c.JSON(http.StatusOK, response.SuccessResponse(map[string]interface{}{}))
}

// ClearMessagesCache clears the cache for the timeline endpoint of a ticker
func (h *handler) ClearMessagesCache(ticker *storage.Ticker) {
h.cache.Range(func(key, value interface{}) bool {
if strings.HasPrefix(key.(string), fmt.Sprintf("response:%s:/v1/timeline", ticker.Domain)) {
h.cache.Delete(key)
}

return true
})
}
11 changes: 9 additions & 2 deletions internal/api/messages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"net/http/httptest"
"strings"
"testing"
"time"

"github.com/gin-gonic/gin"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"github.com/systemli/ticker/internal/cache"
"github.com/systemli/ticker/internal/config"
"github.com/systemli/ticker/internal/storage"
)
Expand All @@ -19,6 +21,7 @@ type MessagesTestSuite struct {
ctx *gin.Context
store *storage.MockStorage
cfg config.Config
cache *cache.Cache
suite.Suite
}

Expand All @@ -32,6 +35,7 @@ func (s *MessagesTestSuite) Run(name string, subtest func()) {
s.ctx, _ = gin.CreateTestContext(s.w)
s.store = &storage.MockStorage{}
s.cfg = config.LoadConfig("")
s.cache = cache.NewCache(time.Minute)

subtest()
})
Expand Down Expand Up @@ -188,16 +192,18 @@ func (s *MessagesTestSuite) TestDeleteMessage() {
s.True(s.store.AssertExpectations(s.T()))
})

s.Run("when database returns message", func() {
ticker := storage.Ticker{ID: 1}
s.Run("happy path", func() {
ticker := storage.Ticker{ID: 1, Domain: "localhost"}
message := storage.Message{ID: 1}
s.cache.Set("response:localhost:/v1/timeline", true, time.Minute)
s.ctx.Set("ticker", ticker)
s.ctx.Set("message", message)
s.store.On("DeleteMessage", message).Return(nil).Once()
h := s.handler()
h.DeleteMessage(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.Nil(s.cache.Get("response:localhost:/v1/timeline"))
s.True(s.store.AssertExpectations(s.T()))
})
}
Expand All @@ -206,6 +212,7 @@ func (s *MessagesTestSuite) handler() handler {
return handler{
storage: s.store,
config: s.cfg,
cache: s.cache,
}
}

Expand Down
18 changes: 18 additions & 0 deletions internal/api/tickers.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package api

import (
"fmt"
"net/http"
"strconv"
"strings"

"github.com/gin-gonic/gin"
"github.com/mattn/go-mastodon"
Expand Down Expand Up @@ -85,6 +87,8 @@ func (h *handler) PutTicker(c *gin.Context) {
return
}

h.ClearTickerCache(&ticker)

c.JSON(http.StatusOK, response.SuccessResponse(map[string]interface{}{"ticker": response.TickerResponse(ticker, h.config)}))
}

Expand Down Expand Up @@ -251,6 +255,8 @@ func (h *handler) DeleteTicker(c *gin.Context) {
return
}

h.ClearTickerCache(&ticker)

c.JSON(http.StatusOK, response.SuccessResponse(map[string]interface{}{}))
}

Expand Down Expand Up @@ -312,9 +318,21 @@ func (h *handler) ResetTicker(c *gin.Context) {
return
}

h.ClearTickerCache(&ticker)

c.JSON(http.StatusOK, response.SuccessResponse(map[string]interface{}{"ticker": response.TickerResponse(ticker, h.config)}))
}

// ClearTickerCache clears the cache for the init endpoint of a ticker
func (h *handler) ClearTickerCache(ticker *storage.Ticker) {
h.cache.Range(func(key, value interface{}) bool {
if strings.HasPrefix(key.(string), fmt.Sprintf("response:%s:/v1/init", ticker.Domain)) {
h.cache.Delete(key)
}
return true
})
}

func updateTicker(t *storage.Ticker, c *gin.Context) error {
var body struct {
Domain string `json:"domain" binding:"required"`
Expand Down
21 changes: 16 additions & 5 deletions internal/api/tickers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import (
"net/http/httptest"
"strings"
"testing"
"time"

"github.com/gin-gonic/gin"
"github.com/mattn/go-mastodon"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"github.com/systemli/ticker/internal/cache"
"github.com/systemli/ticker/internal/config"
"github.com/systemli/ticker/internal/storage"
)
Expand All @@ -22,6 +24,7 @@ type TickerTestSuite struct {
ctx *gin.Context
store *storage.MockStorage
cfg config.Config
cache *cache.Cache
suite.Suite
}

Expand All @@ -35,6 +38,7 @@ func (s *TickerTestSuite) Run(name string, subtest func()) {
s.ctx, _ = gin.CreateTestContext(s.w)
s.store = &storage.MockStorage{}
s.cfg = config.LoadConfig("")
s.cache = cache.NewCache(time.Minute)

subtest()
})
Expand Down Expand Up @@ -177,8 +181,9 @@ func (s *TickerTestSuite) TestPutTicker() {
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns ticker", func() {
s.Run("happy path", func() {
s.ctx.Set("ticker", storage.Ticker{})
s.cache.Set("response:localhost:/v1/init", true, time.Minute)
body := `{"domain":"localhost","title":"title","description":"description"}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
Expand All @@ -187,6 +192,7 @@ func (s *TickerTestSuite) TestPutTicker() {
h.PutTicker(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.Nil(s.cache.Get("response:localhost:/v1/init"))
s.store.AssertExpectations(s.T())
})
}
Expand Down Expand Up @@ -437,15 +443,17 @@ func (s *TickerTestSuite) TestDeleteTicker() {
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns ticker", func() {
s.ctx.Set("ticker", storage.Ticker{})
s.Run("happy path", func() {
s.cache.Set("response:localhost:/v1/init", true, time.Minute)
s.ctx.Set("ticker", storage.Ticker{Domain: "localhost"})
s.store.On("DeleteMessages", mock.Anything).Return(nil)
s.store.On("DeleteUploadsByTicker", mock.Anything).Return(nil)
s.store.On("DeleteTicker", mock.Anything).Return(nil)
h := s.handler()
h.DeleteTicker(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.Nil(s.cache.Get("response:localhost:/v1/init"))
s.store.AssertExpectations(s.T())
})
}
Expand Down Expand Up @@ -538,8 +546,9 @@ func (s *TickerTestSuite) TestResetTicker() {
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns ticker", func() {
s.ctx.Set("ticker", storage.Ticker{})
s.Run("happy path", func() {
s.cache.Set("response:localhost:/v1/init", true, time.Minute)
s.ctx.Set("ticker", storage.Ticker{Domain: "localhost"})
s.store.On("DeleteMessages", mock.Anything).Return(nil).Once()
s.store.On("DeleteUploadsByTicker", mock.Anything).Return(nil).Once()
s.store.On("SaveTicker", mock.Anything).Return(nil).Once()
Expand All @@ -548,6 +557,7 @@ func (s *TickerTestSuite) TestResetTicker() {
h.ResetTicker(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.Nil(s.cache.Get("response:localhost:/v1/init"))
s.store.AssertExpectations(s.T())
})
}
Expand All @@ -556,6 +566,7 @@ func (s *TickerTestSuite) handler() handler {
return handler{
storage: s.store,
config: s.cfg,
cache: s.cache,
}
}

Expand Down

0 comments on commit ced6edc

Please sign in to comment.