mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-02 16:52:25 -06:00 
			
		
		
		
	
		
			
	
	
		
			158 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			158 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package cache
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"github.com/ReneKroon/ttlcache"
							 | 
						||
| 
								 | 
							
									"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// AccountCache is a wrapper around ttlcache.Cache to provide URL and URI lookups for gtsmodel.Account
							 | 
						||
| 
								 | 
							
								type AccountCache struct {
							 | 
						||
| 
								 | 
							
									cache *ttlcache.Cache   // map of IDs -> cached accounts
							 | 
						||
| 
								 | 
							
									urls  map[string]string // map of account URLs -> IDs
							 | 
						||
| 
								 | 
							
									uris  map[string]string // map of account URIs -> IDs
							 | 
						||
| 
								 | 
							
									mutex sync.Mutex
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewAccountCache returns a new instantiated AccountCache object
							 | 
						||
| 
								 | 
							
								func NewAccountCache() *AccountCache {
							 | 
						||
| 
								 | 
							
									c := AccountCache{
							 | 
						||
| 
								 | 
							
										cache: ttlcache.NewCache(),
							 | 
						||
| 
								 | 
							
										urls:  make(map[string]string, 100),
							 | 
						||
| 
								 | 
							
										uris:  make(map[string]string, 100),
							 | 
						||
| 
								 | 
							
										mutex: sync.Mutex{},
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Set callback to purge lookup maps on expiration
							 | 
						||
| 
								 | 
							
									c.cache.SetExpirationCallback(func(key string, value interface{}) {
							 | 
						||
| 
								 | 
							
										account := value.(*gtsmodel.Account)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										c.mutex.Lock()
							 | 
						||
| 
								 | 
							
										delete(c.urls, account.URL)
							 | 
						||
| 
								 | 
							
										delete(c.uris, account.URI)
							 | 
						||
| 
								 | 
							
										c.mutex.Unlock()
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return &c
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// GetByID attempts to fetch a account from the cache by its ID, you will receive a copy for thread-safety
							 | 
						||
| 
								 | 
							
								func (c *AccountCache) GetByID(id string) (*gtsmodel.Account, bool) {
							 | 
						||
| 
								 | 
							
									c.mutex.Lock()
							 | 
						||
| 
								 | 
							
									account, ok := c.getByID(id)
							 | 
						||
| 
								 | 
							
									c.mutex.Unlock()
							 | 
						||
| 
								 | 
							
									return account, ok
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// GetByURL attempts to fetch a account from the cache by its URL, you will receive a copy for thread-safety
							 | 
						||
| 
								 | 
							
								func (c *AccountCache) GetByURL(url string) (*gtsmodel.Account, bool) {
							 | 
						||
| 
								 | 
							
									// Perform safe ID lookup
							 | 
						||
| 
								 | 
							
									c.mutex.Lock()
							 | 
						||
| 
								 | 
							
									id, ok := c.urls[url]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Not found, unlock early
							 | 
						||
| 
								 | 
							
									if !ok {
							 | 
						||
| 
								 | 
							
										c.mutex.Unlock()
							 | 
						||
| 
								 | 
							
										return nil, false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Attempt account lookup
							 | 
						||
| 
								 | 
							
									account, ok := c.getByID(id)
							 | 
						||
| 
								 | 
							
									c.mutex.Unlock()
							 | 
						||
| 
								 | 
							
									return account, ok
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// GetByURI attempts to fetch a account from the cache by its URI, you will receive a copy for thread-safety
							 | 
						||
| 
								 | 
							
								func (c *AccountCache) GetByURI(uri string) (*gtsmodel.Account, bool) {
							 | 
						||
| 
								 | 
							
									// Perform safe ID lookup
							 | 
						||
| 
								 | 
							
									c.mutex.Lock()
							 | 
						||
| 
								 | 
							
									id, ok := c.uris[uri]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Not found, unlock early
							 | 
						||
| 
								 | 
							
									if !ok {
							 | 
						||
| 
								 | 
							
										c.mutex.Unlock()
							 | 
						||
| 
								 | 
							
										return nil, false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Attempt account lookup
							 | 
						||
| 
								 | 
							
									account, ok := c.getByID(id)
							 | 
						||
| 
								 | 
							
									c.mutex.Unlock()
							 | 
						||
| 
								 | 
							
									return account, ok
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// getByID performs an unsafe (no mutex locks) lookup of account by ID, returning a copy of account in cache
							 | 
						||
| 
								 | 
							
								func (c *AccountCache) getByID(id string) (*gtsmodel.Account, bool) {
							 | 
						||
| 
								 | 
							
									v, ok := c.cache.Get(id)
							 | 
						||
| 
								 | 
							
									if !ok {
							 | 
						||
| 
								 | 
							
										return nil, false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return copyAccount(v.(*gtsmodel.Account)), true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Put places a account in the cache, ensuring that the object place is a copy for thread-safety
							 | 
						||
| 
								 | 
							
								func (c *AccountCache) Put(account *gtsmodel.Account) {
							 | 
						||
| 
								 | 
							
									if account == nil || account.ID == "" {
							 | 
						||
| 
								 | 
							
										panic("invalid account")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									c.mutex.Lock()
							 | 
						||
| 
								 | 
							
									c.cache.Set(account.ID, copyAccount(account))
							 | 
						||
| 
								 | 
							
									if account.URL != "" {
							 | 
						||
| 
								 | 
							
										c.urls[account.URL] = account.ID
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if account.URI != "" {
							 | 
						||
| 
								 | 
							
										c.uris[account.URI] = account.ID
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									c.mutex.Unlock()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// copyAccount performs a surface-level copy of account, 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 copyAccount(account *gtsmodel.Account) *gtsmodel.Account {
							 | 
						||
| 
								 | 
							
									return >smodel.Account{
							 | 
						||
| 
								 | 
							
										ID:                      account.ID,
							 | 
						||
| 
								 | 
							
										Username:                account.Username,
							 | 
						||
| 
								 | 
							
										Domain:                  account.Domain,
							 | 
						||
| 
								 | 
							
										AvatarMediaAttachmentID: account.AvatarMediaAttachmentID,
							 | 
						||
| 
								 | 
							
										AvatarMediaAttachment:   nil,
							 | 
						||
| 
								 | 
							
										AvatarRemoteURL:         account.AvatarRemoteURL,
							 | 
						||
| 
								 | 
							
										HeaderMediaAttachmentID: account.HeaderMediaAttachmentID,
							 | 
						||
| 
								 | 
							
										HeaderMediaAttachment:   nil,
							 | 
						||
| 
								 | 
							
										HeaderRemoteURL:         account.HeaderRemoteURL,
							 | 
						||
| 
								 | 
							
										DisplayName:             account.DisplayName,
							 | 
						||
| 
								 | 
							
										Fields:                  account.Fields,
							 | 
						||
| 
								 | 
							
										Note:                    account.Note,
							 | 
						||
| 
								 | 
							
										Memorial:                account.Memorial,
							 | 
						||
| 
								 | 
							
										MovedToAccountID:        account.MovedToAccountID,
							 | 
						||
| 
								 | 
							
										CreatedAt:               account.CreatedAt,
							 | 
						||
| 
								 | 
							
										UpdatedAt:               account.UpdatedAt,
							 | 
						||
| 
								 | 
							
										Bot:                     account.Bot,
							 | 
						||
| 
								 | 
							
										Reason:                  account.Reason,
							 | 
						||
| 
								 | 
							
										Locked:                  account.Locked,
							 | 
						||
| 
								 | 
							
										Discoverable:            account.Discoverable,
							 | 
						||
| 
								 | 
							
										Privacy:                 account.Privacy,
							 | 
						||
| 
								 | 
							
										Sensitive:               account.Sensitive,
							 | 
						||
| 
								 | 
							
										Language:                account.Language,
							 | 
						||
| 
								 | 
							
										URI:                     account.URI,
							 | 
						||
| 
								 | 
							
										URL:                     account.URL,
							 | 
						||
| 
								 | 
							
										LastWebfingeredAt:       account.LastWebfingeredAt,
							 | 
						||
| 
								 | 
							
										InboxURI:                account.InboxURI,
							 | 
						||
| 
								 | 
							
										OutboxURI:               account.OutboxURI,
							 | 
						||
| 
								 | 
							
										FollowingURI:            account.FollowingURI,
							 | 
						||
| 
								 | 
							
										FollowersURI:            account.FollowersURI,
							 | 
						||
| 
								 | 
							
										FeaturedCollectionURI:   account.FeaturedCollectionURI,
							 | 
						||
| 
								 | 
							
										ActorType:               account.ActorType,
							 | 
						||
| 
								 | 
							
										AlsoKnownAs:             account.AlsoKnownAs,
							 | 
						||
| 
								 | 
							
										PrivateKey:              account.PrivateKey,
							 | 
						||
| 
								 | 
							
										PublicKey:               account.PublicKey,
							 | 
						||
| 
								 | 
							
										PublicKeyURI:            account.PublicKeyURI,
							 | 
						||
| 
								 | 
							
										SensitizedAt:            account.SensitizedAt,
							 | 
						||
| 
								 | 
							
										SilencedAt:              account.SilencedAt,
							 | 
						||
| 
								 | 
							
										SuspendedAt:             account.SuspendedAt,
							 | 
						||
| 
								 | 
							
										HideCollections:         account.HideCollections,
							 | 
						||
| 
								 | 
							
										SuspensionOrigin:        account.SuspensionOrigin,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |