diff options
author | 2023-07-24 13:14:13 +0100 | |
---|---|---|
committer | 2023-07-24 13:14:13 +0100 | |
commit | 9eff0d46e49b947dc2642207ee49ed657eb6b565 (patch) | |
tree | 62994afff170737d83f1ed911e385504a0ad16cd /internal/media | |
parent | [chore]: Bump github.com/microcosm-cc/bluemonday from 1.0.24 to 1.0.25 (#2021) (diff) | |
download | gotosocial-9eff0d46e49b947dc2642207ee49ed657eb6b565.tar.xz |
[feature/performance] support uncaching remote emoji + scheduled cleanup functions (#1987)
Diffstat (limited to 'internal/media')
-rw-r--r-- | internal/media/manager.go | 59 | ||||
-rw-r--r-- | internal/media/processingemoji.go | 42 |
2 files changed, 55 insertions, 46 deletions
diff --git a/internal/media/manager.go b/internal/media/manager.go index 1d673128a..afe686cb9 100644 --- a/internal/media/manager.go +++ b/internal/media/manager.go @@ -51,12 +51,7 @@ type Manager struct { state *state.State } -// NewManager returns a media manager with the given db and underlying storage. -// -// A worker pool will also be initialized for the manager, to ensure that only -// a limited number of media will be processed in parallel. The numbers of workers -// is determined from the $GOMAXPROCS environment variable (usually no. CPU cores). -// See internal/concurrency.NewWorkerPool() documentation for further information. +// NewManager returns a media manager with given state. func NewManager(state *state.State) *Manager { m := &Manager{state: state} return m @@ -159,7 +154,7 @@ func (m *Manager) PreProcessMedia(ctx context.Context, data DataFunc, accountID return processingMedia, nil } -// PreProcessMediaRecache refetches, reprocesses, and recaches an existing attachment that has been uncached via pruneRemote. +// PreProcessMediaRecache refetches, reprocesses, and recaches an existing attachment that has been uncached via cleaner pruning. // // Note: unlike ProcessMedia, this will NOT queue the media to be asychronously processed. func (m *Manager) PreProcessMediaRecache(ctx context.Context, data DataFunc, attachmentID string) (*ProcessingMedia, error) { @@ -209,17 +204,18 @@ func (m *Manager) ProcessMedia(ctx context.Context, data DataFunc, accountID str // // Note: unlike ProcessEmoji, this will NOT queue the emoji to be asynchronously processed. func (m *Manager) PreProcessEmoji(ctx context.Context, data DataFunc, shortcode string, emojiID string, uri string, ai *AdditionalEmojiInfo, refresh bool) (*ProcessingEmoji, error) { - instanceAccount, err := m.state.DB.GetInstanceAccount(ctx, "") - if err != nil { - return nil, gtserror.Newf("error fetching this instance account from the db: %s", err) - } - var ( newPathID string emoji *gtsmodel.Emoji now = time.Now() ) + // Fetch the local instance account for emoji path generation. + instanceAcc, err := m.state.DB.GetInstanceAccount(ctx, "") + if err != nil { + return nil, gtserror.Newf("error fetching instance account: %w", err) + } + if refresh { // Look for existing emoji by given ID. emoji, err = m.state.DB.GetEmojiByID(ctx, emojiID) @@ -261,8 +257,8 @@ func (m *Manager) PreProcessEmoji(ctx context.Context, data DataFunc, shortcode } // store + serve static image at new path ID - emoji.ImageStaticURL = uris.GenerateURIForAttachment(instanceAccount.ID, string(TypeEmoji), string(SizeStatic), newPathID, mimePng) - emoji.ImageStaticPath = fmt.Sprintf("%s/%s/%s/%s.%s", instanceAccount.ID, TypeEmoji, SizeStatic, newPathID, mimePng) + emoji.ImageStaticURL = uris.GenerateURIForAttachment(instanceAcc.ID, string(TypeEmoji), string(SizeStatic), newPathID, mimePng) + emoji.ImageStaticPath = fmt.Sprintf("%s/%s/%s/%s.%s", instanceAcc.ID, TypeEmoji, SizeStatic, newPathID, mimePng) emoji.Shortcode = shortcode emoji.URI = uri @@ -278,12 +274,12 @@ func (m *Manager) PreProcessEmoji(ctx context.Context, data DataFunc, shortcode Domain: "", // assume our own domain unless told otherwise ImageRemoteURL: "", ImageStaticRemoteURL: "", - ImageURL: "", // we don't know yet - ImageStaticURL: uris.GenerateURIForAttachment(instanceAccount.ID, string(TypeEmoji), string(SizeStatic), emojiID, mimePng), // all static emojis are encoded as png - ImagePath: "", // we don't know yet - ImageStaticPath: fmt.Sprintf("%s/%s/%s/%s.%s", instanceAccount.ID, TypeEmoji, SizeStatic, emojiID, mimePng), // all static emojis are encoded as png - ImageContentType: "", // we don't know yet - ImageStaticContentType: mimeImagePng, // all static emojis are encoded as png + ImageURL: "", // we don't know yet + ImageStaticURL: uris.GenerateURIForAttachment(instanceAcc.ID, string(TypeEmoji), string(SizeStatic), emojiID, mimePng), // all static emojis are encoded as png + ImagePath: "", // we don't know yet + ImageStaticPath: fmt.Sprintf("%s/%s/%s/%s.%s", instanceAcc.ID, TypeEmoji, SizeStatic, emojiID, mimePng), // all static emojis are encoded as png + ImageContentType: "", // we don't know yet + ImageStaticContentType: mimeImagePng, // all static emojis are encoded as png ImageFileSize: 0, ImageStaticFileSize: 0, Disabled: &disabled, @@ -329,9 +325,8 @@ func (m *Manager) PreProcessEmoji(ctx context.Context, data DataFunc, shortcode } processingEmoji := &ProcessingEmoji{ - instAccID: instanceAccount.ID, emoji: emoji, - refresh: refresh, + existing: refresh, newPathID: newPathID, dataFn: data, mgr: m, @@ -340,6 +335,26 @@ func (m *Manager) PreProcessEmoji(ctx context.Context, data DataFunc, shortcode return processingEmoji, nil } +// PreProcessEmojiRecache refetches, reprocesses, and recaches an existing emoji that has been uncached via cleaner pruning. +// +// Note: unlike ProcessEmoji, this will NOT queue the emoji to be asychronously processed. +func (m *Manager) PreProcessEmojiRecache(ctx context.Context, data DataFunc, emojiID string) (*ProcessingEmoji, error) { + // get the existing emoji from the database. + emoji, err := m.state.DB.GetEmojiByID(ctx, emojiID) + if err != nil { + return nil, err + } + + processingEmoji := &ProcessingEmoji{ + emoji: emoji, + dataFn: data, + existing: true, // inidcate recache + mgr: m, + } + + return processingEmoji, nil +} + // ProcessEmoji will call PreProcessEmoji, followed by queuing the emoji to be processing in the emoji worker queue. func (m *Manager) ProcessEmoji(ctx context.Context, data DataFunc, shortcode string, id string, uri string, ai *AdditionalEmojiInfo, refresh bool) (*ProcessingEmoji, error) { // Create a new processing emoji object for this emoji request. diff --git a/internal/media/processingemoji.go b/internal/media/processingemoji.go index d3a1edbf8..1c7e60144 100644 --- a/internal/media/processingemoji.go +++ b/internal/media/processingemoji.go @@ -31,16 +31,16 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" + "github.com/superseriousbusiness/gotosocial/internal/regexes" "github.com/superseriousbusiness/gotosocial/internal/uris" ) // ProcessingEmoji represents an emoji currently processing. It exposes // various functions for retrieving data from the process. type ProcessingEmoji struct { - instAccID string // instance account ID emoji *gtsmodel.Emoji // processing emoji details - refresh bool // whether this is an existing emoji being refreshed - newPathID string // new emoji path ID to use if refreshed + existing bool // indicates whether this is an existing emoji ID being refreshed / recached + newPathID string // new emoji path ID to use when being refreshed dataFn DataFunc // load-data function, returns media stream done bool // done is set when process finishes with non ctx canceled type error proc runners.Processor // proc helps synchronize only a singular running processing instance @@ -121,24 +121,9 @@ func (p *ProcessingEmoji) load(ctx context.Context) (*gtsmodel.Emoji, bool, erro return err } - if p.refresh { - columns := []string{ - "image_remote_url", - "image_static_remote_url", - "image_url", - "image_static_url", - "image_path", - "image_static_path", - "image_content_type", - "image_file_size", - "image_static_file_size", - "image_updated_at", - "shortcode", - "uri", - } - - // Existing emoji we're refreshing, so only need to update. - err = p.mgr.state.DB.UpdateEmoji(ctx, p.emoji, columns...) + if p.existing { + // Existing emoji we're updating, so only update. + err = p.mgr.state.DB.UpdateEmoji(ctx, p.emoji) return err } @@ -217,7 +202,7 @@ func (p *ProcessingEmoji) store(ctx context.Context) error { var pathID string - if p.refresh { + if p.newPathID != "" { // This is a refreshed emoji with a new // path ID that this will be stored under. pathID = p.newPathID @@ -226,10 +211,13 @@ func (p *ProcessingEmoji) store(ctx context.Context) error { pathID = p.emoji.ID } + // Determine instance account ID from already generated image static path. + instanceAccID := regexes.FilePath.FindStringSubmatch(p.emoji.ImageStaticPath)[1] + // Calculate emoji file path. p.emoji.ImagePath = fmt.Sprintf( "%s/%s/%s/%s.%s", - p.instAccID, + instanceAccID, TypeEmoji, SizeOriginal, pathID, @@ -258,12 +246,13 @@ func (p *ProcessingEmoji) store(ctx context.Context) error { if err := p.mgr.state.Storage.Delete(ctx, p.emoji.ImagePath); err != nil { log.Errorf(ctx, "error removing too-large-emoji from storage: %v", err) } + return gtserror.Newf("calculated emoji size %s greater than max allowed %s", size, maxSize) } // Fill in remaining attachment data now it's stored. p.emoji.ImageURL = uris.GenerateURIForAttachment( - p.instAccID, + instanceAccID, string(TypeEmoji), string(SizeOriginal), pathID, @@ -271,6 +260,10 @@ func (p *ProcessingEmoji) store(ctx context.Context) error { ) p.emoji.ImageContentType = info.MIME.Value p.emoji.ImageFileSize = int(sz) + p.emoji.Cached = func() *bool { + ok := true + return &ok + }() return nil } @@ -297,6 +290,7 @@ func (p *ProcessingEmoji) finish(ctx context.Context) error { // This shouldn't already exist, but we do a check as it's worth logging. if have, _ := p.mgr.state.Storage.Has(ctx, p.emoji.ImageStaticPath); have { log.Warnf(ctx, "static emoji already exists at storage path: %s", p.emoji.ImagePath) + // Attempt to remove static existing emoji at storage path (might be broken / out-of-date) if err := p.mgr.state.Storage.Delete(ctx, p.emoji.ImageStaticPath); err != nil { return gtserror.Newf("error removing static emoji from storage: %v", err) |