Browse Source

Cache chat history

gek/load-tests
Gabe Kangas 3 years ago
parent
commit
65452b04a8
No known key found for this signature in database
GPG Key ID: 9A56337728BC81EA
  1. 73
      core/chat/persistence.go

73
core/chat/persistence.go

@ -21,6 +21,13 @@ const (
maxBacklogNumber = 50 // Return max number of messages in history request maxBacklogNumber = 50 // Return max number of messages in history request
) )
var (
_chatHistoryCache *[]interface{}
_adminChatHistoryCache *[]interface{}
_cachedAdminChatHistoryStatement *sql.Stmt
_cachedSaveEventStatement *sql.Stmt
)
func setupPersistence() { func setupPersistence() {
_datastore = data.GetDatastore() _datastore = data.GetDatastore()
data.CreateMessagesTable(_datastore.DB) data.CreateMessagesTable(_datastore.DB)
@ -47,30 +54,23 @@ func saveFederatedAction(event events.FediverseEngagementEvent) {
// nolint: unparam // 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) { 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() { defer func() {
_historyCache = nil _adminChatHistoryCache = nil
_chatHistoryCache = nil
}() }()
tx, err := _datastore.DB.Begin() _datastore.DbLock.Lock()
if err != nil { defer _datastore.DbLock.Unlock()
log.Errorln("error saving", eventType, err)
return
}
defer tx.Rollback() // nolint
stmt, err := tx.Prepare("INSERT INTO messages(id, user_id, body, eventType, hidden_at, timestamp, image, link, title, subtitle) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") if _cachedSaveEventStatement == nil {
if err != nil { stmt, err := _datastore.DB.Prepare("INSERT INTO messages(id, user_id, body, eventType, hidden_at, timestamp, image, link, title, subtitle) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
log.Errorln("error saving", eventType, err) if err != nil {
return log.Errorln("error preparing save event statement", err)
return
}
_cachedSaveEventStatement = stmt
} }
defer stmt.Close() if _, err := _cachedSaveEventStatement.Exec(id, userID, body, eventType, hidden, timestamp, image, link, title, subtitle); err != nil {
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 {
log.Errorln("error saving", eventType, err) log.Errorln("error saving", eventType, err)
return return
} }
@ -212,12 +212,16 @@ type rowData struct {
func getChat(query *sql.Stmt) []interface{} { func getChat(query *sql.Stmt) []interface{} {
history := make([]interface{}, 0) history := make([]interface{}, 0)
rows, err := query.Query() rows, err := query.Query()
if err != nil || rows.Err() != nil { if err != nil || rows.Err() != nil {
log.Errorln("error fetching chat history", err) log.Errorln("error fetching chat history", err)
return history return history
} }
defer rows.Close() defer rows.Close()
rowDatas := []rowData{}
for rows.Next() { for rows.Next() {
row := rowData{} row := rowData{}
@ -248,6 +252,10 @@ func getChat(query *sql.Stmt) []interface{} {
break break
} }
rowDatas = append(rowDatas, row)
}
for _, row := range rowDatas {
var message interface{} var message interface{}
switch row.eventType { switch row.eventType {
@ -271,29 +279,25 @@ func getChat(query *sql.Stmt) []interface{} {
return history return history
} }
var _historyCache *[]interface{}
var cachedAdminChatHistoryStatement *sql.Stmt
// GetChatModerationHistory will return all the chat messages suitable for moderation purposes. // GetChatModerationHistory will return all the chat messages suitable for moderation purposes.
func GetChatModerationHistory() []interface{} { func GetChatModerationHistory() []interface{} {
if _historyCache != nil { if _adminChatHistoryCache != nil {
return *_historyCache 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`) 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 { if err != nil {
log.Errorln("error preparing chat moderation history statement", err) log.Errorln("error preparing chat moderation history statement", err)
return nil return nil
} }
cachedAdminChatHistoryStatement = stmt _cachedAdminChatHistoryStatement = stmt
} }
// Get all messages regardless of visibility // Get all messages regardless of visibility
result := getChat(cachedAdminChatHistoryStatement) result := getChat(_cachedAdminChatHistoryStatement)
_historyCache = &result _adminChatHistoryCache = &result
return 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. // GetChatHistory will return all the chat messages suitable for returning as user-facing chat history.
func GetChatHistory() []interface{} { func GetChatHistory() []interface{} {
if _chatHistoryCache != nil {
return *_chatHistoryCache
}
if cachedChatHistoryStatement == nil { 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)) 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 { if err != nil {
@ -313,6 +321,7 @@ func GetChatHistory() []interface{} {
// Get all visible messages // Get all visible messages
m := getChat(cachedChatHistoryStatement) m := getChat(cachedChatHistoryStatement)
_chatHistoryCache = &m
return m return m
} }
@ -321,7 +330,8 @@ func GetChatHistory() []interface{} {
// and then send out visibility changed events to chat clients. // and then send out visibility changed events to chat clients.
func SetMessageVisibilityForUserID(userID string, visible bool) error { func SetMessageVisibilityForUserID(userID string, visible bool) error {
defer func() { defer func() {
_historyCache = nil _adminChatHistoryCache = nil
_chatHistoryCache = nil
}() }()
// Get a list of IDs to send to the connected clients to hide // 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 { func saveMessageVisibility(messageIDs []string, visible bool) error {
defer func() { defer func() {
_historyCache = nil _adminChatHistoryCache = nil
_chatHistoryCache = nil
}() }()
_datastore.DbLock.Lock() _datastore.DbLock.Lock()

Loading…
Cancel
Save