mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[performance] cache recently allowed/denied domains to cut down on db calls (#794)
* fetch creation and fetching domain blocks from db Signed-off-by: kim <grufwub@gmail.com> * add separate domainblock cache type, handle removing block from cache on delete Signed-off-by: kim <grufwub@gmail.com> * fix sentinel nil values being passed into cache Signed-off-by: kim <grufwub@gmail.com> Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
106
internal/cache/domain.go
vendored
Normal file
106
internal/cache/domain.go
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// DomainCache is a cache wrapper to provide URL and URI lookups for gtsmodel.Status
|
||||
type DomainBlockCache struct {
|
||||
cache cache.LookupCache[string, string, *gtsmodel.DomainBlock]
|
||||
}
|
||||
|
||||
// NewStatusCache returns a new instantiated statusCache object
|
||||
func NewDomainBlockCache() *DomainBlockCache {
|
||||
c := &DomainBlockCache{}
|
||||
c.cache = cache.NewLookup(cache.LookupCfg[string, string, *gtsmodel.DomainBlock]{
|
||||
RegisterLookups: func(lm *cache.LookupMap[string, string]) {
|
||||
lm.RegisterLookup("id")
|
||||
},
|
||||
|
||||
AddLookups: func(lm *cache.LookupMap[string, string], block *gtsmodel.DomainBlock) {
|
||||
// Block can be equal to nil when sentinel
|
||||
if block != nil && block.ID != "" {
|
||||
lm.Set("id", block.ID, block.Domain)
|
||||
}
|
||||
},
|
||||
|
||||
DeleteLookups: func(lm *cache.LookupMap[string, string], block *gtsmodel.DomainBlock) {
|
||||
// Block can be equal to nil when sentinel
|
||||
if block != nil && block.ID != "" {
|
||||
lm.Delete("id", block.ID)
|
||||
}
|
||||
},
|
||||
})
|
||||
c.cache.SetTTL(time.Minute*5, false)
|
||||
c.cache.Start(time.Second * 10)
|
||||
return c
|
||||
}
|
||||
|
||||
// GetByID attempts to fetch a status from the cache by its ID, you will receive a copy for thread-safety
|
||||
func (c *DomainBlockCache) GetByID(id string) (*gtsmodel.DomainBlock, bool) {
|
||||
return c.cache.GetBy("id", id)
|
||||
}
|
||||
|
||||
// GetByURL attempts to fetch a status from the cache by its URL, you will receive a copy for thread-safety
|
||||
func (c *DomainBlockCache) GetByDomain(domain string) (*gtsmodel.DomainBlock, bool) {
|
||||
return c.cache.Get(domain)
|
||||
}
|
||||
|
||||
// Put places a status in the cache, ensuring that the object place is a copy for thread-safety
|
||||
func (c *DomainBlockCache) Put(domain string, block *gtsmodel.DomainBlock) {
|
||||
if domain == "" {
|
||||
panic("invalid domain")
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
// This is a sentinel value for (no block)
|
||||
c.cache.Set(domain, nil)
|
||||
} else {
|
||||
// This is a valid domain block
|
||||
c.cache.Set(domain, copyDomainBlock(block))
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateByDomain will invalidate a domain block from the cache by domain name.
|
||||
func (c *DomainBlockCache) InvalidateByDomain(domain string) {
|
||||
c.cache.Invalidate(domain)
|
||||
}
|
||||
|
||||
// copyStatus performs a surface-level copy of status, only keeping attached IDs intact, not the objects.
|
||||
// due to all the data being copied being 99% primitive types or strings (which are immutable and passed by ptr)
|
||||
// this should be a relatively cheap process
|
||||
func copyDomainBlock(block *gtsmodel.DomainBlock) *gtsmodel.DomainBlock {
|
||||
return >smodel.DomainBlock{
|
||||
ID: block.ID,
|
||||
CreatedAt: block.CreatedAt,
|
||||
UpdatedAt: block.UpdatedAt,
|
||||
Domain: block.Domain,
|
||||
CreatedByAccountID: block.CreatedByAccountID,
|
||||
CreatedByAccount: nil,
|
||||
PrivateComment: block.PrivateComment,
|
||||
PublicComment: block.PublicComment,
|
||||
Obfuscate: block.Obfuscate,
|
||||
SubscriptionID: block.SubscriptionID,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user