From b891c00b09ed94d0fdfeb449df5e345c67208700 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Dec 2020 12:15:12 +0000 Subject: [PATCH] Add RoomInfo cache, remove RoomServerRoomNIDsCache (#1646) * Add RoomInfo cache, remove RoomServerRoomNID cache, ensure caches are thread-safe * Don't panic if the roomInfo isn't known yet * LRU package is already threadsafe * Use RoomInfo cache to find room version if possible in Events() * Adding comments about RoomInfoCache safety --- internal/caching/cache_roominfo.go | 45 +++++++++++++++++++ internal/caching/cache_roomservernids.go | 25 +---------- internal/caching/caches.go | 1 + internal/caching/impl_inmemorylru.go | 18 ++++---- .../storage/shared/latest_events_updater.go | 7 +++ roomserver/storage/shared/storage.go | 33 ++++++++++---- 6 files changed, 88 insertions(+), 41 deletions(-) create mode 100644 internal/caching/cache_roominfo.go diff --git a/internal/caching/cache_roominfo.go b/internal/caching/cache_roominfo.go new file mode 100644 index 000000000..f32d6ba9b --- /dev/null +++ b/internal/caching/cache_roominfo.go @@ -0,0 +1,45 @@ +package caching + +import ( + "github.com/matrix-org/dendrite/roomserver/types" +) + +// WARNING: This cache is mutable because it's entirely possible that +// the IsStub or StateSnaphotNID fields can change, even though the +// room version and room NID fields will not. This is only safe because +// the RoomInfoCache is used ONLY within the roomserver and because it +// will be kept up-to-date by the latest events updater. It MUST NOT be +// used from other components as we currently have no way to invalidate +// the cache in downstream components. + +const ( + RoomInfoCacheName = "roominfo" + RoomInfoCacheMaxEntries = 1024 + RoomInfoCacheMutable = true +) + +// RoomInfosCache contains the subset of functions needed for +// a room Info cache. It must only be used from the roomserver only +// It is not safe for use from other components. +type RoomInfoCache interface { + GetRoomInfo(roomID string) (roomInfo types.RoomInfo, ok bool) + StoreRoomInfo(roomID string, roomInfo types.RoomInfo) +} + +// GetRoomInfo must only be called from the roomserver only. It is not +// safe for use from other components. +func (c Caches) GetRoomInfo(roomID string) (types.RoomInfo, bool) { + val, found := c.RoomInfos.Get(roomID) + if found && val != nil { + if roomInfo, ok := val.(types.RoomInfo); ok { + return roomInfo, true + } + } + return types.RoomInfo{}, false +} + +// StoreRoomInfo must only be called from the roomserver only. It is not +// safe for use from other components. +func (c Caches) StoreRoomInfo(roomID string, roomInfo types.RoomInfo) { + c.RoomInfos.Set(roomID, roomInfo) +} diff --git a/internal/caching/cache_roomservernids.go b/internal/caching/cache_roomservernids.go index cac595494..bf4fe85ed 100644 --- a/internal/caching/cache_roomservernids.go +++ b/internal/caching/cache_roomservernids.go @@ -15,10 +15,6 @@ const ( RoomServerEventTypeNIDsCacheMaxEntries = 64 RoomServerEventTypeNIDsCacheMutable = false - RoomServerRoomNIDsCacheName = "roomserver_room_nids" - RoomServerRoomNIDsCacheMaxEntries = 1024 - RoomServerRoomNIDsCacheMutable = false - RoomServerRoomIDsCacheName = "roomserver_room_ids" RoomServerRoomIDsCacheMaxEntries = 1024 RoomServerRoomIDsCacheMutable = false @@ -27,6 +23,7 @@ const ( type RoomServerCaches interface { RoomServerNIDsCache RoomVersionCache + RoomInfoCache } // RoomServerNIDsCache contains the subset of functions needed for @@ -38,9 +35,6 @@ type RoomServerNIDsCache interface { GetRoomServerEventTypeNID(eventType string) (types.EventTypeNID, bool) StoreRoomServerEventTypeNID(eventType string, nid types.EventTypeNID) - GetRoomServerRoomNID(roomID string) (types.RoomNID, bool) - StoreRoomServerRoomNID(roomID string, nid types.RoomNID) - GetRoomServerRoomID(roomNID types.RoomNID) (string, bool) StoreRoomServerRoomID(roomNID types.RoomNID, roomID string) } @@ -73,21 +67,6 @@ func (c Caches) StoreRoomServerEventTypeNID(eventType string, nid types.EventTyp c.RoomServerEventTypeNIDs.Set(eventType, nid) } -func (c Caches) GetRoomServerRoomNID(roomID string) (types.RoomNID, bool) { - val, found := c.RoomServerRoomNIDs.Get(roomID) - if found && val != nil { - if roomNID, ok := val.(types.RoomNID); ok { - return roomNID, true - } - } - return 0, false -} - -func (c Caches) StoreRoomServerRoomNID(roomID string, roomNID types.RoomNID) { - c.RoomServerRoomNIDs.Set(roomID, roomNID) - c.RoomServerRoomIDs.Set(strconv.Itoa(int(roomNID)), roomID) -} - func (c Caches) GetRoomServerRoomID(roomNID types.RoomNID) (string, bool) { val, found := c.RoomServerRoomIDs.Get(strconv.Itoa(int(roomNID))) if found && val != nil { @@ -99,5 +78,5 @@ func (c Caches) GetRoomServerRoomID(roomNID types.RoomNID) (string, bool) { } func (c Caches) StoreRoomServerRoomID(roomNID types.RoomNID, roomID string) { - c.StoreRoomServerRoomNID(roomID, roomNID) + c.RoomServerRoomIDs.Set(strconv.Itoa(int(roomNID)), roomID) } diff --git a/internal/caching/caches.go b/internal/caching/caches.go index e7b7f550d..f04d05d42 100644 --- a/internal/caching/caches.go +++ b/internal/caching/caches.go @@ -10,6 +10,7 @@ type Caches struct { RoomServerEventTypeNIDs Cache // RoomServerNIDsCache RoomServerRoomNIDs Cache // RoomServerNIDsCache RoomServerRoomIDs Cache // RoomServerNIDsCache + RoomInfos Cache // RoomInfoCache FederationEvents Cache // FederationEventsCache } diff --git a/internal/caching/impl_inmemorylru.go b/internal/caching/impl_inmemorylru.go index f05e8f3c6..cf05a8b55 100644 --- a/internal/caching/impl_inmemorylru.go +++ b/internal/caching/impl_inmemorylru.go @@ -45,19 +45,19 @@ func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) { if err != nil { return nil, err } - roomServerRoomNIDs, err := NewInMemoryLRUCachePartition( - RoomServerRoomNIDsCacheName, - RoomServerRoomNIDsCacheMutable, - RoomServerRoomNIDsCacheMaxEntries, + roomServerRoomIDs, err := NewInMemoryLRUCachePartition( + RoomServerRoomIDsCacheName, + RoomServerRoomIDsCacheMutable, + RoomServerRoomIDsCacheMaxEntries, enablePrometheus, ) if err != nil { return nil, err } - roomServerRoomIDs, err := NewInMemoryLRUCachePartition( - RoomServerRoomIDsCacheName, - RoomServerRoomIDsCacheMutable, - RoomServerRoomIDsCacheMaxEntries, + roomInfos, err := NewInMemoryLRUCachePartition( + RoomInfoCacheName, + RoomInfoCacheMutable, + RoomInfoCacheMaxEntries, enablePrometheus, ) if err != nil { @@ -77,8 +77,8 @@ func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) { ServerKeys: serverKeys, RoomServerStateKeyNIDs: roomServerStateKeyNIDs, RoomServerEventTypeNIDs: roomServerEventTypeNIDs, - RoomServerRoomNIDs: roomServerRoomNIDs, RoomServerRoomIDs: roomServerRoomIDs, + RoomInfos: roomInfos, FederationEvents: federationEvents, }, nil } diff --git a/roomserver/storage/shared/latest_events_updater.go b/roomserver/storage/shared/latest_events_updater.go index 8825dc464..36865081a 100644 --- a/roomserver/storage/shared/latest_events_updater.go +++ b/roomserver/storage/shared/latest_events_updater.go @@ -105,6 +105,13 @@ func (u *LatestEventsUpdater) SetLatestEvents( if err := u.d.RoomsTable.UpdateLatestEventNIDs(u.ctx, txn, roomNID, eventNIDs, lastEventNIDSent, currentStateSnapshotNID); err != nil { return fmt.Errorf("u.d.RoomsTable.updateLatestEventNIDs: %w", err) } + if roomID, ok := u.d.Cache.GetRoomServerRoomID(roomNID); ok { + if roomInfo, ok := u.d.Cache.GetRoomInfo(roomID); ok { + roomInfo.StateSnapshotNID = currentStateSnapshotNID + roomInfo.IsStub = false + u.d.Cache.StoreRoomInfo(roomID, roomInfo) + } + } return nil }) } diff --git a/roomserver/storage/shared/storage.go b/roomserver/storage/shared/storage.go index d61fa61d3..b4d9d5624 100644 --- a/roomserver/storage/shared/storage.go +++ b/roomserver/storage/shared/storage.go @@ -124,7 +124,15 @@ func (d *Database) StateEntriesForTuples( } func (d *Database) RoomInfo(ctx context.Context, roomID string) (*types.RoomInfo, error) { - return d.RoomsTable.SelectRoomInfo(ctx, roomID) + if roomInfo, ok := d.Cache.GetRoomInfo(roomID); ok { + return &roomInfo, nil + } + roomInfo, err := d.RoomsTable.SelectRoomInfo(ctx, roomID) + if err == nil && roomInfo != nil { + d.Cache.StoreRoomServerRoomID(roomInfo.RoomNID, roomID) + d.Cache.StoreRoomInfo(roomID, *roomInfo) + } + return roomInfo, err } func (d *Database) AddState( @@ -322,14 +330,24 @@ func (d *Database) Events( for _, n := range roomNIDs { uniqueRoomNIDs[n] = struct{}{} } - roomNIDList := make([]types.RoomNID, 0, len(uniqueRoomNIDs)) + roomVersions := make(map[types.RoomNID]gomatrixserverlib.RoomVersion) + fetchNIDList := make([]types.RoomNID, 0, len(uniqueRoomNIDs)) for n := range uniqueRoomNIDs { - roomNIDList = append(roomNIDList, n) + if roomID, ok := d.Cache.GetRoomServerRoomID(n); ok { + if roomInfo, ok := d.Cache.GetRoomInfo(roomID); ok { + roomVersions[n] = roomInfo.RoomVersion + continue + } + } + fetchNIDList = append(fetchNIDList, n) } - roomVersions, err := d.RoomsTable.SelectRoomVersionsForRoomNIDs(ctx, roomNIDList) + dbRoomVersions, err := d.RoomsTable.SelectRoomVersionsForRoomNIDs(ctx, fetchNIDList) if err != nil { return nil, err } + for n, v := range dbRoomVersions { + roomVersions[n] = v + } results := make([]types.Event, len(eventJSONs)) for i, eventJSON := range eventJSONs { result := &results[i] @@ -556,8 +574,8 @@ func (d *Database) assignRoomNID( ctx context.Context, txn *sql.Tx, roomID string, roomVersion gomatrixserverlib.RoomVersion, ) (types.RoomNID, error) { - if roomNID, ok := d.Cache.GetRoomServerRoomNID(roomID); ok { - return roomNID, nil + if roomInfo, ok := d.Cache.GetRoomInfo(roomID); ok { + return roomInfo.RoomNID, nil } // Check if we already have a numeric ID in the database. roomNID, err := d.RoomsTable.SelectRoomNID(ctx, txn, roomID) @@ -569,9 +587,6 @@ func (d *Database) assignRoomNID( roomNID, err = d.RoomsTable.SelectRoomNID(ctx, txn, roomID) } } - if err == nil { - d.Cache.StoreRoomServerRoomNID(roomID, roomNID) - } return roomNID, err }