Allow specifying max age for caches (#2239)

* Allow specifying max age for caches

* Evict cache entry if it's found to be stale when we call Get

* Fix bugs
This commit is contained in:
Neil Alexander 2022-03-01 16:59:52 +00:00 committed by GitHub
parent 726529fe99
commit bb2380c254
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 5 deletions

View file

@ -10,6 +10,7 @@ const (
FederationEventCacheName = "federation_event" FederationEventCacheName = "federation_event"
FederationEventCacheMaxEntries = 256 FederationEventCacheMaxEntries = 256
FederationEventCacheMutable = true // to allow use of Unset only FederationEventCacheMutable = true // to allow use of Unset only
FederationEventCacheMaxAge = CacheNoMaxAge
) )
// FederationCache contains the subset of functions needed for // FederationCache contains the subset of functions needed for

View file

@ -1,6 +1,8 @@
package caching package caching
import ( import (
"time"
"github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/dendrite/roomserver/types"
) )
@ -16,6 +18,7 @@ const (
RoomInfoCacheName = "roominfo" RoomInfoCacheName = "roominfo"
RoomInfoCacheMaxEntries = 1024 RoomInfoCacheMaxEntries = 1024
RoomInfoCacheMutable = true RoomInfoCacheMutable = true
RoomInfoCacheMaxAge = time.Minute * 5
) )
// RoomInfosCache contains the subset of functions needed for // RoomInfosCache contains the subset of functions needed for

View file

@ -10,6 +10,7 @@ const (
RoomServerRoomIDsCacheName = "roomserver_room_ids" RoomServerRoomIDsCacheName = "roomserver_room_ids"
RoomServerRoomIDsCacheMaxEntries = 1024 RoomServerRoomIDsCacheMaxEntries = 1024
RoomServerRoomIDsCacheMutable = false RoomServerRoomIDsCacheMutable = false
RoomServerRoomIDsCacheMaxAge = CacheNoMaxAge
) )
type RoomServerCaches interface { type RoomServerCaches interface {

View file

@ -6,6 +6,7 @@ const (
RoomVersionCacheName = "room_versions" RoomVersionCacheName = "room_versions"
RoomVersionCacheMaxEntries = 1024 RoomVersionCacheMaxEntries = 1024
RoomVersionCacheMutable = false RoomVersionCacheMutable = false
RoomVersionCacheMaxAge = CacheNoMaxAge
) )
// RoomVersionsCache contains the subset of functions needed for // RoomVersionsCache contains the subset of functions needed for

View file

@ -10,6 +10,7 @@ const (
ServerKeyCacheName = "server_key" ServerKeyCacheName = "server_key"
ServerKeyCacheMaxEntries = 4096 ServerKeyCacheMaxEntries = 4096
ServerKeyCacheMutable = true ServerKeyCacheMutable = true
ServerKeyCacheMaxAge = CacheNoMaxAge
) )
// ServerKeyCache contains the subset of functions needed for // ServerKeyCache contains the subset of functions needed for

View file

@ -1,5 +1,7 @@
package caching package caching
import "time"
// Caches contains a set of references to caches. They may be // Caches contains a set of references to caches. They may be
// different implementations as long as they satisfy the Cache // different implementations as long as they satisfy the Cache
// interface. // interface.
@ -19,3 +21,5 @@ type Cache interface {
Set(key string, value interface{}) Set(key string, value interface{})
Unset(key string) Unset(key string)
} }
const CacheNoMaxAge = time.Duration(0)

View file

@ -14,6 +14,7 @@ func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) {
RoomVersionCacheName, RoomVersionCacheName,
RoomVersionCacheMutable, RoomVersionCacheMutable,
RoomVersionCacheMaxEntries, RoomVersionCacheMaxEntries,
RoomVersionCacheMaxAge,
enablePrometheus, enablePrometheus,
) )
if err != nil { if err != nil {
@ -23,6 +24,7 @@ func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) {
ServerKeyCacheName, ServerKeyCacheName,
ServerKeyCacheMutable, ServerKeyCacheMutable,
ServerKeyCacheMaxEntries, ServerKeyCacheMaxEntries,
ServerKeyCacheMaxAge,
enablePrometheus, enablePrometheus,
) )
if err != nil { if err != nil {
@ -32,6 +34,7 @@ func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) {
RoomServerRoomIDsCacheName, RoomServerRoomIDsCacheName,
RoomServerRoomIDsCacheMutable, RoomServerRoomIDsCacheMutable,
RoomServerRoomIDsCacheMaxEntries, RoomServerRoomIDsCacheMaxEntries,
RoomServerRoomIDsCacheMaxAge,
enablePrometheus, enablePrometheus,
) )
if err != nil { if err != nil {
@ -41,6 +44,7 @@ func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) {
RoomInfoCacheName, RoomInfoCacheName,
RoomInfoCacheMutable, RoomInfoCacheMutable,
RoomInfoCacheMaxEntries, RoomInfoCacheMaxEntries,
RoomInfoCacheMaxAge,
enablePrometheus, enablePrometheus,
) )
if err != nil { if err != nil {
@ -50,6 +54,7 @@ func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) {
FederationEventCacheName, FederationEventCacheName,
FederationEventCacheMutable, FederationEventCacheMutable,
FederationEventCacheMaxEntries, FederationEventCacheMaxEntries,
FederationEventCacheMaxAge,
enablePrometheus, enablePrometheus,
) )
if err != nil { if err != nil {
@ -96,15 +101,22 @@ type InMemoryLRUCachePartition struct {
name string name string
mutable bool mutable bool
maxEntries int maxEntries int
maxAge time.Duration
lru *lru.Cache lru *lru.Cache
} }
func NewInMemoryLRUCachePartition(name string, mutable bool, maxEntries int, enablePrometheus bool) (*InMemoryLRUCachePartition, error) { type inMemoryLRUCacheEntry struct {
value interface{}
created time.Time
}
func NewInMemoryLRUCachePartition(name string, mutable bool, maxEntries int, maxAge time.Duration, enablePrometheus bool) (*InMemoryLRUCachePartition, error) {
var err error var err error
cache := InMemoryLRUCachePartition{ cache := InMemoryLRUCachePartition{
name: name, name: name,
mutable: mutable, mutable: mutable,
maxEntries: maxEntries, maxEntries: maxEntries,
maxAge: maxAge,
} }
cache.lru, err = lru.New(maxEntries) cache.lru, err = lru.New(maxEntries)
if err != nil { if err != nil {
@ -124,11 +136,16 @@ func NewInMemoryLRUCachePartition(name string, mutable bool, maxEntries int, ena
func (c *InMemoryLRUCachePartition) Set(key string, value interface{}) { func (c *InMemoryLRUCachePartition) Set(key string, value interface{}) {
if !c.mutable { if !c.mutable {
if peek, ok := c.lru.Peek(key); ok && peek != value { if peek, ok := c.lru.Peek(key); ok {
if entry, ok := peek.(*inMemoryLRUCacheEntry); ok && entry.value != value {
panic(fmt.Sprintf("invalid use of immutable cache tries to mutate existing value of %q", key)) panic(fmt.Sprintf("invalid use of immutable cache tries to mutate existing value of %q", key))
} }
} }
c.lru.Add(key, value) }
c.lru.Add(key, &inMemoryLRUCacheEntry{
value: value,
created: time.Now(),
})
} }
func (c *InMemoryLRUCachePartition) Unset(key string) { func (c *InMemoryLRUCachePartition) Unset(key string) {
@ -139,5 +156,20 @@ func (c *InMemoryLRUCachePartition) Unset(key string) {
} }
func (c *InMemoryLRUCachePartition) Get(key string) (value interface{}, ok bool) { func (c *InMemoryLRUCachePartition) Get(key string) (value interface{}, ok bool) {
return c.lru.Get(key) v, ok := c.lru.Get(key)
if !ok {
return nil, false
}
entry, ok := v.(*inMemoryLRUCacheEntry)
switch {
case ok && c.maxAge == CacheNoMaxAge:
return entry.value, ok // There's no maximum age policy
case ok && time.Since(entry.created) < c.maxAge:
return entry.value, ok // The value for the key isn't stale
default:
// Either the key was found and it was stale, or the key
// wasn't found at all
c.lru.Remove(key)
return nil, false
}
} }