From 65452b04a8e8f6f570e839a6fcb190ddad09a369 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sat, 30 Apr 2022 21:27:15 -0700 Subject: [PATCH] Cache chat history --- core/chat/persistence.go | 73 +++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/core/chat/persistence.go b/core/chat/persistence.go index aaa2939c7..83746b536 100644 --- a/core/chat/persistence.go +++ b/core/chat/persistence.go @@ -21,6 +21,13 @@ const ( maxBacklogNumber = 50 // Return max number of messages in history request ) +var ( + _chatHistoryCache *[]interface{} + _adminChatHistoryCache *[]interface{} + _cachedAdminChatHistoryStatement *sql.Stmt + _cachedSaveEventStatement *sql.Stmt +) + func setupPersistence() { _datastore = data.GetDatastore() data.CreateMessagesTable(_datastore.DB) @@ -47,30 +54,23 @@ func saveFederatedAction(event events.FediverseEngagementEvent) { // nolint: unparam func saveEvent(id string, userID *string, body string, eventType string, hidden *time.Time, timestamp time.Time, image *string, link *string, title *string, subtitle *string) { defer func() { - _historyCache = nil + _adminChatHistoryCache = nil + _chatHistoryCache = nil }() - tx, err := _datastore.DB.Begin() - if err != nil { - log.Errorln("error saving", eventType, err) - return - } - - defer tx.Rollback() // nolint + _datastore.DbLock.Lock() + defer _datastore.DbLock.Unlock() - stmt, err := tx.Prepare("INSERT INTO messages(id, user_id, body, eventType, hidden_at, timestamp, image, link, title, subtitle) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") - if err != nil { - log.Errorln("error saving", eventType, err) - return + if _cachedSaveEventStatement == nil { + stmt, err := _datastore.DB.Prepare("INSERT INTO messages(id, user_id, body, eventType, hidden_at, timestamp, image, link, title, subtitle) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + if err != nil { + log.Errorln("error preparing save event statement", err) + return + } + _cachedSaveEventStatement = stmt } - defer stmt.Close() - - if _, err = stmt.Exec(id, userID, body, eventType, hidden, timestamp, image, link, title, subtitle); err != nil { - log.Errorln("error saving", eventType, err) - return - } - if err = tx.Commit(); err != nil { + if _, err := _cachedSaveEventStatement.Exec(id, userID, body, eventType, hidden, timestamp, image, link, title, subtitle); err != nil { log.Errorln("error saving", eventType, err) return } @@ -212,12 +212,16 @@ type rowData struct { func getChat(query *sql.Stmt) []interface{} { history := make([]interface{}, 0) rows, err := query.Query() + if err != nil || rows.Err() != nil { log.Errorln("error fetching chat history", err) return history } + defer rows.Close() + rowDatas := []rowData{} + for rows.Next() { row := rowData{} @@ -248,6 +252,10 @@ func getChat(query *sql.Stmt) []interface{} { break } + rowDatas = append(rowDatas, row) + } + + for _, row := range rowDatas { var message interface{} switch row.eventType { @@ -271,29 +279,25 @@ func getChat(query *sql.Stmt) []interface{} { return history } -var _historyCache *[]interface{} - -var cachedAdminChatHistoryStatement *sql.Stmt - // GetChatModerationHistory will return all the chat messages suitable for moderation purposes. func GetChatModerationHistory() []interface{} { - if _historyCache != nil { - return *_historyCache + if _adminChatHistoryCache != nil { + return *_adminChatHistoryCache } - if cachedAdminChatHistoryStatement == nil { + if _cachedAdminChatHistoryStatement == nil { stmt, err := _datastore.DB.Prepare(`SELECT messages.id, user_id, body, title, subtitle, image, link, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, scopes, users.type FROM messages INNER JOIN users ON messages.user_id = users.id ORDER BY timestamp DESC`) if err != nil { log.Errorln("error preparing chat moderation history statement", err) return nil } - cachedAdminChatHistoryStatement = stmt + _cachedAdminChatHistoryStatement = stmt } // Get all messages regardless of visibility - result := getChat(cachedAdminChatHistoryStatement) + result := getChat(_cachedAdminChatHistoryStatement) - _historyCache = &result + _adminChatHistoryCache = &result return result } @@ -302,6 +306,10 @@ var cachedChatHistoryStatement *sql.Stmt // GetChatHistory will return all the chat messages suitable for returning as user-facing chat history. func GetChatHistory() []interface{} { + if _chatHistoryCache != nil { + return *_chatHistoryCache + } + if cachedChatHistoryStatement == nil { stmt, err := _datastore.DB.Prepare(fmt.Sprintf("SELECT messages.id, messages.user_id, messages.body, messages.title, messages.subtitle, messages.image, messages.link, messages.eventType, messages.hidden_at, messages.timestamp, users.display_name, users.display_color, users.created_at, users.disabled_at, users.previous_names, users.namechanged_at, users.scopes, users.type FROM users JOIN messages ON users.id = messages.user_id WHERE hidden_at IS NULL AND disabled_at IS NULL ORDER BY timestamp DESC LIMIT %d", maxBacklogNumber)) if err != nil { @@ -313,6 +321,7 @@ func GetChatHistory() []interface{} { // Get all visible messages m := getChat(cachedChatHistoryStatement) + _chatHistoryCache = &m return m } @@ -321,7 +330,8 @@ func GetChatHistory() []interface{} { // and then send out visibility changed events to chat clients. func SetMessageVisibilityForUserID(userID string, visible bool) error { defer func() { - _historyCache = nil + _adminChatHistoryCache = nil + _chatHistoryCache = nil }() // Get a list of IDs to send to the connected clients to hide @@ -347,7 +357,8 @@ func SetMessageVisibilityForUserID(userID string, visible bool) error { func saveMessageVisibility(messageIDs []string, visible bool) error { defer func() { - _historyCache = nil + _adminChatHistoryCache = nil + _chatHistoryCache = nil }() _datastore.DbLock.Lock()