Modifying post etag cache to apply to getPostsSince (#4837)
Этот коммит содержится в:
коммит произвёл
enahum
родитель
927eb65978
Коммит
148fd01b54
@@ -1324,7 +1324,7 @@ func getPostsSince(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pchan := Srv.Store.Post().GetPostsSince(id, time)
|
pchan := Srv.Store.Post().GetPostsSince(id, time, true)
|
||||||
|
|
||||||
if !HasPermissionToChannelContext(c, id, model.PERMISSION_READ_CHANNEL) {
|
if !HasPermissionToChannelContext(c, id, model.PERMISSION_READ_CHANNEL) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ func InvalidateCacheForChannelPosts(channelId string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InvalidateCacheForChannelPostsSkipClusterSend(channelId string) {
|
func InvalidateCacheForChannelPostsSkipClusterSend(channelId string) {
|
||||||
Srv.Store.Post().InvalidatePostEtagCache(channelId)
|
Srv.Store.Post().InvalidateLastPostTimeCache(channelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InvalidateCacheForUser(userId string) {
|
func InvalidateCacheForUser(userId string) {
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ type SqlPostStore struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
POSTS_ETAG_CACHE_SIZE = 25000
|
LAST_POST_TIME_CACHE_SIZE = 25000
|
||||||
POSTS_ETAG_CACHE_SEC = 900 // 15 minutes
|
LAST_POST_TIME_CACHE_SEC = 900 // 15 minutes
|
||||||
)
|
)
|
||||||
|
|
||||||
var postEtagCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE)
|
var lastPostTimeCache = utils.NewLru(LAST_POST_TIME_CACHE_SIZE)
|
||||||
|
|
||||||
func ClearPostCaches() {
|
func ClearPostCaches() {
|
||||||
postEtagCache.Purge()
|
lastPostTimeCache.Purge()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSqlPostStore(sqlStore *SqlStore) PostStore {
|
func NewSqlPostStore(sqlStore *SqlStore) PostStore {
|
||||||
@@ -86,7 +86,7 @@ func (s SqlPostStore) Save(post *model.Post) StoreChannel {
|
|||||||
if err := s.GetMaster().Insert(post); err != nil {
|
if err := s.GetMaster().Insert(post); err != nil {
|
||||||
result.Err = model.NewLocAppError("SqlPostStore.Save", "store.sql_post.save.app_error", nil, "id="+post.Id+", "+err.Error())
|
result.Err = model.NewLocAppError("SqlPostStore.Save", "store.sql_post.save.app_error", nil, "id="+post.Id+", "+err.Error())
|
||||||
} else {
|
} else {
|
||||||
time := model.GetMillis()
|
time := post.UpdateAt
|
||||||
|
|
||||||
if post.Type != model.POST_JOIN_LEAVE && post.Type != model.POST_ADD_REMOVE {
|
if post.Type != model.POST_JOIN_LEAVE && post.Type != model.POST_ADD_REMOVE {
|
||||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt, TotalMsgCount = TotalMsgCount + 1 WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId})
|
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt, TotalMsgCount = TotalMsgCount + 1 WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId})
|
||||||
@@ -222,46 +222,46 @@ type etagPosts struct {
|
|||||||
UpdateAt int64
|
UpdateAt int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlPostStore) InvalidatePostEtagCache(channelId string) {
|
func (s SqlPostStore) InvalidateLastPostTimeCache(channelId string) {
|
||||||
postEtagCache.Remove(channelId)
|
lastPostTimeCache.Remove(channelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlPostStore) GetEtag(channelId string, allowFromCache bool) StoreChannel {
|
func (s SqlPostStore) GetEtag(channelId string, allowFromCache bool) StoreChannel {
|
||||||
storeChannel := make(StoreChannel, 1)
|
storeChannel := make(StoreChannel, 1)
|
||||||
metrics := einterfaces.GetMetricsInterface()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
result := StoreResult{}
|
result := StoreResult{}
|
||||||
|
metrics := einterfaces.GetMetricsInterface()
|
||||||
|
|
||||||
if allowFromCache {
|
if allowFromCache {
|
||||||
if cacheItem, ok := postEtagCache.Get(channelId); ok {
|
if cacheItem, ok := lastPostTimeCache.Get(channelId); ok {
|
||||||
if metrics != nil {
|
if metrics != nil {
|
||||||
metrics.IncrementMemCacheHitCounter("Post Etag")
|
metrics.IncrementMemCacheHitCounter("Last Post Time")
|
||||||
}
|
}
|
||||||
result.Data = cacheItem.(string)
|
result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, cacheItem.(int64))
|
||||||
storeChannel <- result
|
storeChannel <- result
|
||||||
close(storeChannel)
|
close(storeChannel)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
if metrics != nil {
|
if metrics != nil {
|
||||||
metrics.IncrementMemCacheMissCounter("Post Etag")
|
metrics.IncrementMemCacheMissCounter("Last Post Time")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if metrics != nil {
|
if metrics != nil {
|
||||||
metrics.IncrementMemCacheMissCounter("Post Etag")
|
metrics.IncrementMemCacheMissCounter("Last Post Time")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var et etagPosts
|
var et etagPosts
|
||||||
err := s.GetReplica().SelectOne(&et, "SELECT Id, UpdateAt FROM Posts WHERE ChannelId = :ChannelId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"ChannelId": channelId})
|
err := s.GetReplica().SelectOne(&et, "SELECT Id, UpdateAt FROM Posts WHERE ChannelId = :ChannelId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"ChannelId": channelId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Data = fmt.Sprintf("%v.0.%v", model.CurrentVersion, model.GetMillis())
|
result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis())
|
||||||
} else {
|
} else {
|
||||||
result.Data = fmt.Sprintf("%v.%v.%v", model.CurrentVersion, et.Id, et.UpdateAt)
|
result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, et.UpdateAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
postEtagCache.AddWithExpiresInSecs(channelId, result.Data.(string), POSTS_ETAG_CACHE_SEC)
|
lastPostTimeCache.AddWithExpiresInSecs(channelId, et.UpdateAt, LAST_POST_TIME_CACHE_SEC)
|
||||||
|
|
||||||
storeChannel <- result
|
storeChannel <- result
|
||||||
close(storeChannel)
|
close(storeChannel)
|
||||||
@@ -428,11 +428,35 @@ func (s SqlPostStore) GetPosts(channelId string, offset int, limit int) StoreCha
|
|||||||
return storeChannel
|
return storeChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlPostStore) GetPostsSince(channelId string, time int64) StoreChannel {
|
func (s SqlPostStore) GetPostsSince(channelId string, time int64, allowFromCache bool) StoreChannel {
|
||||||
storeChannel := make(StoreChannel, 1)
|
storeChannel := make(StoreChannel, 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
result := StoreResult{}
|
result := StoreResult{}
|
||||||
|
metrics := einterfaces.GetMetricsInterface()
|
||||||
|
|
||||||
|
if allowFromCache {
|
||||||
|
// If the last post in the channel's time is less than or equal to the time we are getting posts since,
|
||||||
|
// we can safely return no posts.
|
||||||
|
if cacheItem, ok := lastPostTimeCache.Get(channelId); ok && cacheItem.(int64) <= time {
|
||||||
|
if metrics != nil {
|
||||||
|
metrics.IncrementMemCacheHitCounter("Last Post Time")
|
||||||
|
}
|
||||||
|
list := &model.PostList{Order: make([]string, 0, 0)}
|
||||||
|
result.Data = list
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if metrics != nil {
|
||||||
|
metrics.IncrementMemCacheMissCounter("Last Post Time")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if metrics != nil {
|
||||||
|
metrics.IncrementMemCacheMissCounter("Last Post Time")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var posts []*model.Post
|
var posts []*model.Post
|
||||||
_, err := s.GetReplica().Select(&posts,
|
_, err := s.GetReplica().Select(&posts,
|
||||||
@@ -469,13 +493,20 @@ func (s SqlPostStore) GetPostsSince(channelId string, time int64) StoreChannel {
|
|||||||
|
|
||||||
list := &model.PostList{Order: make([]string, 0, len(posts))}
|
list := &model.PostList{Order: make([]string, 0, len(posts))}
|
||||||
|
|
||||||
|
var latestUpdate int64 = 0
|
||||||
|
|
||||||
for _, p := range posts {
|
for _, p := range posts {
|
||||||
list.AddPost(p)
|
list.AddPost(p)
|
||||||
if p.UpdateAt > time {
|
if p.UpdateAt > time {
|
||||||
list.AddOrder(p.Id)
|
list.AddOrder(p.Id)
|
||||||
}
|
}
|
||||||
|
if latestUpdate < p.UpdateAt {
|
||||||
|
latestUpdate = p.UpdateAt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastPostTimeCache.AddWithExpiresInSecs(channelId, latestUpdate, LAST_POST_TIME_CACHE_SEC)
|
||||||
|
|
||||||
result.Data = list
|
result.Data = list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -38,14 +39,14 @@ func TestPostStoreGet(t *testing.T) {
|
|||||||
o1.Message = "a" + model.NewId() + "b"
|
o1.Message = "a" + model.NewId() + "b"
|
||||||
|
|
||||||
etag1 := (<-store.Post().GetEtag(o1.ChannelId, false)).Data.(string)
|
etag1 := (<-store.Post().GetEtag(o1.ChannelId, false)).Data.(string)
|
||||||
if strings.Index(etag1, model.CurrentVersion+".0.") != 0 {
|
if strings.Index(etag1, model.CurrentVersion+".") != 0 {
|
||||||
t.Fatal("Invalid Etag")
|
t.Fatal("Invalid Etag")
|
||||||
}
|
}
|
||||||
|
|
||||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||||
|
|
||||||
etag2 := (<-store.Post().GetEtag(o1.ChannelId, false)).Data.(string)
|
etag2 := (<-store.Post().GetEtag(o1.ChannelId, false)).Data.(string)
|
||||||
if strings.Index(etag2, model.CurrentVersion+"."+o1.Id) != 0 {
|
if strings.Index(etag2, fmt.Sprintf("%v.%v", model.CurrentVersion, o1.UpdateAt)) != 0 {
|
||||||
t.Fatal("Invalid Etag")
|
t.Fatal("Invalid Etag")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +71,13 @@ func TestGetEtagCache(t *testing.T) {
|
|||||||
o1.Message = "a" + model.NewId() + "b"
|
o1.Message = "a" + model.NewId() + "b"
|
||||||
|
|
||||||
etag1 := (<-store.Post().GetEtag(o1.ChannelId, true)).Data.(string)
|
etag1 := (<-store.Post().GetEtag(o1.ChannelId, true)).Data.(string)
|
||||||
if strings.Index(etag1, model.CurrentVersion+".0.") != 0 {
|
if strings.Index(etag1, model.CurrentVersion+".") != 0 {
|
||||||
t.Fatal("Invalid Etag")
|
t.Fatal("Invalid Etag")
|
||||||
}
|
}
|
||||||
|
|
||||||
// This one should come from the cache
|
// This one should come from the cache
|
||||||
etag2 := (<-store.Post().GetEtag(o1.ChannelId, true)).Data.(string)
|
etag2 := (<-store.Post().GetEtag(o1.ChannelId, true)).Data.(string)
|
||||||
if strings.Index(etag2, model.CurrentVersion+".0.") != 0 {
|
if strings.Index(etag2, model.CurrentVersion+".") != 0 {
|
||||||
t.Fatal("Invalid Etag")
|
t.Fatal("Invalid Etag")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,15 +85,15 @@ func TestGetEtagCache(t *testing.T) {
|
|||||||
|
|
||||||
// We have not invalidated the cache so this should be the same as above
|
// We have not invalidated the cache so this should be the same as above
|
||||||
etag3 := (<-store.Post().GetEtag(o1.ChannelId, true)).Data.(string)
|
etag3 := (<-store.Post().GetEtag(o1.ChannelId, true)).Data.(string)
|
||||||
if strings.Index(etag3, model.CurrentVersion+".0.") != 0 {
|
if strings.Index(etag3, etag2) != 0 {
|
||||||
t.Fatal("Invalid Etag")
|
t.Fatal("Invalid Etag")
|
||||||
}
|
}
|
||||||
|
|
||||||
store.Post().InvalidatePostEtagCache(o1.ChannelId)
|
store.Post().InvalidateLastPostTimeCache(o1.ChannelId)
|
||||||
|
|
||||||
// Invalidated cache so we should get a good result
|
// Invalidated cache so we should get a good result
|
||||||
etag4 := (<-store.Post().GetEtag(o1.ChannelId, true)).Data.(string)
|
etag4 := (<-store.Post().GetEtag(o1.ChannelId, true)).Data.(string)
|
||||||
if strings.Index(etag4, model.CurrentVersion+"."+o1.Id) != 0 {
|
if strings.Index(etag4, fmt.Sprintf("%v.%v", model.CurrentVersion, o1.UpdateAt)) != 0 {
|
||||||
t.Fatal("Invalid Etag")
|
t.Fatal("Invalid Etag")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +201,7 @@ func TestPostStoreDelete(t *testing.T) {
|
|||||||
o1.Message = "a" + model.NewId() + "b"
|
o1.Message = "a" + model.NewId() + "b"
|
||||||
|
|
||||||
etag1 := (<-store.Post().GetEtag(o1.ChannelId, false)).Data.(string)
|
etag1 := (<-store.Post().GetEtag(o1.ChannelId, false)).Data.(string)
|
||||||
if strings.Index(etag1, model.CurrentVersion+".0.") != 0 {
|
if strings.Index(etag1, model.CurrentVersion+".") != 0 {
|
||||||
t.Fatal("Invalid Etag")
|
t.Fatal("Invalid Etag")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +225,7 @@ func TestPostStoreDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
etag2 := (<-store.Post().GetEtag(o1.ChannelId, false)).Data.(string)
|
etag2 := (<-store.Post().GetEtag(o1.ChannelId, false)).Data.(string)
|
||||||
if strings.Index(etag2, model.CurrentVersion+"."+o1.Id) != 0 {
|
if strings.Index(etag2, model.CurrentVersion+".") != 0 {
|
||||||
t.Fatal("Invalid Etag")
|
t.Fatal("Invalid Etag")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -680,7 +681,7 @@ func TestPostStoreGetPostsSince(t *testing.T) {
|
|||||||
o5.RootId = o4.Id
|
o5.RootId = o4.Id
|
||||||
o5 = (<-store.Post().Save(o5)).Data.(*model.Post)
|
o5 = (<-store.Post().Save(o5)).Data.(*model.Post)
|
||||||
|
|
||||||
r1 := (<-store.Post().GetPostsSince(o1.ChannelId, o1.CreateAt)).Data.(*model.PostList)
|
r1 := (<-store.Post().GetPostsSince(o1.ChannelId, o1.CreateAt, false)).Data.(*model.PostList)
|
||||||
|
|
||||||
if r1.Order[0] != o5.Id {
|
if r1.Order[0] != o5.Id {
|
||||||
t.Fatal("invalid order")
|
t.Fatal("invalid order")
|
||||||
@@ -705,6 +706,12 @@ func TestPostStoreGetPostsSince(t *testing.T) {
|
|||||||
if r1.Posts[o1.Id].Message != o1.Message {
|
if r1.Posts[o1.Id].Message != o1.Message {
|
||||||
t.Fatal("Missing parent")
|
t.Fatal("Missing parent")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r2 := (<-store.Post().GetPostsSince(o1.ChannelId, o5.UpdateAt, true)).Data.(*model.PostList)
|
||||||
|
|
||||||
|
if len(r2.Order) != 0 {
|
||||||
|
t.Fatal("wrong size ", len(r2.Posts))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostStoreSearch(t *testing.T) {
|
func TestPostStoreSearch(t *testing.T) {
|
||||||
|
|||||||
@@ -130,13 +130,13 @@ type PostStore interface {
|
|||||||
GetFlaggedPosts(userId string, offset int, limit int) StoreChannel
|
GetFlaggedPosts(userId string, offset int, limit int) StoreChannel
|
||||||
GetPostsBefore(channelId string, postId string, numPosts int, offset int) StoreChannel
|
GetPostsBefore(channelId string, postId string, numPosts int, offset int) StoreChannel
|
||||||
GetPostsAfter(channelId string, postId string, numPosts int, offset int) StoreChannel
|
GetPostsAfter(channelId string, postId string, numPosts int, offset int) StoreChannel
|
||||||
GetPostsSince(channelId string, time int64) StoreChannel
|
GetPostsSince(channelId string, time int64, allowFromCache bool) StoreChannel
|
||||||
GetEtag(channelId string, allowFromCache bool) StoreChannel
|
GetEtag(channelId string, allowFromCache bool) StoreChannel
|
||||||
Search(teamId string, userId string, params *model.SearchParams) StoreChannel
|
Search(teamId string, userId string, params *model.SearchParams) StoreChannel
|
||||||
AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel
|
AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel
|
||||||
AnalyticsPostCountsByDay(teamId string) StoreChannel
|
AnalyticsPostCountsByDay(teamId string) StoreChannel
|
||||||
AnalyticsPostCount(teamId string, mustHaveFile bool, mustHaveHashtag bool) StoreChannel
|
AnalyticsPostCount(teamId string, mustHaveFile bool, mustHaveHashtag bool) StoreChannel
|
||||||
InvalidatePostEtagCache(channelId string)
|
InvalidateLastPostTimeCache(channelId string)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserStore interface {
|
type UserStore interface {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user