mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-25 21:26:40 +00:00
[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:
parent
dd1a4cd892
commit
e58d2d8122
|
@ -27,17 +27,24 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create creates a new account in the database using the provided flags.
|
// Create creates a new account in the database using the provided flags.
|
||||||
var Create action.GTSAction = func(ctx context.Context) error {
|
var Create action.GTSAction = func(ctx context.Context) error {
|
||||||
dbConn, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbConn
|
||||||
|
|
||||||
username := config.GetAdminAccountUsername()
|
username := config.GetAdminAccountUsername()
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return errors.New("no username set")
|
return errors.New("no username set")
|
||||||
|
@ -88,11 +95,17 @@
|
||||||
|
|
||||||
// Confirm sets a user to Approved, sets Email to the current UnconfirmedEmail value, and sets ConfirmedAt to now.
|
// Confirm sets a user to Approved, sets Email to the current UnconfirmedEmail value, and sets ConfirmedAt to now.
|
||||||
var Confirm action.GTSAction = func(ctx context.Context) error {
|
var Confirm action.GTSAction = func(ctx context.Context) error {
|
||||||
dbConn, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbConn
|
||||||
|
|
||||||
username := config.GetAdminAccountUsername()
|
username := config.GetAdminAccountUsername()
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return errors.New("no username set")
|
return errors.New("no username set")
|
||||||
|
@ -125,11 +138,17 @@
|
||||||
|
|
||||||
// Promote sets a user to admin.
|
// Promote sets a user to admin.
|
||||||
var Promote action.GTSAction = func(ctx context.Context) error {
|
var Promote action.GTSAction = func(ctx context.Context) error {
|
||||||
dbConn, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbConn
|
||||||
|
|
||||||
username := config.GetAdminAccountUsername()
|
username := config.GetAdminAccountUsername()
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return errors.New("no username set")
|
return errors.New("no username set")
|
||||||
|
@ -159,11 +178,17 @@
|
||||||
|
|
||||||
// Demote sets admin on a user to false.
|
// Demote sets admin on a user to false.
|
||||||
var Demote action.GTSAction = func(ctx context.Context) error {
|
var Demote action.GTSAction = func(ctx context.Context) error {
|
||||||
dbConn, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbConn
|
||||||
|
|
||||||
username := config.GetAdminAccountUsername()
|
username := config.GetAdminAccountUsername()
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return errors.New("no username set")
|
return errors.New("no username set")
|
||||||
|
@ -193,11 +218,17 @@
|
||||||
|
|
||||||
// Disable sets Disabled to true on a user.
|
// Disable sets Disabled to true on a user.
|
||||||
var Disable action.GTSAction = func(ctx context.Context) error {
|
var Disable action.GTSAction = func(ctx context.Context) error {
|
||||||
dbConn, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbConn
|
||||||
|
|
||||||
username := config.GetAdminAccountUsername()
|
username := config.GetAdminAccountUsername()
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return errors.New("no username set")
|
return errors.New("no username set")
|
||||||
|
@ -227,11 +258,17 @@
|
||||||
|
|
||||||
// Password sets the password of target account.
|
// Password sets the password of target account.
|
||||||
var Password action.GTSAction = func(ctx context.Context) error {
|
var Password action.GTSAction = func(ctx context.Context) error {
|
||||||
dbConn, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbConn
|
||||||
|
|
||||||
username := config.GetAdminAccountUsername()
|
username := config.GetAdminAccountUsername()
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return errors.New("no username set")
|
return errors.New("no username set")
|
||||||
|
|
|
@ -27,12 +27,16 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
|
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Orphaned prunes orphaned media from storage.
|
// Orphaned prunes orphaned media from storage.
|
||||||
var Orphaned action.GTSAction = func(ctx context.Context) error {
|
var Orphaned action.GTSAction = func(ctx context.Context) error {
|
||||||
dbService, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +58,7 @@
|
||||||
return fmt.Errorf("error pruning: %s", err)
|
return fmt.Errorf("error pruning: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dry {
|
if dry /* dick heyyoooooo */ {
|
||||||
log.Infof("DRY RUN: %d stored items are orphaned and eligible to be pruned", pruned)
|
log.Infof("DRY RUN: %d stored items are orphaned and eligible to be pruned", pruned)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("%d stored items were orphaned and pruned", pruned)
|
log.Infof("%d stored items were orphaned and pruned", pruned)
|
||||||
|
|
|
@ -26,16 +26,22 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/trans"
|
"github.com/superseriousbusiness/gotosocial/internal/trans"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Export exports info from the database into a file
|
// Export exports info from the database into a file
|
||||||
var Export action.GTSAction = func(ctx context.Context) error {
|
var Export action.GTSAction = func(ctx context.Context) error {
|
||||||
dbConn, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
|
||||||
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbConn
|
||||||
|
|
||||||
exporter := trans.NewExporter(dbConn)
|
exporter := trans.NewExporter(dbConn)
|
||||||
|
|
||||||
path := config.GetAdminTransPath()
|
path := config.GetAdminTransPath()
|
||||||
|
|
|
@ -26,16 +26,22 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/trans"
|
"github.com/superseriousbusiness/gotosocial/internal/trans"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Import imports info from a file into the database
|
// Import imports info from a file into the database
|
||||||
var Import action.GTSAction = func(ctx context.Context) error {
|
var Import action.GTSAction = func(ctx context.Context) error {
|
||||||
dbConn, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
|
||||||
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbConn
|
||||||
|
|
||||||
importer := trans.NewImporter(dbConn)
|
importer := trans.NewImporter(dbConn)
|
||||||
|
|
||||||
path := config.GetAdminTransPath()
|
path := config.GetAdminTransPath()
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
|
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
|
@ -73,11 +74,20 @@
|
||||||
|
|
||||||
// Start creates and starts a gotosocial server
|
// Start creates and starts a gotosocial server
|
||||||
var Start action.GTSAction = func(ctx context.Context) error {
|
var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
dbService, err := bundb.NewBunDBService(ctx)
|
var state state.State
|
||||||
|
|
||||||
|
// Initialize caches
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
// Open connection to the database
|
||||||
|
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the state DB connection
|
||||||
|
state.DB = dbService
|
||||||
|
|
||||||
if err := dbService.CreateInstanceAccount(ctx); err != nil {
|
if err := dbService.CreateInstanceAccount(ctx); err != nil {
|
||||||
return fmt.Errorf("error creating instance account: %s", err)
|
return fmt.Errorf("error creating instance account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
44
internal/cache/ap.go
vendored
Normal file
44
internal/cache/ap.go
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
type APCaches interface {
|
||||||
|
// Init will initialize all the ActivityPub caches in this collection.
|
||||||
|
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||||
|
Init()
|
||||||
|
|
||||||
|
// Start will attempt to start all of the ActivityPub caches, or panic.
|
||||||
|
Start()
|
||||||
|
|
||||||
|
// Stop will attempt to stop all of the ActivityPub caches, or panic.
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAP returns a new default implementation of APCaches.
|
||||||
|
func NewAP() APCaches {
|
||||||
|
return &apCaches{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type apCaches struct{}
|
||||||
|
|
||||||
|
func (c *apCaches) Init() {}
|
||||||
|
|
||||||
|
func (c *apCaches) Start() {}
|
||||||
|
|
||||||
|
func (c *apCaches) Stop() {}
|
60
internal/cache/cache.go
vendored
Normal file
60
internal/cache/cache.go
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
type Caches struct {
|
||||||
|
// GTS provides access to the collection of gtsmodel object caches.
|
||||||
|
GTS GTSCaches
|
||||||
|
|
||||||
|
// AP provides access to the collection of ActivityPub object caches.
|
||||||
|
AP APCaches
|
||||||
|
|
||||||
|
// prevent pass-by-value.
|
||||||
|
_ nocopy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init will (re)initialize both the GTS and AP cache collections.
|
||||||
|
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||||
|
func (c *Caches) Init() {
|
||||||
|
if c.GTS == nil {
|
||||||
|
// use default impl
|
||||||
|
c.GTS = NewGTS()
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.AP == nil {
|
||||||
|
// use default impl
|
||||||
|
c.AP = NewAP()
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize caches
|
||||||
|
c.GTS.Init()
|
||||||
|
c.AP.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start will start both the GTS and AP cache collections.
|
||||||
|
func (c *Caches) Start() {
|
||||||
|
c.GTS.Start()
|
||||||
|
c.AP.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop will stop both the GTS and AP cache collections.
|
||||||
|
func (c *Caches) Stop() {
|
||||||
|
c.GTS.Stop()
|
||||||
|
c.AP.Stop()
|
||||||
|
}
|
313
internal/cache/gts.go
vendored
Normal file
313
internal/cache/gts.go
vendored
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"codeberg.org/gruf/go-cache/v3/result"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GTSCaches interface {
|
||||||
|
// Init will initialize all the gtsmodel caches in this collection.
|
||||||
|
// NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe.
|
||||||
|
Init()
|
||||||
|
|
||||||
|
// Start will attempt to start all of the gtsmodel caches, or panic.
|
||||||
|
Start()
|
||||||
|
|
||||||
|
// Stop will attempt to stop all of the gtsmodel caches, or panic.
|
||||||
|
Stop()
|
||||||
|
|
||||||
|
// Account provides access to the gtsmodel Account database cache.
|
||||||
|
Account() *result.Cache[*gtsmodel.Account]
|
||||||
|
|
||||||
|
// Block provides access to the gtsmodel Block (account) database cache.
|
||||||
|
Block() *result.Cache[*gtsmodel.Block]
|
||||||
|
|
||||||
|
// DomainBlock provides access to the gtsmodel DomainBlock database cache.
|
||||||
|
DomainBlock() *result.Cache[*gtsmodel.DomainBlock]
|
||||||
|
|
||||||
|
// Emoji provides access to the gtsmodel Emoji database cache.
|
||||||
|
Emoji() *result.Cache[*gtsmodel.Emoji]
|
||||||
|
|
||||||
|
// EmojiCategory provides access to the gtsmodel EmojiCategory database cache.
|
||||||
|
EmojiCategory() *result.Cache[*gtsmodel.EmojiCategory]
|
||||||
|
|
||||||
|
// Mention provides access to the gtsmodel Mention database cache.
|
||||||
|
Mention() *result.Cache[*gtsmodel.Mention]
|
||||||
|
|
||||||
|
// Notification provides access to the gtsmodel Notification database cache.
|
||||||
|
Notification() *result.Cache[*gtsmodel.Notification]
|
||||||
|
|
||||||
|
// Status provides access to the gtsmodel Status database cache.
|
||||||
|
Status() *result.Cache[*gtsmodel.Status]
|
||||||
|
|
||||||
|
// Tombstone provides access to the gtsmodel Tombstone database cache.
|
||||||
|
Tombstone() *result.Cache[*gtsmodel.Tombstone]
|
||||||
|
|
||||||
|
// User provides access to the gtsmodel User database cache.
|
||||||
|
User() *result.Cache[*gtsmodel.User]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGTS returns a new default implementation of GTSCaches.
|
||||||
|
func NewGTS() GTSCaches {
|
||||||
|
return >sCaches{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type gtsCaches struct {
|
||||||
|
account *result.Cache[*gtsmodel.Account]
|
||||||
|
block *result.Cache[*gtsmodel.Block]
|
||||||
|
domainBlock *result.Cache[*gtsmodel.DomainBlock]
|
||||||
|
emoji *result.Cache[*gtsmodel.Emoji]
|
||||||
|
emojiCategory *result.Cache[*gtsmodel.EmojiCategory]
|
||||||
|
mention *result.Cache[*gtsmodel.Mention]
|
||||||
|
notification *result.Cache[*gtsmodel.Notification]
|
||||||
|
status *result.Cache[*gtsmodel.Status]
|
||||||
|
tombstone *result.Cache[*gtsmodel.Tombstone]
|
||||||
|
user *result.Cache[*gtsmodel.User]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Init() {
|
||||||
|
c.initAccount()
|
||||||
|
c.initBlock()
|
||||||
|
c.initDomainBlock()
|
||||||
|
c.initEmoji()
|
||||||
|
c.initEmojiCategory()
|
||||||
|
c.initMention()
|
||||||
|
c.initNotification()
|
||||||
|
c.initStatus()
|
||||||
|
c.initTombstone()
|
||||||
|
c.initUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Start() {
|
||||||
|
tryUntil("starting gtsmodel.Account cache", 5, func() bool {
|
||||||
|
return c.account.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.Block cache", 5, func() bool {
|
||||||
|
return c.block.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.DomainBlock cache", 5, func() bool {
|
||||||
|
return c.domainBlock.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.Emoji cache", 5, func() bool {
|
||||||
|
return c.emoji.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.EmojiCategory cache", 5, func() bool {
|
||||||
|
return c.emojiCategory.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.Mention cache", 5, func() bool {
|
||||||
|
return c.mention.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.Notification cache", 5, func() bool {
|
||||||
|
return c.notification.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.Status cache", 5, func() bool {
|
||||||
|
return c.status.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.Tombstone cache", 5, func() bool {
|
||||||
|
return c.tombstone.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
tryUntil("starting gtsmodel.User cache", 5, func() bool {
|
||||||
|
return c.user.Start(time.Second * 10)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Stop() {
|
||||||
|
tryUntil("stopping gtsmodel.Account cache", 5, c.account.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.Block cache", 5, c.block.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.DomainBlock cache", 5, c.domainBlock.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.Emoji cache", 5, c.emoji.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.EmojiCategory cache", 5, c.emojiCategory.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.Mention cache", 5, c.mention.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.Notification cache", 5, c.notification.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.Status cache", 5, c.status.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.Tombstone cache", 5, c.tombstone.Stop)
|
||||||
|
tryUntil("stopping gtsmodel.User cache", 5, c.user.Stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Account() *result.Cache[*gtsmodel.Account] {
|
||||||
|
return c.account
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Block() *result.Cache[*gtsmodel.Block] {
|
||||||
|
return c.block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) DomainBlock() *result.Cache[*gtsmodel.DomainBlock] {
|
||||||
|
return c.domainBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Emoji() *result.Cache[*gtsmodel.Emoji] {
|
||||||
|
return c.emoji
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) EmojiCategory() *result.Cache[*gtsmodel.EmojiCategory] {
|
||||||
|
return c.emojiCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Mention() *result.Cache[*gtsmodel.Mention] {
|
||||||
|
return c.mention
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Notification() *result.Cache[*gtsmodel.Notification] {
|
||||||
|
return c.notification
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Status() *result.Cache[*gtsmodel.Status] {
|
||||||
|
return c.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) Tombstone() *result.Cache[*gtsmodel.Tombstone] {
|
||||||
|
return c.tombstone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) User() *result.Cache[*gtsmodel.User] {
|
||||||
|
return c.user
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initAccount() {
|
||||||
|
c.account = 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)
|
||||||
|
c.account.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initBlock() {
|
||||||
|
c.block = 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)
|
||||||
|
c.block.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initDomainBlock() {
|
||||||
|
c.domainBlock = result.NewSized([]result.Lookup{
|
||||||
|
{Name: "Domain"},
|
||||||
|
}, func(d1 *gtsmodel.DomainBlock) *gtsmodel.DomainBlock {
|
||||||
|
d2 := new(gtsmodel.DomainBlock)
|
||||||
|
*d2 = *d1
|
||||||
|
return d2
|
||||||
|
}, 1000)
|
||||||
|
c.domainBlock.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initEmoji() {
|
||||||
|
c.emoji = 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)
|
||||||
|
c.emoji.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initEmojiCategory() {
|
||||||
|
c.emojiCategory = result.NewSized([]result.Lookup{
|
||||||
|
{Name: "ID"},
|
||||||
|
{Name: "Name"},
|
||||||
|
}, func(c1 *gtsmodel.EmojiCategory) *gtsmodel.EmojiCategory {
|
||||||
|
c2 := new(gtsmodel.EmojiCategory)
|
||||||
|
*c2 = *c1
|
||||||
|
return c2
|
||||||
|
}, 1000)
|
||||||
|
c.emojiCategory.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initMention() {
|
||||||
|
c.mention = result.NewSized([]result.Lookup{
|
||||||
|
{Name: "ID"},
|
||||||
|
}, func(m1 *gtsmodel.Mention) *gtsmodel.Mention {
|
||||||
|
m2 := new(gtsmodel.Mention)
|
||||||
|
*m2 = *m1
|
||||||
|
return m2
|
||||||
|
}, 1000)
|
||||||
|
c.mention.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initNotification() {
|
||||||
|
c.notification = result.NewSized([]result.Lookup{
|
||||||
|
{Name: "ID"},
|
||||||
|
}, func(n1 *gtsmodel.Notification) *gtsmodel.Notification {
|
||||||
|
n2 := new(gtsmodel.Notification)
|
||||||
|
*n2 = *n1
|
||||||
|
return n2
|
||||||
|
}, 1000)
|
||||||
|
c.notification.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initStatus() {
|
||||||
|
c.status = 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)
|
||||||
|
c.status.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initTombstone will initialize the gtsmodel.Tombstone cache.
|
||||||
|
func (c *gtsCaches) initTombstone() {
|
||||||
|
c.tombstone = result.NewSized([]result.Lookup{
|
||||||
|
{Name: "ID"},
|
||||||
|
{Name: "URI"},
|
||||||
|
}, func(t1 *gtsmodel.Tombstone) *gtsmodel.Tombstone {
|
||||||
|
t2 := new(gtsmodel.Tombstone)
|
||||||
|
*t2 = *t1
|
||||||
|
return t2
|
||||||
|
}, 100)
|
||||||
|
c.tombstone.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gtsCaches) initUser() {
|
||||||
|
c.user = 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)
|
||||||
|
c.user.SetTTL(time.Minute*5, false)
|
||||||
|
}
|
36
internal/cache/util.go
vendored
Normal file
36
internal/cache/util.go
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import "github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
|
||||||
|
// nocopy when embedded will signal linter to
|
||||||
|
// error on pass-by-value of parent struct.
|
||||||
|
type nocopy struct{}
|
||||||
|
|
||||||
|
func (*nocopy) Lock() {}
|
||||||
|
|
||||||
|
func (*nocopy) Unlock() {}
|
||||||
|
|
||||||
|
// tryUntil will attempt to call 'do' for 'count' attempts, before panicking with 'msg'.
|
||||||
|
func tryUntil(msg string, count int, do func() bool) {
|
||||||
|
for i := 0; i < count && !do(); i++ {
|
||||||
|
}
|
||||||
|
log.Panicf("failed %s after %d tries", msg, count)
|
||||||
|
}
|
|
@ -25,39 +25,18 @@
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"github.com/uptrace/bun/dialect"
|
"github.com/uptrace/bun/dialect"
|
||||||
)
|
)
|
||||||
|
|
||||||
type accountDB struct {
|
type accountDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
cache *result.Cache[*gtsmodel.Account]
|
state *state.State
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) newAccountQ(account *gtsmodel.Account) *bun.SelectQuery {
|
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) {
|
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
|
// 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
|
var account gtsmodel.Account
|
||||||
|
|
||||||
// Not cached! Perform database query
|
// 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 {
|
if len(account.EmojiIDs) > 0 {
|
||||||
// Set the account's related emojis
|
// 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting account emojis: %w", err)
|
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 {
|
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
|
// It is safe to run this database transaction within cache.Store
|
||||||
// as the cache does not attempt a mutex lock until AFTER hook.
|
// 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
|
// Update the account's last-updated
|
||||||
account.UpdatedAt = time.Now()
|
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
|
// It is safe to run this database transaction within cache.Store
|
||||||
// as the cache does not attempt a mutex lock until AFTER hook.
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
a.cache.Invalidate("ID", id)
|
a.state.Caches.GTS.Account().Invalidate("ID", id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +493,7 @@ func (a *accountDB) statusesFromIDs(ctx context.Context, statusIDs []string) ([]
|
||||||
|
|
||||||
for _, id := range statusIDs {
|
for _, id := range statusIDs {
|
||||||
// Fetch from status from database by ID
|
// 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 {
|
if err != nil {
|
||||||
log.Errorf("statusesFromIDs: error getting status %q: %v", id, err)
|
log.Errorf("statusesFromIDs: error getting status %q: %v", id, err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
@ -43,9 +44,8 @@
|
||||||
const rsaKeyBits = 2048
|
const rsaKeyBits = 2048
|
||||||
|
|
||||||
type adminDB struct {
|
type adminDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
accounts *accountDB
|
state *state.State
|
||||||
users *userDB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, db.Error) {
|
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!
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the user!
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the new account!
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"github.com/uptrace/bun/dialect/pgdialect"
|
"github.com/uptrace/bun/dialect/pgdialect"
|
||||||
"github.com/uptrace/bun/dialect/sqlitedialect"
|
"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.
|
// 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.
|
// 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 conn *DBConn
|
||||||
var err error
|
var err error
|
||||||
dbType := strings.ToLower(config.GetDbType())
|
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)
|
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 := ¬ificationDB{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{
|
ps := &DBService{
|
||||||
Account: account,
|
Account: &accountDB{
|
||||||
|
conn: conn,
|
||||||
|
state: state,
|
||||||
|
},
|
||||||
Admin: &adminDB{
|
Admin: &adminDB{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
accounts: account,
|
state: state,
|
||||||
users: user,
|
|
||||||
},
|
},
|
||||||
Basic: &basicDB{
|
Basic: &basicDB{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
},
|
},
|
||||||
Domain: domain,
|
Domain: &domainDB{
|
||||||
Emoji: emoji,
|
conn: conn,
|
||||||
|
state: state,
|
||||||
|
},
|
||||||
|
Emoji: &emojiDB{
|
||||||
|
conn: conn,
|
||||||
|
state: state,
|
||||||
|
},
|
||||||
Instance: &instanceDB{
|
Instance: &instanceDB{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
},
|
},
|
||||||
Media: &mediaDB{
|
Media: &mediaDB{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
},
|
},
|
||||||
Mention: mention,
|
Mention: &mentionDB{
|
||||||
Notification: notif,
|
conn: conn,
|
||||||
Relationship: relationship,
|
state: state,
|
||||||
|
},
|
||||||
|
Notification: ¬ificationDB{
|
||||||
|
conn: conn,
|
||||||
|
state: state,
|
||||||
|
},
|
||||||
|
Relationship: &relationshipDB{
|
||||||
|
conn: conn,
|
||||||
|
state: state,
|
||||||
|
},
|
||||||
Session: &sessionDB{
|
Session: &sessionDB{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
},
|
},
|
||||||
Status: status,
|
Status: &statusDB{
|
||||||
Timeline: timeline,
|
conn: conn,
|
||||||
User: user,
|
state: state,
|
||||||
Tombstone: tombstone,
|
},
|
||||||
conn: conn,
|
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
|
// we can confidently return this useable service now
|
||||||
|
|
|
@ -33,7 +33,7 @@ type BundbNewTestSuite struct {
|
||||||
|
|
||||||
func (suite *BundbNewTestSuite) TestCreateNewDB() {
|
func (suite *BundbNewTestSuite) TestCreateNewDB() {
|
||||||
// create a new db with standard test settings
|
// 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.NoError(err)
|
||||||
suite.NotNil(db)
|
suite.NotNil(db)
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func (suite *BundbNewTestSuite) TestCreateNewSqliteDBNoAddress() {
|
||||||
// create a new db with no address specified
|
// create a new db with no address specified
|
||||||
config.SetDbAddress("")
|
config.SetDbAddress("")
|
||||||
config.SetDbType("sqlite")
|
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.EqualError(err, "'db-address' was not set when attempting to start sqlite")
|
||||||
suite.Nil(db)
|
suite.Nil(db)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,34 +22,18 @@
|
||||||
"context"
|
"context"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"golang.org/x/net/idna"
|
"golang.org/x/net/idna"
|
||||||
)
|
)
|
||||||
|
|
||||||
type domainDB struct {
|
type domainDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
cache *result.Cache[*gtsmodel.DomainBlock]
|
state *state.State
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalizeDomain converts the given domain to lowercase
|
// normalizeDomain converts the given domain to lowercase
|
||||||
|
@ -71,7 +55,7 @@ func (d *domainDB) CreateDomainBlock(ctx context.Context, block *gtsmodel.Domain
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.cache.Store(block, func() error {
|
return d.state.Caches.GTS.DomainBlock().Store(block, func() error {
|
||||||
_, err := d.conn.NewInsert().
|
_, err := d.conn.NewInsert().
|
||||||
Model(block).
|
Model(block).
|
||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
|
@ -87,7 +71,7 @@ func (d *domainDB) GetDomainBlock(ctx context.Context, domain string) (*gtsmodel
|
||||||
return nil, err
|
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*
|
// Check for easy case, domain referencing *us*
|
||||||
if domain == "" || domain == config.GetAccountDomain() {
|
if domain == "" || domain == config.GetAccountDomain() {
|
||||||
return nil, db.ErrNoEntries
|
return nil, db.ErrNoEntries
|
||||||
|
@ -125,7 +109,7 @@ func (d *domainDB) DeleteDomainBlock(ctx context.Context, domain string) db.Erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear domain from cache
|
// Clear domain from cache
|
||||||
d.cache.Invalidate("Domain", domain)
|
d.state.Caches.GTS.DomainBlock().Invalidate(domain)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,50 +23,17 @@
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"github.com/uptrace/bun/dialect"
|
"github.com/uptrace/bun/dialect"
|
||||||
)
|
)
|
||||||
|
|
||||||
type emojiDB struct {
|
type emojiDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
emojiCache *result.Cache[*gtsmodel.Emoji]
|
state *state.State
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emojiDB) newEmojiQ(emoji *gtsmodel.Emoji) *bun.SelectQuery {
|
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 {
|
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)
|
_, err := e.conn.NewInsert().Model(emoji).Exec(ctx)
|
||||||
return e.conn.ProcessError(err)
|
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)
|
return nil, e.conn.ProcessError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.emojiCache.Invalidate("ID", emoji.ID)
|
e.state.Caches.GTS.Emoji().Invalidate("ID", emoji.ID)
|
||||||
return emoji, nil
|
return emoji, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +106,7 @@ func (e *emojiDB) DeleteEmojiByID(ctx context.Context, id string) db.Error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
e.emojiCache.Invalidate("ID", id)
|
e.state.Caches.GTS.Emoji().Invalidate("ID", id)
|
||||||
return nil
|
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) {
|
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 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) {
|
func (e *emojiDB) GetEmojiByID(ctx context.Context, id string) (*gtsmodel.Emoji, db.Error) {
|
||||||
|
@ -338,7 +305,7 @@ func(emoji *gtsmodel.Emoji) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emojiDB) PutEmojiCategory(ctx context.Context, emojiCategory *gtsmodel.EmojiCategory) db.Error {
|
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)
|
_, err := e.conn.NewInsert().Model(emojiCategory).Exec(ctx)
|
||||||
return e.conn.ProcessError(err)
|
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 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) {
|
func (e *emojiDB) GetEmojiCategory(ctx context.Context, id string) (*gtsmodel.EmojiCategory, db.Error) {
|
||||||
|
@ -383,7 +350,7 @@ func(emojiCategory *gtsmodel.EmojiCategory) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emojiDB) getEmoji(ctx context.Context, lookup string, dbQuery func(*gtsmodel.Emoji) error, keyParts ...any) (*gtsmodel.Emoji, db.Error) {
|
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
|
var emoji gtsmodel.Emoji
|
||||||
|
|
||||||
// Not cached! Perform database query
|
// Not cached! Perform database query
|
||||||
|
@ -395,8 +362,7 @@ func (e *emojiDB) getEmoji(ctx context.Context, lookup string, dbQuery func(*gts
|
||||||
}, keyParts...)
|
}, keyParts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emojiDB) emojisFromIDs(ctx context.Context, emojiIDs []string) ([]*gtsmodel.Emoji, db.Error) {
|
func (e *emojiDB) GetEmojisByIDs(ctx context.Context, emojiIDs []string) ([]*gtsmodel.Emoji, db.Error) {
|
||||||
// Catch case of no emojis early
|
|
||||||
if len(emojiIDs) == 0 {
|
if len(emojiIDs) == 0 {
|
||||||
return nil, db.ErrNoEntries
|
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) {
|
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
|
var category gtsmodel.EmojiCategory
|
||||||
|
|
||||||
// Not cached! Perform database query
|
// Not cached! Perform database query
|
||||||
|
@ -429,8 +395,7 @@ func (e *emojiDB) getEmojiCategory(ctx context.Context, lookup string, dbQuery f
|
||||||
}, keyParts...)
|
}, keyParts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emojiDB) emojiCategoriesFromIDs(ctx context.Context, emojiCategoryIDs []string) ([]*gtsmodel.EmojiCategory, db.Error) {
|
func (e *emojiDB) GetEmojiCategoriesByIDs(ctx context.Context, emojiCategoryIDs []string) ([]*gtsmodel.EmojiCategory, db.Error) {
|
||||||
// Catch case of no emoji categories early
|
|
||||||
if len(emojiCategoryIDs) == 0 {
|
if len(emojiCategoryIDs) == 0 {
|
||||||
return nil, db.ErrNoEntries
|
return nil, db.ErrNoEntries
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,33 +20,17 @@
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mentionDB struct {
|
type mentionDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
cache *result.Cache[*gtsmodel.Mention]
|
state *state.State
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mentionDB) newMentionQ(i interface{}) *bun.SelectQuery {
|
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) {
|
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
|
var mention gtsmodel.Mention
|
||||||
|
|
||||||
q := m.newMentionQ(&mention).
|
q := m.newMentionQ(&mention).
|
||||||
|
|
|
@ -20,37 +20,21 @@
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type notificationDB struct {
|
type notificationDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
cache *result.Cache[*gtsmodel.Notification]
|
state *state.State
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notificationDB) GetNotification(ctx context.Context, id string) (*gtsmodel.Notification, db.Error) {
|
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
|
var notif gtsmodel.Notification
|
||||||
|
|
||||||
q := n.conn.NewSelect().
|
q := n.conn.NewSelect().
|
||||||
|
@ -130,6 +114,6 @@ func (n *notificationDB) ClearNotifications(ctx context.Context, accountID strin
|
||||||
return n.conn.ProcessError(err)
|
return n.conn.ProcessError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.cache.Clear()
|
n.state.Caches.GTS.Notification().Clear()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,35 +23,16 @@
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type relationshipDB struct {
|
type relationshipDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
accounts *accountDB
|
state *state.State
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) newFollowQ(follow interface{}) *bun.SelectQuery {
|
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
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the block target account
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
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
|
var block gtsmodel.Block
|
||||||
|
|
||||||
q := r.conn.NewSelect().Model(&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 {
|
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)
|
_, err := r.conn.NewInsert().Model(block).Exec(ctx)
|
||||||
return r.conn.ProcessError(err)
|
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
|
// Drop any old value from cache by this ID
|
||||||
r.blockCache.Invalidate("ID", id)
|
r.state.Caches.GTS.Block().Invalidate("ID", id)
|
||||||
return nil
|
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
|
// Drop any old value from cache by this URI
|
||||||
r.blockCache.Invalidate("URI", uri)
|
r.state.Caches.GTS.Block().Invalidate("URI", uri)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,36 +26,16 @@
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type statusDB struct {
|
type statusDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
cache *result.Cache[*gtsmodel.Status]
|
state *state.State
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery {
|
func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery {
|
||||||
|
@ -111,7 +91,7 @@ func(status *gtsmodel.Status) error {
|
||||||
|
|
||||||
func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*gtsmodel.Status) error, keyParts ...any) (*gtsmodel.Status, db.Error) {
|
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
|
// 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
|
var status gtsmodel.Status
|
||||||
|
|
||||||
// Not cached! Perform database query
|
// 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
|
// 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting status account: %w", err)
|
return nil, fmt.Errorf("error getting status account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if id := status.BoostOfAccountID; id != "" {
|
if id := status.BoostOfAccountID; id != "" {
|
||||||
// Set boost of status' author account
|
// 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting boosted status account: %w", err)
|
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 != "" {
|
if id := status.InReplyToAccountID; id != "" {
|
||||||
// Set in-reply-to status' author account
|
// 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting in reply to status account: %w", err)
|
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 {
|
if len(status.EmojiIDs) > 0 {
|
||||||
// Fetch status emojis
|
// 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting status emojis: %w", err)
|
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 {
|
if len(status.MentionIDs) > 0 {
|
||||||
// Fetch status mentions
|
// 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting status mentions: %w", err)
|
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 {
|
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
|
// It is safe to run this database transaction within cache.Store
|
||||||
// as the cache does not attempt a mutex lock until AFTER hook.
|
// 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
|
// 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
|
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
|
// Drop any old value from cache by this ID
|
||||||
s.cache.Invalidate("ID", id)
|
s.state.Caches.GTS.Status().Invalidate("ID", id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,14 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
type timelineDB struct {
|
type timelineDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
status *statusDB
|
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) {
|
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 {
|
for _, id := range statusIDs {
|
||||||
// Fetch status from db for ID
|
// Fetch status from db for ID
|
||||||
status, err := t.status.GetStatusByID(ctx, id)
|
status, err := t.state.DB.GetStatusByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("GetHomeTimeline: error fetching status %q: %v", id, err)
|
log.Errorf("GetHomeTimeline: error fetching status %q: %v", id, err)
|
||||||
continue
|
continue
|
||||||
|
@ -179,7 +180,7 @@ func (t *timelineDB) GetPublicTimeline(ctx context.Context, maxID string, sinceI
|
||||||
|
|
||||||
for _, id := range statusIDs {
|
for _, id := range statusIDs {
|
||||||
// Fetch status from db for ID
|
// Fetch status from db for ID
|
||||||
status, err := t.status.GetStatusByID(ctx, id)
|
status, err := t.state.DB.GetStatusByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("GetPublicTimeline: error fetching status %q: %v", id, err)
|
log.Errorf("GetPublicTimeline: error fetching status %q: %v", id, err)
|
||||||
continue
|
continue
|
||||||
|
@ -239,7 +240,7 @@ func (t *timelineDB) GetFavedTimeline(ctx context.Context, accountID string, max
|
||||||
|
|
||||||
for _, fave := range faves {
|
for _, fave := range faves {
|
||||||
// Fetch status from db for corresponding favourite
|
// 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 {
|
if err != nil {
|
||||||
log.Errorf("GetFavedTimeline: error fetching status for fave %q: %v", fave.ID, err)
|
log.Errorf("GetFavedTimeline: error fetching status for fave %q: %v", fave.ID, err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -20,38 +20,20 @@
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type tombstoneDB struct {
|
type tombstoneDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
cache *result.Cache[*gtsmodel.Tombstone]
|
state *state.State
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tombstoneDB) GetTombstoneByURI(ctx context.Context, uri string) (*gtsmodel.Tombstone, db.Error) {
|
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
|
var tomb gtsmodel.Tombstone
|
||||||
|
|
||||||
q := t.conn.
|
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 {
|
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.
|
_, err := t.conn.
|
||||||
NewInsert().
|
NewInsert().
|
||||||
Model(tombstone).
|
Model(tombstone).
|
||||||
|
@ -95,7 +77,7 @@ func (t *tombstoneDB) DeleteTombstone(ctx context.Context, id string) db.Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate from cache by ID
|
// Invalidate from cache by ID
|
||||||
t.cache.Invalidate("ID", id)
|
t.state.Caches.GTS.Tombstone().Invalidate("ID", id)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,38 +22,19 @@
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"codeberg.org/gruf/go-cache/v3/result"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type userDB struct {
|
type userDB struct {
|
||||||
conn *DBConn
|
conn *DBConn
|
||||||
cache *result.Cache[*gtsmodel.User]
|
state *state.State
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *userDB) GetUserByID(ctx context.Context, id string) (*gtsmodel.User, db.Error) {
|
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
|
var user gtsmodel.User
|
||||||
|
|
||||||
q := u.conn.
|
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) {
|
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
|
var user gtsmodel.User
|
||||||
|
|
||||||
q := u.conn.
|
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) {
|
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
|
var user gtsmodel.User
|
||||||
|
|
||||||
q := u.conn.
|
q := u.conn.
|
||||||
|
@ -105,9 +86,9 @@ func (u *userDB) GetUserByEmailAddress(ctx context.Context, emailAddress string)
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}, emailAddress)
|
}, 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
|
var user gtsmodel.User
|
||||||
|
|
||||||
q := u.conn.
|
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) {
|
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
|
var user gtsmodel.User
|
||||||
|
|
||||||
q := u.conn.
|
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 {
|
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.
|
_, err := u.conn.
|
||||||
NewInsert().
|
NewInsert().
|
||||||
Model(user).
|
Model(user).
|
||||||
|
@ -172,8 +153,8 @@ func (u *userDB) UpdateUser(ctx context.Context, user *gtsmodel.User, columns ..
|
||||||
return u.conn.ProcessError(err)
|
return u.conn.ProcessError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate in cache
|
// Invalidate user from cache
|
||||||
u.cache.Invalidate("ID", user.ID)
|
u.state.Caches.GTS.User().Invalidate("ID", user.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +168,6 @@ func (u *userDB) DeleteUserByID(ctx context.Context, userID string) db.Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate user from cache
|
// Invalidate user from cache
|
||||||
u.cache.Invalidate("ID", userID)
|
u.state.Caches.GTS.User().Invalidate("ID", userID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ type Emoji interface {
|
||||||
UpdateEmoji(ctx context.Context, emoji *gtsmodel.Emoji, columns ...string) (*gtsmodel.Emoji, Error)
|
UpdateEmoji(ctx context.Context, emoji *gtsmodel.Emoji, columns ...string) (*gtsmodel.Emoji, Error)
|
||||||
// DeleteEmojiByID deletes one emoji by its database ID.
|
// DeleteEmojiByID deletes one emoji by its database ID.
|
||||||
DeleteEmojiByID(ctx context.Context, id string) Error
|
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 gets all emojis which are useable by accounts on this instance.
|
||||||
GetUseableEmojis(ctx context.Context) ([]*gtsmodel.Emoji, Error)
|
GetUseableEmojis(ctx context.Context) ([]*gtsmodel.Emoji, Error)
|
||||||
// GetEmojis gets emojis based on given parameters. Useful for admin actions.
|
// 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)
|
GetEmojiByStaticURL(ctx context.Context, imageStaticURL string) (*gtsmodel.Emoji, Error)
|
||||||
// PutEmojiCategory puts one new emoji category in the database.
|
// PutEmojiCategory puts one new emoji category in the database.
|
||||||
PutEmojiCategory(ctx context.Context, emojiCategory *gtsmodel.EmojiCategory) Error
|
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 gets a slice of the names of all existing emoji categories.
|
||||||
GetEmojiCategories(ctx context.Context) ([]*gtsmodel.EmojiCategory, Error)
|
GetEmojiCategories(ctx context.Context) ([]*gtsmodel.EmojiCategory, Error)
|
||||||
// GetEmojiCategory gets one emoji category by its id.
|
// GetEmojiCategory gets one emoji category by its id.
|
||||||
|
|
49
internal/state/state.go
Normal file
49
internal/state/state.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State provides a means of dependency injection and sharing of resources
|
||||||
|
// across different subpackages of the GoToSocial codebase. DO NOT assume
|
||||||
|
// that any particular field will be initialized if you are accessing this
|
||||||
|
// during initialization. A pointer to a State{} is often passed during
|
||||||
|
// subpackage initialization, while the returned subpackage type will later
|
||||||
|
// then be set and stored within the State{} itself.
|
||||||
|
type State struct {
|
||||||
|
// Caches provides access to this state's collection of caches.
|
||||||
|
Caches cache.Caches
|
||||||
|
|
||||||
|
// DB provides access to the database.
|
||||||
|
DB db.DB
|
||||||
|
|
||||||
|
// prevent pass-by-value.
|
||||||
|
_ nocopy
|
||||||
|
}
|
||||||
|
|
||||||
|
// nocopy when embedded will signal linter to
|
||||||
|
// error on pass-by-value of parent struct.
|
||||||
|
type nocopy struct{}
|
||||||
|
|
||||||
|
func (*nocopy) Lock() {}
|
||||||
|
|
||||||
|
func (*nocopy) Unlock() {}
|
|
@ -148,7 +148,8 @@ func NewTimeline(
|
||||||
grabFunction GrabFunction,
|
grabFunction GrabFunction,
|
||||||
filterFunction FilterFunction,
|
filterFunction FilterFunction,
|
||||||
prepareFunction PrepareFunction,
|
prepareFunction PrepareFunction,
|
||||||
skipInsertFunction SkipInsertFunction) (Timeline, error) {
|
skipInsertFunction SkipInsertFunction,
|
||||||
|
) (Timeline, error) {
|
||||||
return &timeline{
|
return &timeline{
|
||||||
indexedItems: &indexedItems{
|
indexedItems: &indexedItems{
|
||||||
skipInsert: skipInsertFunction,
|
skipInsert: skipInsertFunction,
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
@ -13,7 +12,6 @@
|
||||||
func (c *converter) FollowRequestToFollow(ctx context.Context, f *gtsmodel.FollowRequest) *gtsmodel.Follow {
|
func (c *converter) FollowRequestToFollow(ctx context.Context, f *gtsmodel.FollowRequest) *gtsmodel.Follow {
|
||||||
showReblogs := *f.ShowReblogs
|
showReblogs := *f.ShowReblogs
|
||||||
notify := *f.Notify
|
notify := *f.Notify
|
||||||
|
|
||||||
return >smodel.Follow{
|
return >smodel.Follow{
|
||||||
ID: f.ID,
|
ID: f.ID,
|
||||||
CreatedAt: f.CreatedAt,
|
CreatedAt: f.CreatedAt,
|
||||||
|
@ -33,8 +31,9 @@ func (c *converter) StatusToBoost(ctx context.Context, s *gtsmodel.Status, boost
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
boostWrapperStatusURI := fmt.Sprintf("%s/%s", accountURIs.StatusesURI, boostWrapperStatusID)
|
|
||||||
boostWrapperStatusURL := fmt.Sprintf("%s/%s", accountURIs.StatusesURL, boostWrapperStatusID)
|
boostWrapperStatusURI := accountURIs.StatusesURI + "/" + boostWrapperStatusID
|
||||||
|
boostWrapperStatusURL := accountURIs.StatusesURL + "/" + boostWrapperStatusID
|
||||||
|
|
||||||
local := true
|
local := true
|
||||||
if boostingAccount.Domain != "" {
|
if boostingAccount.Domain != "" {
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testModels = []interface{}{
|
var testModels = []interface{}{
|
||||||
|
@ -92,10 +93,16 @@ func NewTestDB() db.DB {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
testDB, err := bundb.NewBunDBService(context.Background())
|
var state state.State
|
||||||
|
state.Caches.Init()
|
||||||
|
|
||||||
|
testDB, err := bundb.NewBunDBService(context.Background(), &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.DB = testDB
|
||||||
|
|
||||||
return testDB
|
return testDB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue