[chore] move caches to a separate State{} structure (#1078)

* move caches to a separate State{} structure

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

* fix call to log.Panic not using formatted call

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

* move caches to use interfaces, to make switchouts easier in future

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

* fix rebase issue

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

* improve code comment

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

* fix further issues after rebase

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

* heh

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

* add missing license text

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

Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
kim 2022-12-08 17:35:14 +00:00 committed by GitHub
commit e58d2d8122
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 725 additions and 332 deletions

View file

@ -25,39 +25,18 @@ import (
"strings"
"time"
"codeberg.org/gruf/go-cache/v3/result"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
)
type accountDB struct {
conn *DBConn
cache *result.Cache[*gtsmodel.Account]
emojis *emojiDB
status *statusDB
}
func (a *accountDB) init() {
// Initialize account result cache
a.cache = result.NewSized([]result.Lookup{
{Name: "ID"},
{Name: "URI"},
{Name: "URL"},
{Name: "Username.Domain"},
{Name: "PublicKeyURI"},
}, func(a1 *gtsmodel.Account) *gtsmodel.Account {
a2 := new(gtsmodel.Account)
*a2 = *a1
return a2
}, 1000)
// Set cache TTL and start sweep routine
a.cache.SetTTL(time.Minute*5, false)
a.cache.Start(time.Second * 10)
conn *DBConn
state *state.State
}
func (a *accountDB) newAccountQ(account *gtsmodel.Account) *bun.SelectQuery {
@ -152,7 +131,7 @@ func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gts
func (a *accountDB) getAccount(ctx context.Context, lookup string, dbQuery func(*gtsmodel.Account) error, keyParts ...any) (*gtsmodel.Account, db.Error) {
// Fetch account from database cache with loader callback
account, err := a.cache.Load(lookup, func() (*gtsmodel.Account, error) {
account, err := a.state.Caches.GTS.Account().Load(lookup, func() (*gtsmodel.Account, error) {
var account gtsmodel.Account
// Not cached! Perform database query
@ -168,7 +147,7 @@ func (a *accountDB) getAccount(ctx context.Context, lookup string, dbQuery func(
if len(account.EmojiIDs) > 0 {
// Set the account's related emojis
account.Emojis, err = a.emojis.emojisFromIDs(ctx, account.EmojiIDs)
account.Emojis, err = a.state.DB.GetEmojisByIDs(ctx, account.EmojiIDs)
if err != nil {
return nil, fmt.Errorf("error getting account emojis: %w", err)
}
@ -178,7 +157,7 @@ func (a *accountDB) getAccount(ctx context.Context, lookup string, dbQuery func(
}
func (a *accountDB) PutAccount(ctx context.Context, account *gtsmodel.Account) db.Error {
return a.cache.Store(account, func() error {
return a.state.Caches.GTS.Account().Store(account, func() error {
// It is safe to run this database transaction within cache.Store
// as the cache does not attempt a mutex lock until AFTER hook.
//
@ -204,7 +183,7 @@ func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account
// Update the account's last-updated
account.UpdatedAt = time.Now()
return a.cache.Store(account, func() error {
return a.state.Caches.GTS.Account().Store(account, func() error {
// It is safe to run this database transaction within cache.Store
// as the cache does not attempt a mutex lock until AFTER hook.
//
@ -263,7 +242,7 @@ func (a *accountDB) DeleteAccount(ctx context.Context, id string) db.Error {
return err
}
a.cache.Invalidate("ID", id)
a.state.Caches.GTS.Account().Invalidate("ID", id)
return nil
}
@ -514,7 +493,7 @@ func (a *accountDB) statusesFromIDs(ctx context.Context, statusIDs []string) ([]
for _, id := range statusIDs {
// Fetch from status from database by ID
status, err := a.status.GetStatusByID(ctx, id)
status, err := a.state.DB.GetStatusByID(ctx, id)
if err != nil {
log.Errorf("statusesFromIDs: error getting status %q: %v", id, err)
continue

View file

@ -34,6 +34,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/uris"
"github.com/uptrace/bun"
"golang.org/x/crypto/bcrypt"
@ -43,9 +44,8 @@ import (
const rsaKeyBits = 2048
type adminDB struct {
conn *DBConn
accounts *accountDB
users *userDB
conn *DBConn
state *state.State
}
func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, db.Error) {
@ -139,7 +139,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string,
}
// insert the new account!
if err := a.accounts.PutAccount(ctx, acct); err != nil {
if err := a.state.DB.PutAccount(ctx, acct); err != nil {
return nil, err
}
}
@ -185,7 +185,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string,
}
// insert the user!
if err := a.users.PutUser(ctx, u); err != nil {
if err := a.state.DB.PutUser(ctx, u); err != nil {
return nil, err
}
@ -241,7 +241,7 @@ func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error {
}
// insert the new account!
if err := a.accounts.PutAccount(ctx, acct); err != nil {
if err := a.state.DB.PutAccount(ctx, acct); err != nil {
return err
}

View file

@ -40,6 +40,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/dialect/sqlitedialect"
@ -122,7 +123,7 @@ func doMigration(ctx context.Context, db *bun.DB) error {
// NewBunDBService returns a bunDB derived from the provided config, which implements the go-fed DB interface.
// Under the hood, it uses https://github.com/uptrace/bun to create and maintain a database connection.
func NewBunDBService(ctx context.Context) (db.DB, error) {
func NewBunDBService(ctx context.Context, state *state.State) (db.DB, error) {
var conn *DBConn
var err error
dbType := strings.ToLower(config.GetDbType())
@ -158,69 +159,64 @@ func NewBunDBService(ctx context.Context) (db.DB, error) {
return nil, fmt.Errorf("db migration error: %s", err)
}
// Create DB structs that require ptrs to each other
account := &accountDB{conn: conn}
admin := &adminDB{conn: conn}
domain := &domainDB{conn: conn}
mention := &mentionDB{conn: conn}
notif := &notificationDB{conn: conn}
status := &statusDB{conn: conn}
emoji := &emojiDB{conn: conn}
relationship := &relationshipDB{conn: conn}
timeline := &timelineDB{conn: conn}
tombstone := &tombstoneDB{conn: conn}
user := &userDB{conn: conn}
// Setup DB cross-referencing
account.emojis = emoji
account.status = status
admin.users = user
relationship.accounts = account
status.accounts = account
status.emojis = emoji
status.mentions = mention
timeline.status = status
// Initialize db structs
account.init()
domain.init()
emoji.init()
mention.init()
notif.init()
relationship.init()
status.init()
tombstone.init()
user.init()
ps := &DBService{
Account: account,
Account: &accountDB{
conn: conn,
state: state,
},
Admin: &adminDB{
conn: conn,
accounts: account,
users: user,
conn: conn,
state: state,
},
Basic: &basicDB{
conn: conn,
},
Domain: domain,
Emoji: emoji,
Domain: &domainDB{
conn: conn,
state: state,
},
Emoji: &emojiDB{
conn: conn,
state: state,
},
Instance: &instanceDB{
conn: conn,
},
Media: &mediaDB{
conn: conn,
},
Mention: mention,
Notification: notif,
Relationship: relationship,
Mention: &mentionDB{
conn: conn,
state: state,
},
Notification: &notificationDB{
conn: conn,
state: state,
},
Relationship: &relationshipDB{
conn: conn,
state: state,
},
Session: &sessionDB{
conn: conn,
},
Status: status,
Timeline: timeline,
User: user,
Tombstone: tombstone,
conn: conn,
Status: &statusDB{
conn: conn,
state: state,
},
Timeline: &timelineDB{
conn: conn,
state: state,
},
User: &userDB{
conn: conn,
state: state,
},
Tombstone: &tombstoneDB{
conn: conn,
state: state,
},
conn: conn,
}
// we can confidently return this useable service now

View file

@ -33,7 +33,7 @@ type BundbNewTestSuite struct {
func (suite *BundbNewTestSuite) TestCreateNewDB() {
// create a new db with standard test settings
db, err := bundb.NewBunDBService(context.Background())
db, err := bundb.NewBunDBService(context.Background(), nil)
suite.NoError(err)
suite.NotNil(db)
}
@ -42,7 +42,7 @@ func (suite *BundbNewTestSuite) TestCreateNewSqliteDBNoAddress() {
// create a new db with no address specified
config.SetDbAddress("")
config.SetDbType("sqlite")
db, err := bundb.NewBunDBService(context.Background())
db, err := bundb.NewBunDBService(context.Background(), nil)
suite.EqualError(err, "'db-address' was not set when attempting to start sqlite")
suite.Nil(db)
}

View file

@ -22,34 +22,18 @@ import (
"context"
"net/url"
"strings"
"time"
"codeberg.org/gruf/go-cache/v3/result"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
"golang.org/x/net/idna"
)
type domainDB struct {
conn *DBConn
cache *result.Cache[*gtsmodel.DomainBlock]
}
func (d *domainDB) init() {
// Initialize domain block result cache
d.cache = result.NewSized([]result.Lookup{
{Name: "Domain"},
}, func(d1 *gtsmodel.DomainBlock) *gtsmodel.DomainBlock {
d2 := new(gtsmodel.DomainBlock)
*d2 = *d1
return d2
}, 1000)
// Set cache TTL and start sweep routine
d.cache.SetTTL(time.Minute*5, false)
d.cache.Start(time.Second * 10)
state *state.State
}
// normalizeDomain converts the given domain to lowercase
@ -71,7 +55,7 @@ func (d *domainDB) CreateDomainBlock(ctx context.Context, block *gtsmodel.Domain
return err
}
return d.cache.Store(block, func() error {
return d.state.Caches.GTS.DomainBlock().Store(block, func() error {
_, err := d.conn.NewInsert().
Model(block).
Exec(ctx)
@ -87,7 +71,7 @@ func (d *domainDB) GetDomainBlock(ctx context.Context, domain string) (*gtsmodel
return nil, err
}
return d.cache.Load("Domain", func() (*gtsmodel.DomainBlock, error) {
return d.state.Caches.GTS.DomainBlock().Load("Domain", func() (*gtsmodel.DomainBlock, error) {
// Check for easy case, domain referencing *us*
if domain == "" || domain == config.GetAccountDomain() {
return nil, db.ErrNoEntries
@ -125,7 +109,7 @@ func (d *domainDB) DeleteDomainBlock(ctx context.Context, domain string) db.Erro
}
// Clear domain from cache
d.cache.Invalidate("Domain", domain)
d.state.Caches.GTS.DomainBlock().Invalidate(domain)
return nil
}

View file

@ -23,50 +23,17 @@ import (
"strings"
"time"
"codeberg.org/gruf/go-cache/v3/result"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
)
type emojiDB struct {
conn *DBConn
emojiCache *result.Cache[*gtsmodel.Emoji]
categoryCache *result.Cache[*gtsmodel.EmojiCategory]
}
func (e *emojiDB) init() {
// Initialize emoji result cache
e.emojiCache = result.NewSized([]result.Lookup{
{Name: "ID"},
{Name: "URI"},
{Name: "Shortcode.Domain"},
{Name: "ImageStaticURL"},
}, func(e1 *gtsmodel.Emoji) *gtsmodel.Emoji {
e2 := new(gtsmodel.Emoji)
*e2 = *e1
return e2
}, 1000)
// Set cache TTL and start sweep routine
e.emojiCache.SetTTL(time.Minute*5, false)
e.emojiCache.Start(time.Second * 10)
// Initialize category result cache
e.categoryCache = result.NewSized([]result.Lookup{
{Name: "ID"},
{Name: "Name"},
}, func(c1 *gtsmodel.EmojiCategory) *gtsmodel.EmojiCategory {
c2 := new(gtsmodel.EmojiCategory)
*c2 = *c1
return c2
}, 1000)
// Set cache TTL and start sweep routine
e.categoryCache.SetTTL(time.Minute*5, false)
e.categoryCache.Start(time.Second * 10)
conn *DBConn
state *state.State
}
func (e *emojiDB) newEmojiQ(emoji *gtsmodel.Emoji) *bun.SelectQuery {
@ -83,7 +50,7 @@ func (e *emojiDB) newEmojiCategoryQ(emojiCategory *gtsmodel.EmojiCategory) *bun.
}
func (e *emojiDB) PutEmoji(ctx context.Context, emoji *gtsmodel.Emoji) db.Error {
return e.emojiCache.Store(emoji, func() error {
return e.state.Caches.GTS.Emoji().Store(emoji, func() error {
_, err := e.conn.NewInsert().Model(emoji).Exec(ctx)
return e.conn.ProcessError(err)
})
@ -102,7 +69,7 @@ func (e *emojiDB) UpdateEmoji(ctx context.Context, emoji *gtsmodel.Emoji, column
return nil, e.conn.ProcessError(err)
}
e.emojiCache.Invalidate("ID", emoji.ID)
e.state.Caches.GTS.Emoji().Invalidate("ID", emoji.ID)
return emoji, nil
}
@ -139,7 +106,7 @@ func (e *emojiDB) DeleteEmojiByID(ctx context.Context, id string) db.Error {
return err
}
e.emojiCache.Invalidate("ID", id)
e.state.Caches.GTS.Emoji().Invalidate("ID", id)
return nil
}
@ -257,7 +224,7 @@ func (e *emojiDB) GetEmojis(ctx context.Context, domain string, includeDisabled
}
}
return e.emojisFromIDs(ctx, emojiIDs)
return e.GetEmojisByIDs(ctx, emojiIDs)
}
func (e *emojiDB) GetUseableEmojis(ctx context.Context) ([]*gtsmodel.Emoji, db.Error) {
@ -276,7 +243,7 @@ func (e *emojiDB) GetUseableEmojis(ctx context.Context) ([]*gtsmodel.Emoji, db.E
return nil, e.conn.ProcessError(err)
}
return e.emojisFromIDs(ctx, emojiIDs)
return e.GetEmojisByIDs(ctx, emojiIDs)
}
func (e *emojiDB) GetEmojiByID(ctx context.Context, id string) (*gtsmodel.Emoji, db.Error) {
@ -338,7 +305,7 @@ func (e *emojiDB) GetEmojiByStaticURL(ctx context.Context, imageStaticURL string
}
func (e *emojiDB) PutEmojiCategory(ctx context.Context, emojiCategory *gtsmodel.EmojiCategory) db.Error {
return e.categoryCache.Store(emojiCategory, func() error {
return e.state.Caches.GTS.EmojiCategory().Store(emojiCategory, func() error {
_, err := e.conn.NewInsert().Model(emojiCategory).Exec(ctx)
return e.conn.ProcessError(err)
})
@ -357,7 +324,7 @@ func (e *emojiDB) GetEmojiCategories(ctx context.Context) ([]*gtsmodel.EmojiCate
return nil, e.conn.ProcessError(err)
}
return e.emojiCategoriesFromIDs(ctx, emojiCategoryIDs)
return e.GetEmojiCategoriesByIDs(ctx, emojiCategoryIDs)
}
func (e *emojiDB) GetEmojiCategory(ctx context.Context, id string) (*gtsmodel.EmojiCategory, db.Error) {
@ -383,7 +350,7 @@ func (e *emojiDB) GetEmojiCategoryByName(ctx context.Context, name string) (*gts
}
func (e *emojiDB) getEmoji(ctx context.Context, lookup string, dbQuery func(*gtsmodel.Emoji) error, keyParts ...any) (*gtsmodel.Emoji, db.Error) {
return e.emojiCache.Load(lookup, func() (*gtsmodel.Emoji, error) {
return e.state.Caches.GTS.Emoji().Load(lookup, func() (*gtsmodel.Emoji, error) {
var emoji gtsmodel.Emoji
// Not cached! Perform database query
@ -395,8 +362,7 @@ func (e *emojiDB) getEmoji(ctx context.Context, lookup string, dbQuery func(*gts
}, keyParts...)
}
func (e *emojiDB) emojisFromIDs(ctx context.Context, emojiIDs []string) ([]*gtsmodel.Emoji, db.Error) {
// Catch case of no emojis early
func (e *emojiDB) GetEmojisByIDs(ctx context.Context, emojiIDs []string) ([]*gtsmodel.Emoji, db.Error) {
if len(emojiIDs) == 0 {
return nil, db.ErrNoEntries
}
@ -417,7 +383,7 @@ func (e *emojiDB) emojisFromIDs(ctx context.Context, emojiIDs []string) ([]*gtsm
}
func (e *emojiDB) getEmojiCategory(ctx context.Context, lookup string, dbQuery func(*gtsmodel.EmojiCategory) error, keyParts ...any) (*gtsmodel.EmojiCategory, db.Error) {
return e.categoryCache.Load(lookup, func() (*gtsmodel.EmojiCategory, error) {
return e.state.Caches.GTS.EmojiCategory().Load(lookup, func() (*gtsmodel.EmojiCategory, error) {
var category gtsmodel.EmojiCategory
// Not cached! Perform database query
@ -429,8 +395,7 @@ func (e *emojiDB) getEmojiCategory(ctx context.Context, lookup string, dbQuery f
}, keyParts...)
}
func (e *emojiDB) emojiCategoriesFromIDs(ctx context.Context, emojiCategoryIDs []string) ([]*gtsmodel.EmojiCategory, db.Error) {
// Catch case of no emoji categories early
func (e *emojiDB) GetEmojiCategoriesByIDs(ctx context.Context, emojiCategoryIDs []string) ([]*gtsmodel.EmojiCategory, db.Error) {
if len(emojiCategoryIDs) == 0 {
return nil, db.ErrNoEntries
}

View file

@ -20,33 +20,17 @@ package bundb
import (
"context"
"time"
"codeberg.org/gruf/go-cache/v3/result"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
)
type mentionDB struct {
conn *DBConn
cache *result.Cache[*gtsmodel.Mention]
}
func (m *mentionDB) init() {
// Initialize notification result cache
m.cache = result.NewSized([]result.Lookup{
{Name: "ID"},
}, func(m1 *gtsmodel.Mention) *gtsmodel.Mention {
m2 := new(gtsmodel.Mention)
*m2 = *m1
return m2
}, 1000)
// Set cache TTL and start sweep routine
m.cache.SetTTL(time.Minute*5, false)
m.cache.Start(time.Second * 10)
state *state.State
}
func (m *mentionDB) newMentionQ(i interface{}) *bun.SelectQuery {
@ -59,7 +43,7 @@ func (m *mentionDB) newMentionQ(i interface{}) *bun.SelectQuery {
}
func (m *mentionDB) GetMention(ctx context.Context, id string) (*gtsmodel.Mention, db.Error) {
return m.cache.Load("ID", func() (*gtsmodel.Mention, error) {
return m.state.Caches.GTS.Mention().Load("ID", func() (*gtsmodel.Mention, error) {
var mention gtsmodel.Mention
q := m.newMentionQ(&mention).

View file

@ -20,37 +20,21 @@ package bundb
import (
"context"
"time"
"codeberg.org/gruf/go-cache/v3/result"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
)
type notificationDB struct {
conn *DBConn
cache *result.Cache[*gtsmodel.Notification]
}
func (n *notificationDB) init() {
// Initialize notification result cache
n.cache = result.NewSized([]result.Lookup{
{Name: "ID"},
}, func(n1 *gtsmodel.Notification) *gtsmodel.Notification {
n2 := new(gtsmodel.Notification)
*n2 = *n1
return n2
}, 1000)
// Set cache TTL and start sweep routine
n.cache.SetTTL(time.Minute*5, false)
n.cache.Start(time.Second * 10)
state *state.State
}
func (n *notificationDB) GetNotification(ctx context.Context, id string) (*gtsmodel.Notification, db.Error) {
return n.cache.Load("ID", func() (*gtsmodel.Notification, error) {
return n.state.Caches.GTS.Notification().Load("ID", func() (*gtsmodel.Notification, error) {
var notif gtsmodel.Notification
q := n.conn.NewSelect().
@ -130,6 +114,6 @@ func (n *notificationDB) ClearNotifications(ctx context.Context, accountID strin
return n.conn.ProcessError(err)
}
n.cache.Clear()
n.state.Caches.GTS.Notification().Clear()
return nil
}

View file

@ -23,35 +23,16 @@ import (
"database/sql"
"errors"
"fmt"
"time"
"codeberg.org/gruf/go-cache/v3/result"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
)
type relationshipDB struct {
conn *DBConn
accounts *accountDB
blockCache *result.Cache[*gtsmodel.Block]
}
func (r *relationshipDB) init() {
// Initialize block result cache
r.blockCache = result.NewSized([]result.Lookup{
{Name: "ID"},
{Name: "AccountID.TargetAccountID"},
{Name: "URI"},
}, func(b1 *gtsmodel.Block) *gtsmodel.Block {
b2 := new(gtsmodel.Block)
*b2 = *b1
return b2
}, 1000)
// Set cache TTL and start sweep routine
r.blockCache.SetTTL(time.Minute*5, false)
r.blockCache.Start(time.Second * 10)
conn *DBConn
state *state.State
}
func (r *relationshipDB) newFollowQ(follow interface{}) *bun.SelectQuery {
@ -94,13 +75,13 @@ func (r *relationshipDB) GetBlock(ctx context.Context, account1 string, account2
}
// Set the block originating account
block.Account, err = r.accounts.GetAccountByID(ctx, block.AccountID)
block.Account, err = r.state.DB.GetAccountByID(ctx, block.AccountID)
if err != nil {
return nil, err
}
// Set the block target account
block.TargetAccount, err = r.accounts.GetAccountByID(ctx, block.TargetAccountID)
block.TargetAccount, err = r.state.DB.GetAccountByID(ctx, block.TargetAccountID)
if err != nil {
return nil, err
}
@ -109,7 +90,7 @@ func (r *relationshipDB) GetBlock(ctx context.Context, account1 string, account2
}
func (r *relationshipDB) getBlock(ctx context.Context, account1 string, account2 string) (*gtsmodel.Block, db.Error) {
return r.blockCache.Load("AccountID.TargetAccountID", func() (*gtsmodel.Block, error) {
return r.state.Caches.GTS.Block().Load("AccountID.TargetAccountID", func() (*gtsmodel.Block, error) {
var block gtsmodel.Block
q := r.conn.NewSelect().Model(&block).
@ -124,7 +105,7 @@ func (r *relationshipDB) getBlock(ctx context.Context, account1 string, account2
}
func (r *relationshipDB) PutBlock(ctx context.Context, block *gtsmodel.Block) db.Error {
return r.blockCache.Store(block, func() error {
return r.state.Caches.GTS.Block().Store(block, func() error {
_, err := r.conn.NewInsert().Model(block).Exec(ctx)
return r.conn.ProcessError(err)
})
@ -140,7 +121,7 @@ func (r *relationshipDB) DeleteBlockByID(ctx context.Context, id string) db.Erro
}
// Drop any old value from cache by this ID
r.blockCache.Invalidate("ID", id)
r.state.Caches.GTS.Block().Invalidate("ID", id)
return nil
}
@ -154,7 +135,7 @@ func (r *relationshipDB) DeleteBlockByURI(ctx context.Context, uri string) db.Er
}
// Drop any old value from cache by this URI
r.blockCache.Invalidate("URI", uri)
r.state.Caches.GTS.Block().Invalidate("URI", uri)
return nil
}

View file

@ -26,36 +26,16 @@ import (
"fmt"
"time"
"codeberg.org/gruf/go-cache/v3/result"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
)
type statusDB struct {
conn *DBConn
cache *result.Cache[*gtsmodel.Status]
accounts *accountDB
emojis *emojiDB
mentions *mentionDB
}
func (s *statusDB) init() {
// Initialize status result cache
s.cache = result.NewSized([]result.Lookup{
{Name: "ID"},
{Name: "URI"},
{Name: "URL"},
}, func(s1 *gtsmodel.Status) *gtsmodel.Status {
s2 := new(gtsmodel.Status)
*s2 = *s1
return s2
}, 1000)
// Set cache TTL and start sweep routine
s.cache.SetTTL(time.Minute*5, false)
s.cache.Start(time.Second * 10)
conn *DBConn
state *state.State
}
func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery {
@ -111,7 +91,7 @@ func (s *statusDB) GetStatusByURL(ctx context.Context, url string) (*gtsmodel.St
func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*gtsmodel.Status) error, keyParts ...any) (*gtsmodel.Status, db.Error) {
// Fetch status from database cache with loader callback
status, err := s.cache.Load(lookup, func() (*gtsmodel.Status, error) {
status, err := s.state.Caches.GTS.Status().Load(lookup, func() (*gtsmodel.Status, error) {
var status gtsmodel.Status
// Not cached! Perform database query
@ -149,14 +129,14 @@ func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*g
}
// Set the status author account
status.Account, err = s.accounts.GetAccountByID(ctx, status.AccountID)
status.Account, err = s.state.DB.GetAccountByID(ctx, status.AccountID)
if err != nil {
return nil, fmt.Errorf("error getting status account: %w", err)
}
if id := status.BoostOfAccountID; id != "" {
// Set boost of status' author account
status.BoostOfAccount, err = s.accounts.GetAccountByID(ctx, id)
status.BoostOfAccount, err = s.state.DB.GetAccountByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("error getting boosted status account: %w", err)
}
@ -164,7 +144,7 @@ func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*g
if id := status.InReplyToAccountID; id != "" {
// Set in-reply-to status' author account
status.InReplyToAccount, err = s.accounts.GetAccountByID(ctx, id)
status.InReplyToAccount, err = s.state.DB.GetAccountByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("error getting in reply to status account: %w", err)
}
@ -172,7 +152,7 @@ func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*g
if len(status.EmojiIDs) > 0 {
// Fetch status emojis
status.Emojis, err = s.emojis.emojisFromIDs(ctx, status.EmojiIDs)
status.Emojis, err = s.state.DB.GetEmojisByIDs(ctx, status.EmojiIDs)
if err != nil {
return nil, fmt.Errorf("error getting status emojis: %w", err)
}
@ -180,7 +160,7 @@ func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*g
if len(status.MentionIDs) > 0 {
// Fetch status mentions
status.Mentions, err = s.mentions.GetMentions(ctx, status.MentionIDs)
status.Mentions, err = s.state.DB.GetMentions(ctx, status.MentionIDs)
if err != nil {
return nil, fmt.Errorf("error getting status mentions: %w", err)
}
@ -190,7 +170,7 @@ func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*g
}
func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Error {
return s.cache.Store(status, func() error {
return s.state.Caches.GTS.Status().Store(status, func() error {
// It is safe to run this database transaction within cache.Store
// as the cache does not attempt a mutex lock until AFTER hook.
//
@ -308,7 +288,7 @@ func (s *statusDB) UpdateStatus(ctx context.Context, status *gtsmodel.Status) db
}
// Drop any old value from cache by this ID
s.cache.Invalidate("ID", status.ID)
s.state.Caches.GTS.Status().Invalidate("ID", status.ID)
return nil
}
@ -347,7 +327,7 @@ func (s *statusDB) DeleteStatusByID(ctx context.Context, id string) db.Error {
}
// Drop any old value from cache by this ID
s.cache.Invalidate("ID", id)
s.state.Caches.GTS.Status().Invalidate("ID", id)
return nil
}

View file

@ -26,13 +26,14 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
"golang.org/x/exp/slices"
)
type timelineDB struct {
conn *DBConn
status *statusDB
conn *DBConn
state *state.State
}
func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
@ -111,7 +112,7 @@ func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxI
for _, id := range statusIDs {
// Fetch status from db for ID
status, err := t.status.GetStatusByID(ctx, id)
status, err := t.state.DB.GetStatusByID(ctx, id)
if err != nil {
log.Errorf("GetHomeTimeline: error fetching status %q: %v", id, err)
continue
@ -179,7 +180,7 @@ func (t *timelineDB) GetPublicTimeline(ctx context.Context, maxID string, sinceI
for _, id := range statusIDs {
// Fetch status from db for ID
status, err := t.status.GetStatusByID(ctx, id)
status, err := t.state.DB.GetStatusByID(ctx, id)
if err != nil {
log.Errorf("GetPublicTimeline: error fetching status %q: %v", id, err)
continue
@ -239,7 +240,7 @@ func (t *timelineDB) GetFavedTimeline(ctx context.Context, accountID string, max
for _, fave := range faves {
// Fetch status from db for corresponding favourite
status, err := t.status.GetStatusByID(ctx, fave.StatusID)
status, err := t.state.DB.GetStatusByID(ctx, fave.StatusID)
if err != nil {
log.Errorf("GetFavedTimeline: error fetching status for fave %q: %v", fave.ID, err)
continue

View file

@ -20,38 +20,20 @@ package bundb
import (
"context"
"time"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
"codeberg.org/gruf/go-cache/v3/result"
)
type tombstoneDB struct {
conn *DBConn
cache *result.Cache[*gtsmodel.Tombstone]
}
func (t *tombstoneDB) init() {
// Initialize tombstone result cache
t.cache = result.NewSized([]result.Lookup{
{Name: "ID"},
{Name: "URI"},
}, func(t1 *gtsmodel.Tombstone) *gtsmodel.Tombstone {
t2 := new(gtsmodel.Tombstone)
*t2 = *t1
return t2
}, 100)
// Set cache TTL and start sweep routine
t.cache.SetTTL(time.Minute*5, false)
t.cache.Start(time.Second * 10)
state *state.State
}
func (t *tombstoneDB) GetTombstoneByURI(ctx context.Context, uri string) (*gtsmodel.Tombstone, db.Error) {
return t.cache.Load("URI", func() (*gtsmodel.Tombstone, error) {
return t.state.Caches.GTS.Tombstone().Load("URI", func() (*gtsmodel.Tombstone, error) {
var tomb gtsmodel.Tombstone
q := t.conn.
@ -76,7 +58,7 @@ func (t *tombstoneDB) TombstoneExistsWithURI(ctx context.Context, uri string) (b
}
func (t *tombstoneDB) PutTombstone(ctx context.Context, tombstone *gtsmodel.Tombstone) db.Error {
return t.cache.Store(tombstone, func() error {
return t.state.Caches.GTS.Tombstone().Store(tombstone, func() error {
_, err := t.conn.
NewInsert().
Model(tombstone).
@ -95,7 +77,7 @@ func (t *tombstoneDB) DeleteTombstone(ctx context.Context, id string) db.Error {
}
// Invalidate from cache by ID
t.cache.Invalidate("ID", id)
t.state.Caches.GTS.Tombstone().Invalidate("ID", id)
return nil
}

View file

@ -22,38 +22,19 @@ import (
"context"
"time"
"codeberg.org/gruf/go-cache/v3/result"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
)
type userDB struct {
conn *DBConn
cache *result.Cache[*gtsmodel.User]
}
func (u *userDB) init() {
// Initialize user result cache
u.cache = result.NewSized([]result.Lookup{
{Name: "ID"},
{Name: "AccountID"},
{Name: "Email"},
{Name: "ConfirmationToken"},
{Name: "ExternalID"},
}, func(u1 *gtsmodel.User) *gtsmodel.User {
u2 := new(gtsmodel.User)
*u2 = *u1
return u2
}, 1000)
// Set cache TTL and start sweep routine
u.cache.SetTTL(time.Minute*5, false)
u.cache.Start(time.Second * 10)
state *state.State
}
func (u *userDB) GetUserByID(ctx context.Context, id string) (*gtsmodel.User, db.Error) {
return u.cache.Load("ID", func() (*gtsmodel.User, error) {
return u.state.Caches.GTS.User().Load("ID", func() (*gtsmodel.User, error) {
var user gtsmodel.User
q := u.conn.
@ -71,7 +52,7 @@ func (u *userDB) GetUserByID(ctx context.Context, id string) (*gtsmodel.User, db
}
func (u *userDB) GetUserByAccountID(ctx context.Context, accountID string) (*gtsmodel.User, db.Error) {
return u.cache.Load("AccountID", func() (*gtsmodel.User, error) {
return u.state.Caches.GTS.User().Load("AccountID", func() (*gtsmodel.User, error) {
var user gtsmodel.User
q := u.conn.
@ -89,7 +70,7 @@ func (u *userDB) GetUserByAccountID(ctx context.Context, accountID string) (*gts
}
func (u *userDB) GetUserByEmailAddress(ctx context.Context, emailAddress string) (*gtsmodel.User, db.Error) {
return u.cache.Load("Email", func() (*gtsmodel.User, error) {
return u.state.Caches.GTS.User().Load("Email", func() (*gtsmodel.User, error) {
var user gtsmodel.User
q := u.conn.
@ -105,9 +86,9 @@ func (u *userDB) GetUserByEmailAddress(ctx context.Context, emailAddress string)
return &user, nil
}, emailAddress)
}
func (u *userDB) GetUserByExternalID(ctx context.Context, id string) (*gtsmodel.User, db.Error) {
return u.cache.Load("ExternalID", func() (*gtsmodel.User, error) {
func (u *userDB) GetUserByExternalID(ctx context.Context, id string) (*gtsmodel.User, db.Error) {
return u.state.Caches.GTS.User().Load("ExternalID", func() (*gtsmodel.User, error) {
var user gtsmodel.User
q := u.conn.
@ -125,7 +106,7 @@ func (u *userDB) GetUserByExternalID(ctx context.Context, id string) (*gtsmodel.
}
func (u *userDB) GetUserByConfirmationToken(ctx context.Context, confirmationToken string) (*gtsmodel.User, db.Error) {
return u.cache.Load("ConfirmationToken", func() (*gtsmodel.User, error) {
return u.state.Caches.GTS.User().Load("ConfirmationToken", func() (*gtsmodel.User, error) {
var user gtsmodel.User
q := u.conn.
@ -143,7 +124,7 @@ func (u *userDB) GetUserByConfirmationToken(ctx context.Context, confirmationTok
}
func (u *userDB) PutUser(ctx context.Context, user *gtsmodel.User) db.Error {
return u.cache.Store(user, func() error {
return u.state.Caches.GTS.User().Store(user, func() error {
_, err := u.conn.
NewInsert().
Model(user).
@ -172,8 +153,8 @@ func (u *userDB) UpdateUser(ctx context.Context, user *gtsmodel.User, columns ..
return u.conn.ProcessError(err)
}
// Invalidate in cache
u.cache.Invalidate("ID", user.ID)
// Invalidate user from cache
u.state.Caches.GTS.User().Invalidate("ID", user.ID)
return nil
}
@ -187,6 +168,6 @@ func (u *userDB) DeleteUserByID(ctx context.Context, userID string) db.Error {
}
// Invalidate user from cache
u.cache.Invalidate("ID", userID)
u.state.Caches.GTS.User().Invalidate("ID", userID)
return nil
}

View file

@ -37,6 +37,8 @@ type Emoji interface {
UpdateEmoji(ctx context.Context, emoji *gtsmodel.Emoji, columns ...string) (*gtsmodel.Emoji, Error)
// DeleteEmojiByID deletes one emoji by its database ID.
DeleteEmojiByID(ctx context.Context, id string) Error
// GetEmojisByIDs gets emojis for the given IDs.
GetEmojisByIDs(ctx context.Context, ids []string) ([]*gtsmodel.Emoji, Error)
// GetUseableEmojis gets all emojis which are useable by accounts on this instance.
GetUseableEmojis(ctx context.Context) ([]*gtsmodel.Emoji, Error)
// GetEmojis gets emojis based on given parameters. Useful for admin actions.
@ -52,6 +54,8 @@ type Emoji interface {
GetEmojiByStaticURL(ctx context.Context, imageStaticURL string) (*gtsmodel.Emoji, Error)
// PutEmojiCategory puts one new emoji category in the database.
PutEmojiCategory(ctx context.Context, emojiCategory *gtsmodel.EmojiCategory) Error
// GetEmojiCategoriesByIDs gets emoji categories for given IDs.
GetEmojiCategoriesByIDs(ctx context.Context, ids []string) ([]*gtsmodel.EmojiCategory, Error)
// GetEmojiCategories gets a slice of the names of all existing emoji categories.
GetEmojiCategories(ctx context.Context) ([]*gtsmodel.EmojiCategory, Error)
// GetEmojiCategory gets one emoji category by its id.