mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 01:12:24 -05:00 
			
		
		
		
	remove local / public caches (is out of scope for this work), share more timeline code
This commit is contained in:
		
					parent
					
						
							
								294e1a6bd8
							
						
					
				
			
			
				commit
				
					
						227d6edc3e
					
				
			
		
					 10 changed files with 256 additions and 180 deletions
				
			
		
							
								
								
									
										4
									
								
								internal/cache/cache.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								internal/cache/cache.go
									
										
									
									
										vendored
									
									
								
							|  | @ -98,7 +98,6 @@ func (c *Caches) Init() { | ||||||
| 	c.initListIDs() | 	c.initListIDs() | ||||||
| 	c.initListedIDs() | 	c.initListedIDs() | ||||||
| 	c.initListTimelines() | 	c.initListTimelines() | ||||||
| 	c.initLocalTimeline() |  | ||||||
| 	c.initMarker() | 	c.initMarker() | ||||||
| 	c.initMedia() | 	c.initMedia() | ||||||
| 	c.initMention() | 	c.initMention() | ||||||
|  | @ -107,7 +106,6 @@ func (c *Caches) Init() { | ||||||
| 	c.initPoll() | 	c.initPoll() | ||||||
| 	c.initPollVote() | 	c.initPollVote() | ||||||
| 	c.initPollVoteIDs() | 	c.initPollVoteIDs() | ||||||
| 	c.initPublicTimeline() |  | ||||||
| 	c.initReport() | 	c.initReport() | ||||||
| 	c.initSinBinStatus() | 	c.initSinBinStatus() | ||||||
| 	c.initStatus() | 	c.initStatus() | ||||||
|  | @ -216,8 +214,6 @@ func (c *Caches) Sweep(threshold float64) { | ||||||
| 	c.DB.UserMuteIDs.Trim(threshold) | 	c.DB.UserMuteIDs.Trim(threshold) | ||||||
| 	c.Timelines.Home.Trim(threshold) | 	c.Timelines.Home.Trim(threshold) | ||||||
| 	c.Timelines.List.Trim(threshold) | 	c.Timelines.List.Trim(threshold) | ||||||
| 	c.Timelines.Public.Trim(threshold) |  | ||||||
| 	c.Timelines.Local.Trim(threshold) |  | ||||||
| 	c.Visibility.Trim(threshold) | 	c.Visibility.Trim(threshold) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								internal/cache/db.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								internal/cache/db.go
									
										
									
									
										vendored
									
									
								
							|  | @ -150,6 +150,7 @@ type DBCaches struct { | ||||||
| 		Domains  atomic.Pointer[int] | 		Domains  atomic.Pointer[int] | ||||||
| 		Statuses atomic.Pointer[int] | 		Statuses atomic.Pointer[int] | ||||||
| 		Users    atomic.Pointer[int] | 		Users    atomic.Pointer[int] | ||||||
|  | 		UserIDs  atomic.Pointer[[]string] | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// InteractionRequest provides access to the gtsmodel InteractionRequest database cache. | 	// InteractionRequest provides access to the gtsmodel InteractionRequest database cache. | ||||||
|  |  | ||||||
							
								
								
									
										26
									
								
								internal/cache/timeline.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								internal/cache/timeline.go
									
										
									
									
										vendored
									
									
								
							|  | @ -29,12 +29,6 @@ type TimelineCaches struct { | ||||||
| 
 | 
 | ||||||
| 	// List ... | 	// List ... | ||||||
| 	List timeline.StatusTimelines | 	List timeline.StatusTimelines | ||||||
| 
 |  | ||||||
| 	// Public ... |  | ||||||
| 	Public timeline.StatusTimelines |  | ||||||
| 
 |  | ||||||
| 	// Local ... |  | ||||||
| 	Local timeline.StatusTimelines |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Caches) initHomeTimelines() { | func (c *Caches) initHomeTimelines() { | ||||||
|  | @ -56,23 +50,3 @@ func (c *Caches) initListTimelines() { | ||||||
| 
 | 
 | ||||||
| 	c.Timelines.List.Init(cap) | 	c.Timelines.List.Init(cap) | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func (c *Caches) initPublicTimeline() { |  | ||||||
| 	// Global cache so |  | ||||||
| 	// allow larger. |  | ||||||
| 	cap := 800 |  | ||||||
| 
 |  | ||||||
| 	log.Infof(nil, "cache size = %d", cap) |  | ||||||
| 
 |  | ||||||
| 	c.Timelines.Public.Init(cap) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (c *Caches) initLocalTimeline() { |  | ||||||
| 	// Global cache so |  | ||||||
| 	// allow larger. |  | ||||||
| 	cap := 800 |  | ||||||
| 
 |  | ||||||
| 	log.Infof(nil, "cache size = %d", cap) |  | ||||||
| 
 |  | ||||||
| 	c.Timelines.Local.Init(cap) |  | ||||||
| } |  | ||||||
|  |  | ||||||
							
								
								
									
										140
									
								
								internal/cache/timeline/status.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										140
									
								
								internal/cache/timeline/status.go
									
										
									
									
										vendored
									
									
								
							|  | @ -522,6 +522,146 @@ func (t *StatusTimeline) Load( | ||||||
| 	return apiStatuses, lo, hi, nil | 	return apiStatuses, lo, hi, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // LoadStatusTimeline is a function that may be used to load a timeline | ||||||
|  | // page in a functionally similar way to StatusTimeline{}.Load(), but without | ||||||
|  | // actually having access to a StatusTimeline{}. For example, for timelines that | ||||||
|  | // we want to share code, but without yet implementing a cache for them. Note this | ||||||
|  | // function may be removed in the future when un-needed. | ||||||
|  | func LoadStatusTimeline( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	page *paging.Page, | ||||||
|  | 
 | ||||||
|  | 	// loadPage should load the timeline of given page for cache hydration. | ||||||
|  | 	loadPage func(page *paging.Page) (statuses []*gtsmodel.Status, err error), | ||||||
|  | 
 | ||||||
|  | 	// loadIDs should load status models with given IDs, this is used | ||||||
|  | 	// to load status models of already cached entries in the timeline. | ||||||
|  | 	loadIDs func(ids []string) (statuses []*gtsmodel.Status, err error), | ||||||
|  | 
 | ||||||
|  | 	// filter can be used to perform filtering of returned | ||||||
|  | 	// statuses BEFORE insert into cache. i.e. this will effect | ||||||
|  | 	// what actually gets stored in the timeline cache. | ||||||
|  | 	filter func(each *gtsmodel.Status) (delete bool, err error), | ||||||
|  | 
 | ||||||
|  | 	// prepareAPI should prepare internal status model to frontend API model. | ||||||
|  | 	prepareAPI func(status *gtsmodel.Status) (apiStatus *apimodel.Status, err error), | ||||||
|  | ) ( | ||||||
|  | 	[]*apimodel.Status, | ||||||
|  | 	string, // lo | ||||||
|  | 	string, // hi | ||||||
|  | 	error, | ||||||
|  | ) { | ||||||
|  | 	switch { | ||||||
|  | 	case page == nil: | ||||||
|  | 		panic("nil page") | ||||||
|  | 	case loadPage == nil: | ||||||
|  | 		panic("nil load page func") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Get paging details. | ||||||
|  | 	lo := page.Min.Value | ||||||
|  | 	hi := page.Max.Value | ||||||
|  | 	limit := page.Limit | ||||||
|  | 	order := page.Order() | ||||||
|  | 
 | ||||||
|  | 	// Use a copy of current page so | ||||||
|  | 	// we can repeatedly update it. | ||||||
|  | 	nextPg := new(paging.Page) | ||||||
|  | 	*nextPg = *page | ||||||
|  | 	nextPg.Min.Value = lo | ||||||
|  | 	nextPg.Max.Value = hi | ||||||
|  | 
 | ||||||
|  | 	// We now reset the lo,hi values to | ||||||
|  | 	// represent the lowest and highest | ||||||
|  | 	// index values of loaded statuses. | ||||||
|  | 	lo, hi = "", "" | ||||||
|  | 
 | ||||||
|  | 	// Preallocate a slice of up-to-limit API models. | ||||||
|  | 	apiStatuses := make([]*apimodel.Status, 0, limit) | ||||||
|  | 
 | ||||||
|  | 	// Check whether loaded enough from cache. | ||||||
|  | 	if need := limit - len(apiStatuses); need > 0 { | ||||||
|  | 
 | ||||||
|  | 		// Load a little more than | ||||||
|  | 		// limit to reduce db calls. | ||||||
|  | 		nextPg.Limit += 10 | ||||||
|  | 
 | ||||||
|  | 		// Perform maximum of 10 load | ||||||
|  | 		// attempts fetching statuses. | ||||||
|  | 		for i := 0; i < 10; i++ { | ||||||
|  | 
 | ||||||
|  | 			// Load next timeline statuses. | ||||||
|  | 			statuses, err := loadPage(nextPg) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, "", "", gtserror.Newf("error loading timeline: %w", err) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// No more statuses from | ||||||
|  | 			// load function = at end. | ||||||
|  | 			if len(statuses) == 0 { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if hi == "" { | ||||||
|  | 				// Set hi returned paging | ||||||
|  | 				// value if not already set. | ||||||
|  | 				hi = statuses[0].ID | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Update nextPg cursor parameter for next database query. | ||||||
|  | 			nextPageParams(nextPg, statuses[len(statuses)-1].ID, order) | ||||||
|  | 
 | ||||||
|  | 			// Perform any filtering on newly loaded statuses. | ||||||
|  | 			statuses, err = doStatusFilter(statuses, filter) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, "", "", gtserror.Newf("error filtering statuses: %w", err) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// After filtering no more | ||||||
|  | 			// statuses remain, retry. | ||||||
|  | 			if len(statuses) == 0 { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Convert to our cache type, | ||||||
|  | 			// these will get inserted into | ||||||
|  | 			// the cache in prepare() below. | ||||||
|  | 			metas := toStatusMeta(statuses) | ||||||
|  | 
 | ||||||
|  | 			// Prepare frontend API models for | ||||||
|  | 			// the loaded statuses. For now this | ||||||
|  | 			// also does its own extra filtering. | ||||||
|  | 			apiStatuses = prepareStatuses(ctx, | ||||||
|  | 				metas, | ||||||
|  | 				prepareAPI, | ||||||
|  | 				apiStatuses, | ||||||
|  | 				limit, | ||||||
|  | 			) | ||||||
|  | 
 | ||||||
|  | 			// If we have anything, return | ||||||
|  | 			// here. Even if below limit. | ||||||
|  | 			if len(apiStatuses) > 0 { | ||||||
|  | 
 | ||||||
|  | 				// Set returned lo status paging value. | ||||||
|  | 				lo = apiStatuses[len(apiStatuses)-1].ID | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if order.Ascending() { | ||||||
|  | 		// The caller always expects the statuses | ||||||
|  | 		// to be returned in DESC order, but we | ||||||
|  | 		// build the status slice in paging order. | ||||||
|  | 		// If paging ASC, we need to reverse the | ||||||
|  | 		// returned statuses and paging values. | ||||||
|  | 		slices.Reverse(apiStatuses) | ||||||
|  | 		lo, hi = hi, lo | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return apiStatuses, lo, hi, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // InsertOne allows you to insert a single status into the timeline, with optional prepared API model. | // InsertOne allows you to insert a single status into the timeline, with optional prepared API model. | ||||||
| func (t *StatusTimeline) InsertOne(status *gtsmodel.Status, prepared *apimodel.Status) { | func (t *StatusTimeline) InsertOne(status *gtsmodel.Status, prepared *apimodel.Status) { | ||||||
| 	t.cache.Insert(&StatusMeta{ | 	t.cache.Insert(&StatusMeta{ | ||||||
|  |  | ||||||
|  | @ -57,8 +57,8 @@ func (p *Processor) publicTimelineGet( | ||||||
| 		// account. | 		// account. | ||||||
| 		requester, | 		requester, | ||||||
| 
 | 
 | ||||||
| 		// Keyed-by-account-ID, public timeline cache. | 		// No cache. | ||||||
| 		p.state.Caches.Timelines.Public.MustGet(requester.ID), | 		nil, | ||||||
| 
 | 
 | ||||||
| 		// Current | 		// Current | ||||||
| 		// page. | 		// page. | ||||||
|  | @ -106,8 +106,8 @@ func (p *Processor) localTimelineGet( | ||||||
| 		// account. | 		// account. | ||||||
| 		requester, | 		requester, | ||||||
| 
 | 
 | ||||||
| 		// Keyed-by-account-ID, local timeline cache. | 		// No cache. | ||||||
| 		p.state.Caches.Timelines.Local.MustGet(requester.ID), | 		nil, | ||||||
| 
 | 
 | ||||||
| 		// Current | 		// Current | ||||||
| 		// page. | 		// page. | ||||||
|  |  | ||||||
|  | @ -154,9 +154,6 @@ func (suite *PublicTestSuite) TestPublicTimelineGetHideFiltered() { | ||||||
| 		suite.FailNow("precondition failed: status we would filter isn't present in unfiltered timeline") | 		suite.FailNow("precondition failed: status we would filter isn't present in unfiltered timeline") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Clear the timeline to drop all cached statuses. |  | ||||||
| 	suite.state.Caches.Timelines.Public.ClearAll() |  | ||||||
| 
 |  | ||||||
| 	// Create a filter to hide one status on the timeline. | 	// Create a filter to hide one status on the timeline. | ||||||
| 	if err := suite.db.PutFilter(ctx, filter); err != nil { | 	if err := suite.db.PutFilter(ctx, filter); err != nil { | ||||||
| 		suite.FailNow(err.Error()) | 		suite.FailNow(err.Error()) | ||||||
|  |  | ||||||
|  | @ -20,20 +20,15 @@ package timeline | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"net/http" | ||||||
| 	"slices" |  | ||||||
| 
 | 
 | ||||||
| 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" | 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
| 	statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" | 	statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/filter/usermute" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtscontext" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/paging" | 	"github.com/superseriousbusiness/gotosocial/internal/paging" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/text" | 	"github.com/superseriousbusiness/gotosocial/internal/text" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/util" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // TagTimelineGet gets a pageable timeline for the given | // TagTimelineGet gets a pageable timeline for the given | ||||||
|  | @ -42,49 +37,69 @@ import ( | ||||||
| // to requestingAcct before returning it. | // to requestingAcct before returning it. | ||||||
| func (p *Processor) TagTimelineGet( | func (p *Processor) TagTimelineGet( | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	requestingAcct *gtsmodel.Account, | 	requester *gtsmodel.Account, | ||||||
| 	tagName string, | 	tagName string, | ||||||
| 	maxID string, | 	maxID string, | ||||||
| 	sinceID string, | 	sinceID string, | ||||||
| 	minID string, | 	minID string, | ||||||
| 	limit int, | 	limit int, | ||||||
| ) (*apimodel.PageableResponse, gtserror.WithCode) { | ) (*apimodel.PageableResponse, gtserror.WithCode) { | ||||||
|  | 
 | ||||||
|  | 	// Fetch the requested tag with name. | ||||||
| 	tag, errWithCode := p.getTag(ctx, tagName) | 	tag, errWithCode := p.getTag(ctx, tagName) | ||||||
| 	if errWithCode != nil { | 	if errWithCode != nil { | ||||||
| 		return nil, errWithCode | 		return nil, errWithCode | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Check for a useable returned tag for endpoint. | ||||||
| 	if tag == nil || !*tag.Useable || !*tag.Listable { | 	if tag == nil || !*tag.Useable || !*tag.Listable { | ||||||
|  | 
 | ||||||
| 		// Obey mastodon API by returning 404 for this. | 		// Obey mastodon API by returning 404 for this. | ||||||
| 		err := fmt.Errorf("tag was not found, or not useable/listable on this instance") | 		const text = "tag was not found, or not useable/listable on this instance" | ||||||
| 		return nil, gtserror.NewErrorNotFound(err, err.Error()) | 		return nil, gtserror.NewWithCode(http.StatusNotFound, text) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	page := paging.Page{ | 	// Fetch status timeline for tag. | ||||||
| 		Min:   paging.EitherMinID(minID, sinceID), | 	return p.getStatusTimeline(ctx, | ||||||
| 		Max:   paging.MaxID(maxID), |  | ||||||
| 		Limit: limit, |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	statuses, err := p.state.DB.GetTagTimeline(ctx, tag.ID, &page) | 		// Auth'd | ||||||
| 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 		// account. | ||||||
| 		err = gtserror.Newf("db error getting statuses: %w", err) | 		requester, | ||||||
| 		return nil, gtserror.NewErrorInternalError(err) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	if page.Order().Ascending() { | 		// No cache. | ||||||
| 		// Returned statuses always | 		nil, | ||||||
| 		// need to be in DESC order. |  | ||||||
| 		slices.Reverse(statuses) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return p.packageTagResponse( | 		// Current | ||||||
| 		ctx, | 		// page. | ||||||
| 		requestingAcct, | 		&paging.Page{ | ||||||
| 		statuses, | 			Min:   paging.EitherMinID(minID, sinceID), | ||||||
| 		limit, | 			Max:   paging.MaxID(maxID), | ||||||
| 		// Use API URL for tag. | 			Limit: limit, | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		// Tag timeline name's endpoint. | ||||||
| 		"/api/v1/timelines/tag/"+tagName, | 		"/api/v1/timelines/tag/"+tagName, | ||||||
|  | 
 | ||||||
|  | 		// No page | ||||||
|  | 		// query. | ||||||
|  | 		nil, | ||||||
|  | 
 | ||||||
|  | 		// Status filter context. | ||||||
|  | 		statusfilter.FilterContextPublic, | ||||||
|  | 
 | ||||||
|  | 		// Database load function. | ||||||
|  | 		func(pg *paging.Page) (statuses []*gtsmodel.Status, err error) { | ||||||
|  | 			return p.state.DB.GetTagTimeline(ctx, tag.ID, pg) | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		// Filtering function, | ||||||
|  | 		// i.e. filter before caching. | ||||||
|  | 		func(s *gtsmodel.Status) (bool, error) { | ||||||
|  | 
 | ||||||
|  | 			// Check the visibility of passed status to requesting user. | ||||||
|  | 			ok, err := p.visFilter.StatusHomeTimelineable(ctx, requester, s) | ||||||
|  | 			return !ok, err | ||||||
|  | 		}, | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -106,69 +121,3 @@ func (p *Processor) getTag(ctx context.Context, tagName string) (*gtsmodel.Tag, | ||||||
| 
 | 
 | ||||||
| 	return tag, nil | 	return tag, nil | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func (p *Processor) packageTagResponse( |  | ||||||
| 	ctx context.Context, |  | ||||||
| 	requestingAcct *gtsmodel.Account, |  | ||||||
| 	statuses []*gtsmodel.Status, |  | ||||||
| 	limit int, |  | ||||||
| 	requestPath string, |  | ||||||
| ) (*apimodel.PageableResponse, gtserror.WithCode) { |  | ||||||
| 	count := len(statuses) |  | ||||||
| 	if count == 0 { |  | ||||||
| 		return util.EmptyPageableResponse(), nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var ( |  | ||||||
| 		items = make([]interface{}, 0, count) |  | ||||||
| 
 |  | ||||||
| 		// Set next + prev values before filtering and API |  | ||||||
| 		// converting, so caller can still page properly. |  | ||||||
| 		nextMaxIDValue = statuses[count-1].ID |  | ||||||
| 		prevMinIDValue = statuses[0].ID |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	filters, err := p.state.DB.GetFiltersForAccountID(ctx, requestingAcct.ID) |  | ||||||
| 	if err != nil { |  | ||||||
| 		err = gtserror.Newf("couldn't retrieve filters for account %s: %w", requestingAcct.ID, err) |  | ||||||
| 		return nil, gtserror.NewErrorInternalError(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	mutes, err := p.state.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), requestingAcct.ID, nil) |  | ||||||
| 	if err != nil { |  | ||||||
| 		err = gtserror.Newf("couldn't retrieve mutes for account %s: %w", requestingAcct.ID, err) |  | ||||||
| 		return nil, gtserror.NewErrorInternalError(err) |  | ||||||
| 	} |  | ||||||
| 	compiledMutes := usermute.NewCompiledUserMuteList(mutes) |  | ||||||
| 
 |  | ||||||
| 	for _, s := range statuses { |  | ||||||
| 		timelineable, err := p.visFilter.StatusTagTimelineable(ctx, requestingAcct, s) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Errorf(ctx, "error checking status visibility: %v", err) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if !timelineable { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, requestingAcct, statusfilter.FilterContextPublic, filters, compiledMutes) |  | ||||||
| 		if errors.Is(err, statusfilter.ErrHideStatus) { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Errorf(ctx, "error converting to api status: %v", err) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		items = append(items, apiStatus) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return util.PackagePageableResponse(util.PageableResponseParams{ |  | ||||||
| 		Items:          items, |  | ||||||
| 		Path:           requestPath, |  | ||||||
| 		NextMaxIDValue: nextMaxIDValue, |  | ||||||
| 		PrevMinIDValue: prevMinIDValue, |  | ||||||
| 		Limit:          limit, |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -64,7 +64,7 @@ func New(state *state.State, converter *typeutils.Converter, visFilter *visibili | ||||||
| func (p *Processor) getStatusTimeline( | func (p *Processor) getStatusTimeline( | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	requester *gtsmodel.Account, | 	requester *gtsmodel.Account, | ||||||
| 	timeline *timeline.StatusTimeline, | 	cache *timeline.StatusTimeline, | ||||||
| 	page *paging.Page, | 	page *paging.Page, | ||||||
| 	pagePath string, | 	pagePath string, | ||||||
| 	pageQuery url.Values, | 	pageQuery url.Values, | ||||||
|  | @ -75,13 +75,11 @@ func (p *Processor) getStatusTimeline( | ||||||
| 	*apimodel.PageableResponse, | 	*apimodel.PageableResponse, | ||||||
| 	gtserror.WithCode, | 	gtserror.WithCode, | ||||||
| ) { | ) { | ||||||
| 	var ( | 	var err error | ||||||
| 		filters []*gtsmodel.Filter | 	var filters []*gtsmodel.Filter | ||||||
| 		mutes   *usermute.CompiledUserMuteList | 	var mutes *usermute.CompiledUserMuteList | ||||||
| 	) |  | ||||||
| 
 | 
 | ||||||
| 	if requester != nil { | 	if requester != nil { | ||||||
| 		var err error |  | ||||||
| 
 | 
 | ||||||
| 		// Fetch all filters relevant for requesting account. | 		// Fetch all filters relevant for requesting account. | ||||||
| 		filters, err = p.state.DB.GetFiltersForAccountID(ctx, | 		filters, err = p.state.DB.GetFiltersForAccountID(ctx, | ||||||
|  | @ -110,42 +108,73 @@ func (p *Processor) getStatusTimeline( | ||||||
| 	// input paging cursor. | 	// input paging cursor. | ||||||
| 	id.ValidatePage(page) | 	id.ValidatePage(page) | ||||||
| 
 | 
 | ||||||
| 	// Load status page via timeline cache, also | 	// Returned models and page params. | ||||||
| 	// getting lo, hi values for next, prev pages. | 	var apiStatuses []*apimodel.Status | ||||||
| 	apiStatuses, lo, hi, err := timeline.Load(ctx, | 	var lo, hi string | ||||||
| 
 | 
 | ||||||
| 		// Status page | 	if cache != nil { | ||||||
| 		// to load. | 		// Load status page via timeline cache, also | ||||||
| 		page, | 		// getting lo, hi values for next, prev pages. | ||||||
|  | 		apiStatuses, lo, hi, err = cache.Load(ctx, | ||||||
| 
 | 
 | ||||||
| 		// Caller provided database | 			// Status page | ||||||
| 		// status page loading function. | 			// to load. | ||||||
| 		loadPage, | 			page, | ||||||
| 
 | 
 | ||||||
| 		// Status load function for cached timeline entries. | 			// Caller provided database | ||||||
| 		func(ids []string) ([]*gtsmodel.Status, error) { | 			// status page loading function. | ||||||
| 			return p.state.DB.GetStatusesByIDs(ctx, ids) | 			loadPage, | ||||||
| 		}, |  | ||||||
| 
 | 
 | ||||||
| 		// Filtering function, | 			// Status load function for cached timeline entries. | ||||||
| 		// i.e. filter before caching. | 			func(ids []string) ([]*gtsmodel.Status, error) { | ||||||
| 		filter, | 				return p.state.DB.GetStatusesByIDs(ctx, ids) | ||||||
|  | 			}, | ||||||
|  | 
 | ||||||
|  | 			// Filtering function, | ||||||
|  | 			// i.e. filter before caching. | ||||||
|  | 			filter, | ||||||
|  | 
 | ||||||
|  | 			// Frontend API model preparation function. | ||||||
|  | 			func(status *gtsmodel.Status) (*apimodel.Status, error) { | ||||||
|  | 				apiStatus, err := p.converter.StatusToAPIStatus(ctx, | ||||||
|  | 					status, | ||||||
|  | 					requester, | ||||||
|  | 					filterCtx, | ||||||
|  | 					filters, | ||||||
|  | 					mutes, | ||||||
|  | 				) | ||||||
|  | 				if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				return apiStatus, nil | ||||||
|  | 			}, | ||||||
|  | 		) | ||||||
|  | 	} else { | ||||||
|  | 		// Load status page without a receiving timeline cache. | ||||||
|  | 		// TODO: remove this code path when all support caching. | ||||||
|  | 		apiStatuses, lo, hi, err = timeline.LoadStatusTimeline(ctx, | ||||||
|  | 			page, | ||||||
|  | 			loadPage, | ||||||
|  | 			func(ids []string) ([]*gtsmodel.Status, error) { | ||||||
|  | 				return p.state.DB.GetStatusesByIDs(ctx, ids) | ||||||
|  | 			}, | ||||||
|  | 			filter, | ||||||
|  | 			func(status *gtsmodel.Status) (*apimodel.Status, error) { | ||||||
|  | 				apiStatus, err := p.converter.StatusToAPIStatus(ctx, | ||||||
|  | 					status, | ||||||
|  | 					requester, | ||||||
|  | 					filterCtx, | ||||||
|  | 					filters, | ||||||
|  | 					mutes, | ||||||
|  | 				) | ||||||
|  | 				if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				return apiStatus, nil | ||||||
|  | 			}, | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 		// Frontend API model preparation function. |  | ||||||
| 		func(status *gtsmodel.Status) (*apimodel.Status, error) { |  | ||||||
| 			apiStatus, err := p.converter.StatusToAPIStatus(ctx, |  | ||||||
| 				status, |  | ||||||
| 				requester, |  | ||||||
| 				filterCtx, |  | ||||||
| 				filters, |  | ||||||
| 				mutes, |  | ||||||
| 			) |  | ||||||
| 			if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) { |  | ||||||
| 				return nil, err |  | ||||||
| 			} |  | ||||||
| 			return apiStatus, nil |  | ||||||
| 		}, |  | ||||||
| 	) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		err := gtserror.Newf("error loading timeline: %w", err) | 		err := gtserror.Newf("error loading timeline: %w", err) | ||||||
| 		return nil, gtserror.WrapWithCode(http.StatusInternalServerError, err) | 		return nil, gtserror.WrapWithCode(http.StatusInternalServerError, err) | ||||||
|  |  | ||||||
|  | @ -1041,9 +1041,7 @@ func (p *clientAPI) DeleteAccountOrUser(ctx context.Context, cMsg *messages.From | ||||||
| 	p.surface.removeTimelineEntriesByAccount(account.ID) | 	p.surface.removeTimelineEntriesByAccount(account.ID) | ||||||
| 
 | 
 | ||||||
| 	// Remove any of their cached timelines. | 	// Remove any of their cached timelines. | ||||||
| 	p.state.Caches.Timelines.Public.Delete(account.ID) |  | ||||||
| 	p.state.Caches.Timelines.Home.Delete(account.ID) | 	p.state.Caches.Timelines.Home.Delete(account.ID) | ||||||
| 	p.state.Caches.Timelines.Local.Delete(account.ID) |  | ||||||
| 
 | 
 | ||||||
| 	// Get the IDs of all the lists owned by the given account ID. | 	// Get the IDs of all the lists owned by the given account ID. | ||||||
| 	listIDs, err := p.state.DB.GetListIDsByAccountID(ctx, account.ID) | 	listIDs, err := p.state.DB.GetListIDsByAccountID(ctx, account.ID) | ||||||
|  |  | ||||||
|  | @ -804,8 +804,6 @@ func (s *Surface) timelineStatusUpdateForTagFollowers( | ||||||
| // deleteStatusFromTimelines completely removes the given status from all timelines. | // deleteStatusFromTimelines completely removes the given status from all timelines. | ||||||
| // It will also stream deletion of the status to all open streams. | // It will also stream deletion of the status to all open streams. | ||||||
| func (s *Surface) deleteStatusFromTimelines(ctx context.Context, statusID string) { | func (s *Surface) deleteStatusFromTimelines(ctx context.Context, statusID string) { | ||||||
| 	s.State.Caches.Timelines.Public.RemoveByStatusIDs(statusID) |  | ||||||
| 	s.State.Caches.Timelines.Local.RemoveByStatusIDs(statusID) |  | ||||||
| 	s.State.Caches.Timelines.Home.RemoveByStatusIDs(statusID) | 	s.State.Caches.Timelines.Home.RemoveByStatusIDs(statusID) | ||||||
| 	s.State.Caches.Timelines.List.RemoveByStatusIDs(statusID) | 	s.State.Caches.Timelines.List.RemoveByStatusIDs(statusID) | ||||||
| 	s.Stream.Delete(ctx, statusID) | 	s.Stream.Delete(ctx, statusID) | ||||||
|  | @ -816,17 +814,13 @@ func (s *Surface) deleteStatusFromTimelines(ctx context.Context, statusID string | ||||||
| // stats, boost counts, etc) next time it's fetched by the timeline owner. This goes | // stats, boost counts, etc) next time it's fetched by the timeline owner. This goes | ||||||
| // both for the status itself, and for any boosts of the status. | // both for the status itself, and for any boosts of the status. | ||||||
| func (s *Surface) invalidateStatusFromTimelines(statusID string) { | func (s *Surface) invalidateStatusFromTimelines(statusID string) { | ||||||
| 	s.State.Caches.Timelines.Public.UnprepareByStatusIDs(statusID) |  | ||||||
| 	s.State.Caches.Timelines.Local.UnprepareByStatusIDs(statusID) |  | ||||||
| 	s.State.Caches.Timelines.Home.UnprepareByStatusIDs(statusID) | 	s.State.Caches.Timelines.Home.UnprepareByStatusIDs(statusID) | ||||||
| 	s.State.Caches.Timelines.List.UnprepareByStatusIDs(statusID) | 	s.State.Caches.Timelines.List.UnprepareByStatusIDs(statusID) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // removeTimelineEntriesByAccount removes all cached timeline entries authored by account ID. | // removeTimelineEntriesByAccount removes all cached timeline entries authored by account ID. | ||||||
| func (s *Surface) removeTimelineEntriesByAccount(accountID string) { | func (s *Surface) removeTimelineEntriesByAccount(accountID string) { | ||||||
| 	s.State.Caches.Timelines.Public.RemoveByAccountIDs(accountID) |  | ||||||
| 	s.State.Caches.Timelines.Home.RemoveByAccountIDs(accountID) | 	s.State.Caches.Timelines.Home.RemoveByAccountIDs(accountID) | ||||||
| 	s.State.Caches.Timelines.Local.RemoveByAccountIDs(accountID) |  | ||||||
| 	s.State.Caches.Timelines.List.RemoveByAccountIDs(accountID) | 	s.State.Caches.Timelines.List.RemoveByAccountIDs(accountID) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -835,9 +829,7 @@ func (s *Surface) invalidateTimelinesForAccount(ctx context.Context, accountID s | ||||||
| 
 | 
 | ||||||
| 	// There's a lot of visibility changes to caclculate for any | 	// There's a lot of visibility changes to caclculate for any | ||||||
| 	// relationship change, so just clear all account's timelines. | 	// relationship change, so just clear all account's timelines. | ||||||
| 	s.State.Caches.Timelines.Public.Clear(accountID) |  | ||||||
| 	s.State.Caches.Timelines.Home.Clear(accountID) | 	s.State.Caches.Timelines.Home.Clear(accountID) | ||||||
| 	s.State.Caches.Timelines.Local.Clear(accountID) |  | ||||||
| 
 | 
 | ||||||
| 	// Get the IDs of all the lists owned by the given account ID. | 	// Get the IDs of all the lists owned by the given account ID. | ||||||
| 	listIDs, err := s.State.DB.GetListIDsByAccountID(ctx, accountID) | 	listIDs, err := s.State.DB.GetListIDsByAccountID(ctx, accountID) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue