diff options
author | 2022-10-03 10:46:11 +0200 | |
---|---|---|
committer | 2022-10-03 10:46:11 +0200 | |
commit | 56f53a2a6f85876485e2ae67d48b78b448caed6e (patch) | |
tree | 9bd8d3fcaffd515d3dc90ff22c6cee17e8d0b073 /internal/cache/user.go | |
parent | [feature] Enlarge active/hovered custom emojis in statuses (#877) (diff) | |
download | gotosocial-56f53a2a6f85876485e2ae67d48b78b448caed6e.tar.xz |
[performance] add user cache and database (#879)
* go fmt
* add + use user cache and database
* fix import
* update tests
* remove unused relation
Diffstat (limited to 'internal/cache/user.go')
-rw-r--r-- | internal/cache/user.go | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/internal/cache/user.go b/internal/cache/user.go new file mode 100644 index 000000000..23bf0b7e9 --- /dev/null +++ b/internal/cache/user.go @@ -0,0 +1,141 @@ +/* + 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/v2" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +) + +// UserCache is a cache wrapper to provide lookups for gtsmodel.User +type UserCache struct { + cache cache.LookupCache[string, string, *gtsmodel.User] +} + +// NewUserCache returns a new instantiated UserCache object +func NewUserCache() *UserCache { + c := &UserCache{} + c.cache = cache.NewLookup(cache.LookupCfg[string, string, *gtsmodel.User]{ + RegisterLookups: func(lm *cache.LookupMap[string, string]) { + lm.RegisterLookup("accountid") + lm.RegisterLookup("email") + lm.RegisterLookup("unconfirmedemail") + lm.RegisterLookup("confirmationtoken") + }, + + AddLookups: func(lm *cache.LookupMap[string, string], user *gtsmodel.User) { + lm.Set("accountid", user.AccountID, user.ID) + if email := user.Email; email != "" { + lm.Set("email", email, user.ID) + } + if unconfirmedEmail := user.UnconfirmedEmail; unconfirmedEmail != "" { + lm.Set("unconfirmedemail", unconfirmedEmail, user.ID) + } + if confirmationToken := user.ConfirmationToken; confirmationToken != "" { + lm.Set("confirmationtoken", confirmationToken, user.ID) + } + }, + + DeleteLookups: func(lm *cache.LookupMap[string, string], user *gtsmodel.User) { + lm.Delete("accountid", user.AccountID) + if email := user.Email; email != "" { + lm.Delete("email", email) + } + if unconfirmedEmail := user.UnconfirmedEmail; unconfirmedEmail != "" { + lm.Delete("unconfirmedemail", unconfirmedEmail) + } + if confirmationToken := user.ConfirmationToken; confirmationToken != "" { + lm.Delete("confirmationtoken", confirmationToken) + } + }, + }) + c.cache.SetTTL(time.Minute*5, false) + c.cache.Start(time.Second * 10) + return c +} + +// GetByID attempts to fetch a user from the cache by its ID, you will receive a copy for thread-safety +func (c *UserCache) GetByID(id string) (*gtsmodel.User, bool) { + return c.cache.Get(id) +} + +// GetByAccountID attempts to fetch a user from the cache by its account ID, you will receive a copy for thread-safety +func (c *UserCache) GetByAccountID(accountID string) (*gtsmodel.User, bool) { + return c.cache.GetBy("accountid", accountID) +} + +// GetByEmail attempts to fetch a user from the cache by its email address, you will receive a copy for thread-safety +func (c *UserCache) GetByEmail(email string) (*gtsmodel.User, bool) { + return c.cache.GetBy("email", email) +} + +// GetByUnconfirmedEmail attempts to fetch a user from the cache by its confirmation token, you will receive a copy for thread-safety +func (c *UserCache) GetByConfirmationToken(token string) (*gtsmodel.User, bool) { + return c.cache.GetBy("confirmationtoken", token) +} + +// Put places a user in the cache, ensuring that the object place is a copy for thread-safety +func (c *UserCache) Put(user *gtsmodel.User) { + if user == nil || user.ID == "" { + panic("invalid user") + } + c.cache.Set(user.ID, copyUser(user)) +} + +// Invalidate invalidates one user from the cache using the ID of the user as key. +func (c *UserCache) Invalidate(userID string) { + c.cache.Invalidate(userID) +} + +func copyUser(user *gtsmodel.User) *gtsmodel.User { + return >smodel.User{ + ID: user.ID, + CreatedAt: user.CreatedAt, + UpdatedAt: user.UpdatedAt, + Email: user.Email, + AccountID: user.AccountID, + Account: nil, + EncryptedPassword: user.EncryptedPassword, + SignUpIP: user.SignUpIP, + CurrentSignInAt: user.CurrentSignInAt, + CurrentSignInIP: user.CurrentSignInIP, + LastSignInAt: user.LastSignInAt, + LastSignInIP: user.LastSignInIP, + SignInCount: user.SignInCount, + InviteID: user.InviteID, + ChosenLanguages: user.ChosenLanguages, + FilteredLanguages: user.FilteredLanguages, + Locale: user.Locale, + CreatedByApplicationID: user.CreatedByApplicationID, + CreatedByApplication: nil, + LastEmailedAt: user.LastEmailedAt, + ConfirmationToken: user.ConfirmationToken, + ConfirmationSentAt: user.ConfirmationSentAt, + ConfirmedAt: user.ConfirmedAt, + UnconfirmedEmail: user.UnconfirmedEmail, + Moderator: copyBoolPtr(user.Moderator), + Admin: copyBoolPtr(user.Admin), + Disabled: copyBoolPtr(user.Disabled), + Approved: copyBoolPtr(user.Approved), + ResetPasswordToken: user.ResetPasswordToken, + ResetPasswordSentAt: user.ResetPasswordSentAt, + } +} |