summaryrefslogtreecommitdiff
path: root/internal/db/bundb/tag.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-01-19 12:57:29 +0000
committerLibravatar GitHub <noreply@github.com>2024-01-19 12:57:29 +0000
commit7ec1e1332e7d04e74451acef18b41f389722b698 (patch)
tree9c69eca7fc664ab5564279a2e065dfd5c2ddd17b /internal/db/bundb/tag.go
parent[chore] chore rationalise http return codes for activitypub handlers (#2540) (diff)
downloadgotosocial-7ec1e1332e7d04e74451acef18b41f389722b698.tar.xz
[performance] overhaul struct (+ result) caching library for simplicity, performance and multiple-result lookups (#2535)
* rewrite cache library as codeberg.org/gruf/go-structr, implement in gotosocial * use actual go-structr release version (not just commit hash) * revert go toolchain changes (damn you go for auto changing this) * fix go mod woes * ensure %w is used in calls to errs.Appendf() * fix error checking * fix possible panic * remove unnecessary start/stop functions, move to main Cache{} struct, add note regarding which caches require start/stop * fix copy-paste artifact... :innocent: * fix all comment copy-paste artifacts * remove dropID() function, now we can just use slices.DeleteFunc() * use util.Deduplicate() instead of collate(), move collate to util * move orderByIDs() to util package and "generify" * add a util.DeleteIf() function, use this to delete entries on failed population * use slices.DeleteFunc() instead of util.DeleteIf() (i had the logic mixed up in my head somehow lol) * add note about how collate differs from deduplicate
Diffstat (limited to 'internal/db/bundb/tag.go')
-rw-r--r--internal/db/bundb/tag.go75
1 files changed, 51 insertions, 24 deletions
diff --git a/internal/db/bundb/tag.go b/internal/db/bundb/tag.go
index fac621f0a..66ee8cb3a 100644
--- a/internal/db/bundb/tag.go
+++ b/internal/db/bundb/tag.go
@@ -22,21 +22,21 @@ import (
"strings"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
+ "github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/uptrace/bun"
)
type tagDB struct {
- conn *DB
+ db *DB
state *state.State
}
-func (m *tagDB) GetTag(ctx context.Context, id string) (*gtsmodel.Tag, error) {
- return m.state.Caches.GTS.Tag().Load("ID", func() (*gtsmodel.Tag, error) {
+func (t *tagDB) GetTag(ctx context.Context, id string) (*gtsmodel.Tag, error) {
+ return t.state.Caches.GTS.Tag.LoadOne("ID", func() (*gtsmodel.Tag, error) {
var tag gtsmodel.Tag
- q := m.conn.
+ q := t.db.
NewSelect().
Model(&tag).
Where("? = ?", bun.Ident("tag.id"), id)
@@ -49,15 +49,15 @@ func (m *tagDB) GetTag(ctx context.Context, id string) (*gtsmodel.Tag, error) {
}, id)
}
-func (m *tagDB) GetTagByName(ctx context.Context, name string) (*gtsmodel.Tag, error) {
+func (t *tagDB) GetTagByName(ctx context.Context, name string) (*gtsmodel.Tag, error) {
// Normalize 'name' string.
name = strings.TrimSpace(name)
name = strings.ToLower(name)
- return m.state.Caches.GTS.Tag().Load("Name", func() (*gtsmodel.Tag, error) {
+ return t.state.Caches.GTS.Tag.LoadOne("Name", func() (*gtsmodel.Tag, error) {
var tag gtsmodel.Tag
- q := m.conn.
+ q := t.db.
NewSelect().
Model(&tag).
Where("? = ?", bun.Ident("tag.name"), name)
@@ -70,25 +70,52 @@ func (m *tagDB) GetTagByName(ctx context.Context, name string) (*gtsmodel.Tag, e
}, name)
}
-func (m *tagDB) GetTags(ctx context.Context, ids []string) ([]*gtsmodel.Tag, error) {
- tags := make([]*gtsmodel.Tag, 0, len(ids))
-
- for _, id := range ids {
- // Attempt fetch from DB
- tag, err := m.GetTag(ctx, id)
- if err != nil {
- log.Errorf(ctx, "error getting tag %q: %v", id, err)
- continue
- }
-
- // Append tag
- tags = append(tags, tag)
+func (t *tagDB) GetTags(ctx context.Context, ids []string) ([]*gtsmodel.Tag, error) {
+ // Preallocate at-worst possible length.
+ uncached := make([]string, 0, len(ids))
+
+ // Load all tag IDs via cache loader callbacks.
+ tags, err := t.state.Caches.GTS.Tag.Load("ID",
+
+ // Load cached + check for uncached.
+ func(load func(keyParts ...any) bool) {
+ for _, id := range ids {
+ if !load(id) {
+ uncached = append(uncached, id)
+ }
+ }
+ },
+
+ // Uncached tag loader function.
+ func() ([]*gtsmodel.Tag, error) {
+ // Preallocate expected length of uncached tags.
+ tags := make([]*gtsmodel.Tag, 0, len(uncached))
+
+ // Perform database query scanning
+ // the remaining (uncached) IDs.
+ if err := t.db.NewSelect().
+ Model(&tags).
+ Where("? IN (?)", bun.Ident("id"), bun.In(uncached)).
+ Scan(ctx); err != nil {
+ return nil, err
+ }
+
+ return tags, nil
+ },
+ )
+ if err != nil {
+ return nil, err
}
+ // Reorder the tags by their
+ // IDs to ensure in correct order.
+ getID := func(t *gtsmodel.Tag) string { return t.ID }
+ util.OrderBy(tags, ids, getID)
+
return tags, nil
}
-func (m *tagDB) PutTag(ctx context.Context, tag *gtsmodel.Tag) error {
+func (t *tagDB) PutTag(ctx context.Context, tag *gtsmodel.Tag) error {
// Normalize 'name' string before it enters
// the db, without changing tag we were given.
//
@@ -101,8 +128,8 @@ func (m *tagDB) PutTag(ctx context.Context, tag *gtsmodel.Tag) error {
t2.Name = strings.ToLower(t2.Name)
// Insert the copy.
- if err := m.state.Caches.GTS.Tag().Store(t2, func() error {
- _, err := m.conn.NewInsert().Model(t2).Exec(ctx)
+ if err := t.state.Caches.GTS.Tag.Store(t2, func() error {
+ _, err := t.db.NewInsert().Model(t2).Exec(ctx)
return err
}); err != nil {
return err // err already processed