mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-29 04:22:24 -05:00 
			
		
		
		
	more code comments, don't bother inserting statuses if timeline not preloaded
This commit is contained in:
		
					parent
					
						
							
								00d8a1f8ac
							
						
					
				
			
			
				commit
				
					
						b76398f685
					
				
			
		
					 3 changed files with 63 additions and 35 deletions
				
			
		
							
								
								
									
										36
									
								
								internal/cache/timeline/preload.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								internal/cache/timeline/preload.go
									
										
									
									
										vendored
									
									
								
							|  | @ -33,10 +33,34 @@ import ( | |||
| // - brand-new     = nil (functionally same as 'needs preload') | ||||
| type preloader struct{ p atomic.Pointer[any] } | ||||
| 
 | ||||
| // Check will concurrency-safely check the preload | ||||
| // state, and if needed call the provided function. | ||||
| // if a preload is in progress, it will wait until complete. | ||||
| func (p *preloader) Check(preload func()) { | ||||
| // Check will return the current preload state, | ||||
| // waiting if a preload is currently in progress. | ||||
| func (p *preloader) Check() bool { | ||||
| 	for { | ||||
| 		// Get state ptr. | ||||
| 		ptr := p.p.Load() | ||||
| 
 | ||||
| 		// Check if requires preloading. | ||||
| 		if ptr == nil || *ptr == false { | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// Check for a preload currently in progress. | ||||
| 		if wg, _ := (*ptr).(*sync.WaitGroup); wg != nil { | ||||
| 			wg.Wait() | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Anything else | ||||
| 		// means success. | ||||
| 		return true | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // CheckPreload will safely check the preload state, | ||||
| // and if needed call the provided function. if a | ||||
| // preload is in progress, it will wait until complete. | ||||
| func (p *preloader) CheckPreload(preload func()) { | ||||
| 	for { | ||||
| 		// Get state ptr. | ||||
| 		ptr := p.p.Load() | ||||
|  | @ -95,7 +119,7 @@ func (p *preloader) start(old *any, preload func()) bool { | |||
| 
 | ||||
| // done marks state as preloaded, | ||||
| // i.e. no more preload required. | ||||
| func (p *preloader) done() { | ||||
| func (p *preloader) Done() { | ||||
| 	old := p.p.Swap(new(any)) | ||||
| 	if old == nil { // was brand-new | ||||
| 		return | ||||
|  | @ -109,7 +133,7 @@ func (p *preloader) done() { | |||
| 
 | ||||
| // clear will clear the state, marking a "preload" as required. | ||||
| // i.e. next call to Check() will call provided preload func. | ||||
| func (p *preloader) clear() { | ||||
| func (p *preloader) Clear() { | ||||
| 	b := false | ||||
| 	a := any(b) | ||||
| 	for { | ||||
|  |  | |||
							
								
								
									
										19
									
								
								internal/cache/timeline/status.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								internal/cache/timeline/status.go
									
										
									
									
										vendored
									
									
								
							|  | @ -32,6 +32,12 @@ import ( | |||
| 	"github.com/superseriousbusiness/gotosocial/internal/util/xslices" | ||||
| ) | ||||
| 
 | ||||
| // repeatBoostDepth determines the minimum count | ||||
| // of statuses after which repeat boosts, or boosts | ||||
| // of the original, may appear. This is may not end | ||||
| // up *exact*, as small races between insert and the | ||||
| // repeatBoost calculation may allow 1 or so extra | ||||
| // to sneak in ahead of time. but it mostly works! | ||||
| const repeatBoostDepth = 40 | ||||
| 
 | ||||
| // StatusMeta contains minimum viable metadata | ||||
|  | @ -190,14 +196,14 @@ func (t *StatusTimeline) Preload( | |||
| 	n int, | ||||
| 	err error, | ||||
| ) { | ||||
| 	t.preloader.Check(func() { | ||||
| 	t.preloader.CheckPreload(func() { | ||||
| 		n, err = t.preload(loadPage, filter) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// Mark preloaded. | ||||
| 		t.preloader.done() | ||||
| 		t.preloader.Done() | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | @ -547,6 +553,13 @@ func loadStatusTimeline( | |||
| // InsertOne allows you to insert a single status into the timeline, with optional prepared API model. | ||||
| // The return value indicates whether status should be skipped from streams, e.g. if already boosted recently. | ||||
| func (t *StatusTimeline) InsertOne(status *gtsmodel.Status, prepared *apimodel.Status) (skip bool) { | ||||
| 
 | ||||
| 	// If timeline no preloaded, i.e. | ||||
| 	// no-one using it, don't insert. | ||||
| 	if !t.preloader.Check() { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if status.BoostOfID != "" { | ||||
| 		// Check through top $repeatBoostDepth number of timeline items. | ||||
| 		for i, value := range t.cache.RangeUnsafe(structr.Desc) { | ||||
|  | @ -718,7 +731,7 @@ func (t *StatusTimeline) Trim() { t.cache.Trim(t.cut, structr.Asc) } | |||
| 
 | ||||
| // Clear will mark the entire timeline as requiring preload, | ||||
| // which will trigger a clear and reload of the entire thing. | ||||
| func (t *StatusTimeline) Clear() { t.preloader.clear() } | ||||
| func (t *StatusTimeline) Clear() { t.preloader.Clear() } | ||||
| 
 | ||||
| // prepareStatuses takes a slice of cached (or, freshly loaded!) StatusMeta{} | ||||
| // models, and use given function to return prepared frontend API models. | ||||
|  |  | |||
|  | @ -108,29 +108,9 @@ func (p *Processor) getStatusTimeline( | |||
| 	// input paging cursor. | ||||
| 	id.ValidatePage(page) | ||||
| 
 | ||||
| 	// Returned models and page params. | ||||
| 	var apiStatuses []*apimodel.Status | ||||
| 	var lo, hi string | ||||
| 
 | ||||
| 	// Pre-prepared filter function that just ensures we | ||||
| 	// don't end up serving multiple copies of the same boost. | ||||
| 	prepare := 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 | ||||
| 	} | ||||
| 
 | ||||
| 	// Load status page via timeline cache, also | ||||
| 	// getting lo, hi values for next, prev pages. | ||||
| 	apiStatuses, lo, hi, err = timeline.Load(ctx, | ||||
| 	apiStatuses, lo, hi, err := timeline.Load(ctx, | ||||
| 
 | ||||
| 		// Status page | ||||
| 		// to load. | ||||
|  | @ -145,13 +125,24 @@ func (p *Processor) getStatusTimeline( | |||
| 			return p.state.DB.GetStatusesByIDs(ctx, ids) | ||||
| 		}, | ||||
| 
 | ||||
| 		// Filtering function, | ||||
| 		// i.e. filter before caching. | ||||
| 		// Call provided status | ||||
| 		// filtering function. | ||||
| 		filter, | ||||
| 
 | ||||
| 		// Frontend API model | ||||
| 		// preparation function. | ||||
| 		prepare, | ||||
| 		// 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 { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue