[bugfix] fix slow accounts / statuses using emojis lookups (#2056)

* update DeleteEmoji to use faster relational tables for status / account finding

Signed-off-by: kim <grufwub@gmail.com>

* update Get{Accounts,Statuses}UsingEmoji() to also use relational tables

Signed-off-by: kim <grufwub@gmail.com>

* remove the now unneeded tags relation from newStatusQ()

Signed-off-by: kim <grufwub@gmail.com>

* fix table names

Signed-off-by: kim <grufwub@gmail.com>

* fix account and status selects using emojis

Signed-off-by: kim <grufwub@gmail.com>

---------

Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
kim 2023-08-02 15:11:23 +01:00 committed by GitHub
parent 24516b84c2
commit 2cee8f2dd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 107 deletions

View file

@ -468,24 +468,13 @@ func (a *accountDB) GetAccountCustomCSSByUsername(ctx context.Context, username
func (a *accountDB) GetAccountsUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Account, error) { func (a *accountDB) GetAccountsUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Account, error) {
var accountIDs []string var accountIDs []string
// Create SELECT account query. // SELECT all accounts using this emoji,
q := a.db.NewSelect(). // using a relational table for improved perf.
Table("accounts"). if _, err := a.db.NewSelect().
Column("id") Table("account_to_emojis").
Column("account_id").
// Append a WHERE LIKE clause to the query Where("? = ?", bun.Ident("emoji_id"), emojiID).
// that checks the `emoji` column for any Exec(ctx, &accountIDs); err != nil {
// text containing this specific emoji ID.
//
// The reason we do this instead of doing a
// `WHERE ? IN (emojis)` is that the latter
// ends up being much MUCH slower, and the
// database stores this ID-array-column as
// text anyways, allowing a simple LIKE query.
q = whereLike(q, "emojis", emojiID)
// Execute the query, scanning destination into accountIDs.
if _, err := q.Exec(ctx, &accountIDs); err != nil {
return nil, a.db.ProcessError(err) return nil, a.db.ProcessError(err)
} }

View file

@ -106,76 +106,25 @@ func (e *emojiDB) DeleteEmojiByID(ctx context.Context, id string) error {
} }
return e.db.RunInTx(ctx, func(tx bun.Tx) error { return e.db.RunInTx(ctx, func(tx bun.Tx) error {
// delete links between this emoji and any statuses that use it // Delete relational links between this emoji
// TODO: remove when we delete this table // and any statuses using it, returning the
if _, err := tx. // status IDs so we can later update them.
NewDelete(). if _, err := tx.NewDelete().
TableExpr("? AS ?", bun.Ident("status_to_emojis"), bun.Ident("status_to_emoji")). Table("status_to_emojis").
Where("? = ?", bun.Ident("status_to_emoji.emoji_id"), id). Where("? = ?", bun.Ident("emoji_id"), id).
Exec(ctx); err != nil { Returning("status_id").
Exec(ctx, &statusIDs); err != nil {
return err return err
} }
// delete links between this emoji and any accounts that use it // Delete relational links between this emoji
// TODO: remove when we delete this table // and any accounts using it, returning the
if _, err := tx. // account IDs so we can later update them.
NewDelete(). if _, err := tx.NewDelete().
TableExpr("? AS ?", bun.Ident("account_to_emojis"), bun.Ident("account_to_emoji")). Table("account_to_emojis").
Where("? = ?", bun.Ident("account_to_emoji.emoji_id"), id). Where("? = ?", bun.Ident("emoji_id"), id).
Exec(ctx); err != nil { Returning("account_id").
return err Exec(ctx, &accountIDs); err != nil {
}
// Prepare a SELECT query with a WHERE LIKE
// that checks the `emoji` column for any
// text containing this specific emoji ID.
//
// (see GetStatusesUsingEmoji() for details.)
aq := tx.NewSelect().Table("accounts").Column("id")
aq = whereLike(aq, "emojis", id)
// Select all accounts using this emoji into accountIDss.
if _, err := aq.Exec(ctx, &accountIDs); err != nil {
return err
}
for _, id := range accountIDs {
var emojiIDs []string
// Select account with ID.
if _, err := tx.NewSelect().
Table("accounts").
Column("emojis").
Where("id = ?", id).
Exec(ctx); err != nil &&
err != sql.ErrNoRows {
return err
}
// Drop ID from account emojis.
emojiIDs = dropID(emojiIDs, id)
// Update account emoji IDs.
if _, err := tx.NewUpdate().
Table("accounts").
Where("id = ?", id).
Set("emojis = ?", emojiIDs).
Exec(ctx); err != nil &&
err != sql.ErrNoRows {
return err
}
}
// Prepare a SELECT query with a WHERE LIKE
// that checks the `emoji` column for any
// text containing this specific emoji ID.
//
// (see GetStatusesUsingEmoji() for details.)
sq := tx.NewSelect().Table("statuses").Column("id")
sq = whereLike(sq, "emojis", id)
// Select all statuses using this emoji into statusIDs.
if _, err := sq.Exec(ctx, &statusIDs); err != nil {
return err return err
} }
@ -186,7 +135,7 @@ func (e *emojiDB) DeleteEmojiByID(ctx context.Context, id string) error {
if _, err := tx.NewSelect(). if _, err := tx.NewSelect().
Table("statuses"). Table("statuses").
Column("emojis"). Column("emojis").
Where("id = ?", id). Where("? = ?", bun.Ident("id"), id).
Exec(ctx); err != nil && Exec(ctx); err != nil &&
err != sql.ErrNoRows { err != sql.ErrNoRows {
return err return err
@ -198,7 +147,34 @@ func (e *emojiDB) DeleteEmojiByID(ctx context.Context, id string) error {
// Update status emoji IDs. // Update status emoji IDs.
if _, err := tx.NewUpdate(). if _, err := tx.NewUpdate().
Table("statuses"). Table("statuses").
Where("id = ?", id). Where("? = ?", bun.Ident("id"), id).
Set("emojis = ?", emojiIDs).
Exec(ctx); err != nil &&
err != sql.ErrNoRows {
return err
}
}
for _, id := range accountIDs {
var emojiIDs []string
// Select account with ID.
if _, err := tx.NewSelect().
Table("accounts").
Column("emojis").
Where("? = ?", bun.Ident("id"), id).
Exec(ctx); err != nil &&
err != sql.ErrNoRows {
return err
}
// Drop ID from account emojis.
emojiIDs = dropID(emojiIDs, id)
// Update account emoji IDs.
if _, err := tx.NewUpdate().
Table("accounts").
Where("? = ?", bun.Ident("id"), id).
Set("emojis = ?", emojiIDs). Set("emojis = ?", emojiIDs).
Exec(ctx); err != nil && Exec(ctx); err != nil &&
err != sql.ErrNoRows { err != sql.ErrNoRows {
@ -209,7 +185,7 @@ func (e *emojiDB) DeleteEmojiByID(ctx context.Context, id string) error {
// Delete emoji from database. // Delete emoji from database.
if _, err := tx.NewDelete(). if _, err := tx.NewDelete().
Table("emojis"). Table("emojis").
Where("id = ?", id). Where("? = ?", bun.Ident("id"), id).
Exec(ctx); err != nil { Exec(ctx); err != nil {
return err return err
} }

View file

@ -43,7 +43,6 @@ func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery {
return s.db. return s.db.
NewSelect(). NewSelect().
Model(status). Model(status).
Relation("Tags").
Relation("CreatedWithApplication") Relation("CreatedWithApplication")
} }
@ -440,24 +439,13 @@ func (s *statusDB) DeleteStatusByID(ctx context.Context, id string) error {
func (s *statusDB) GetStatusesUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Status, error) { func (s *statusDB) GetStatusesUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Status, error) {
var statusIDs []string var statusIDs []string
// Create SELECT status query. // SELECT all statuses using this emoji,
q := s.db.NewSelect(). // using a relational table for improved perf.
Table("statuses"). if _, err := s.db.NewSelect().
Column("id") Table("status_to_emojis").
Column("status_id").
// Append a WHERE LIKE clause to the query Where("? = ?", bun.Ident("emoji_id"), emojiID).
// that checks the `emoji` column for any Exec(ctx, &statusIDs); err != nil {
// text containing this specific emoji ID.
//
// The reason we do this instead of doing a
// `WHERE ? IN (emojis)` is that the latter
// ends up being much MUCH slower, and the
// database stores this ID-array-column as
// text anyways, allowing a simple LIKE query.
q = whereLike(q, "emojis", emojiID)
// Execute the query, scanning destination into statusIDs.
if _, err := q.Exec(ctx, &statusIDs); err != nil {
return nil, s.db.ProcessError(err) return nil, s.db.ProcessError(err)
} }