| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // 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/>. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | package timeline | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	pruneLengthIndexed  = 400 | 
					
						
							|  |  |  | 	pruneLengthPrepared = 50 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | // Manager abstracts functions for creating multiple timelines, and adding, removing, and fetching entries from those timelines. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // By the time a timelineable hits the manager interface, it should already have been filtered and it should be established that the item indeed | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | // belongs in the given timeline. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // The manager makes a distinction between *indexed* items and *prepared* items. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // Indexed items consist of just that item's ID (in the database) and the time it was created. An indexed item takes up very little memory, so | 
					
						
							|  |  |  | // it's not a huge priority to keep trimming the indexed items list. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // Prepared items consist of the item's database ID, the time it was created, AND the apimodel representation of that item, for quick serialization. | 
					
						
							|  |  |  | // Prepared items of course take up more memory than indexed items, so they should be regularly pruned if they're not being actively served. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | type Manager interface { | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	// IngestOne takes one timelineable and indexes it into the given timeline, and then immediately prepares it for serving. | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// This is useful in cases where we know the item will need to be shown at the top of a user's timeline immediately (eg., a new status is created). | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// It should already be established before calling this function that the item actually belongs in the timeline! | 
					
						
							| 
									
										
										
										
											2021-06-19 11:18:55 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// The returned bool indicates whether the item was actually put in the timeline. This could be false in cases where | 
					
						
							|  |  |  | 	// a status is a boost, but a boost of the original status or the status itself already exists recently in the timeline. | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	IngestOne(ctx context.Context, timelineID string, item Timelineable) (bool, error) | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	// GetTimeline returns limit n amount of prepared entries from the given timeline, in descending chronological order. | 
					
						
							|  |  |  | 	GetTimeline(ctx context.Context, timelineID string, maxID string, sinceID string, minID string, limit int, local bool) ([]Preparable, error) | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// GetIndexedLength returns the amount of items that have been indexed for the given account ID. | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	GetIndexedLength(ctx context.Context, timelineID string) int | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	// GetOldestIndexedID returns the id ID for the oldest item that we have indexed for the given timeline. | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 	// Will be an empty string if nothing is (yet) indexed. | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	GetOldestIndexedID(ctx context.Context, timelineID string) string | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	// Remove removes one item from the given timeline. | 
					
						
							|  |  |  | 	Remove(ctx context.Context, timelineID string, itemID string) (int, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// RemoveTimeline completely removes one timeline. | 
					
						
							|  |  |  | 	RemoveTimeline(ctx context.Context, timelineID string) error | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// WipeItemFromAllTimelines removes one item from the index and prepared items of all timelines | 
					
						
							|  |  |  | 	WipeItemFromAllTimelines(ctx context.Context, itemID string) error | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	// WipeStatusesFromAccountID removes all items by the given accountID from the given timeline. | 
					
						
							|  |  |  | 	WipeItemsFromAccountID(ctx context.Context, timelineID string, accountID string) error | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-11 11:18:44 +02:00
										 |  |  | 	// UnprepareItem unprepares/uncaches the prepared version fo the given itemID from the given timelineID. | 
					
						
							|  |  |  | 	// Use this for cache invalidation when the prepared representation of an item has changed. | 
					
						
							|  |  |  | 	UnprepareItem(ctx context.Context, timelineID string, itemID string) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// UnprepareItemFromAllTimelines unprepares/uncaches the prepared version of the given itemID from all timelines. | 
					
						
							|  |  |  | 	// Use this for cache invalidation when the prepared representation of an item has changed. | 
					
						
							|  |  |  | 	UnprepareItemFromAllTimelines(ctx context.Context, itemID string) error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-04 18:55:30 +02:00
										 |  |  | 	// Prune manually triggers a prune operation for the given timelineID. | 
					
						
							|  |  |  | 	Prune(ctx context.Context, timelineID string, desiredPreparedItemsLength int, desiredIndexedItemsLength int) (int, error) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	// Start starts hourly cleanup jobs for this timeline manager. | 
					
						
							|  |  |  | 	Start() error | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	// Stop stops the timeline manager (currently a stub, doesn't do anything). | 
					
						
							|  |  |  | 	Stop() error | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // NewManager returns a new timeline manager. | 
					
						
							|  |  |  | func NewManager(grabFunction GrabFunction, filterFunction FilterFunction, prepareFunction PrepareFunction, skipInsertFunction SkipInsertFunction) Manager { | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	return &manager{ | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 		timelines:          sync.Map{}, | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 		grabFunction:       grabFunction, | 
					
						
							|  |  |  | 		filterFunction:     filterFunction, | 
					
						
							|  |  |  | 		prepareFunction:    prepareFunction, | 
					
						
							|  |  |  | 		skipInsertFunction: skipInsertFunction, | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type manager struct { | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	timelines          sync.Map | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	grabFunction       GrabFunction | 
					
						
							|  |  |  | 	filterFunction     FilterFunction | 
					
						
							|  |  |  | 	prepareFunction    PrepareFunction | 
					
						
							|  |  |  | 	skipInsertFunction SkipInsertFunction | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | func (m *manager) Start() error { | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 	// Start a background goroutine which iterates | 
					
						
							|  |  |  | 	// through all stored timelines once per hour, | 
					
						
							|  |  |  | 	// and cleans up old entries if that timeline | 
					
						
							|  |  |  | 	// hasn't been accessed in the last hour. | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		for now := range time.NewTicker(1 * time.Hour).C { | 
					
						
							| 
									
										
										
										
											2023-08-09 18:40:32 +02:00
										 |  |  | 			now := now // rescope | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 			// Define the range function inside here, | 
					
						
							|  |  |  | 			// so that we can use the 'now' returned | 
					
						
							|  |  |  | 			// by the ticker, instead of having to call | 
					
						
							|  |  |  | 			// time.Now() multiple times. | 
					
						
							|  |  |  | 			// | 
					
						
							|  |  |  | 			// Unless it panics, this function always | 
					
						
							|  |  |  | 			// returns 'true', to continue the Range | 
					
						
							|  |  |  | 			// call through the sync.Map. | 
					
						
							|  |  |  | 			f := func(_ any, v any) bool { | 
					
						
							|  |  |  | 				timeline, ok := v.(Timeline) | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 				if !ok { | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 					log.Panic(nil, "couldn't parse timeline manager sync map value as Timeline, this should never happen so panic") | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 				if now.Sub(timeline.LastGot()) < 1*time.Hour { | 
					
						
							|  |  |  | 					// Timeline has been fetched in the | 
					
						
							|  |  |  | 					// last hour, move on to the next one. | 
					
						
							|  |  |  | 					return true | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 				if amountPruned := timeline.Prune(pruneLengthPrepared, pruneLengthIndexed); amountPruned > 0 { | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 					log.WithField("accountID", timeline.TimelineID()).Infof("pruned %d indexed and prepared items from timeline", amountPruned) | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return true | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Execute the function for each timeline. | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 			m.timelines.Range(f) | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *manager) Stop() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | func (m *manager) IngestOne(ctx context.Context, timelineID string, item Timelineable) (bool, error) { | 
					
						
							|  |  |  | 	return m.getOrCreateTimeline(ctx, timelineID).IndexAndPrepareOne( | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 		ctx, | 
					
						
							|  |  |  | 		item.GetID(), | 
					
						
							|  |  |  | 		item.GetBoostOfID(), | 
					
						
							|  |  |  | 		item.GetAccountID(), | 
					
						
							|  |  |  | 		item.GetBoostOfAccountID(), | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | func (m *manager) Remove(ctx context.Context, timelineID string, itemID string) (int, error) { | 
					
						
							|  |  |  | 	return m.getOrCreateTimeline(ctx, timelineID).Remove(ctx, itemID) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *manager) RemoveTimeline(ctx context.Context, timelineID string) error { | 
					
						
							|  |  |  | 	m.timelines.Delete(timelineID) | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | func (m *manager) GetTimeline(ctx context.Context, timelineID string, maxID string, sinceID string, minID string, limit int, local bool) ([]Preparable, error) { | 
					
						
							|  |  |  | 	return m.getOrCreateTimeline(ctx, timelineID).Get(ctx, limit, maxID, sinceID, minID, true) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | func (m *manager) GetIndexedLength(ctx context.Context, timelineID string) int { | 
					
						
							|  |  |  | 	return m.getOrCreateTimeline(ctx, timelineID).Len() | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | func (m *manager) GetOldestIndexedID(ctx context.Context, timelineID string) string { | 
					
						
							|  |  |  | 	return m.getOrCreateTimeline(ctx, timelineID).OldestIndexedItemID() | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | func (m *manager) WipeItemFromAllTimelines(ctx context.Context, itemID string) error { | 
					
						
							| 
									
										
										
										
											2023-08-02 17:21:46 +02:00
										 |  |  | 	errs := new(gtserror.MultiError) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	m.timelines.Range(func(_ any, v any) bool { | 
					
						
							|  |  |  | 		if _, err := v.(Timeline).Remove(ctx, itemID); err != nil { | 
					
						
							| 
									
										
										
										
											2023-08-02 17:21:46 +02:00
										 |  |  | 			errs.Append(err) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 		return true // always continue range | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-02 17:21:46 +02:00
										 |  |  | 	if err := errs.Combine(); err != nil { | 
					
						
							|  |  |  | 		return gtserror.Newf("error(s) wiping status %s: %w", itemID, errs.Combine()) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | func (m *manager) WipeItemsFromAccountID(ctx context.Context, timelineID string, accountID string) error { | 
					
						
							|  |  |  | 	_, err := m.getOrCreateTimeline(ctx, timelineID).RemoveAllByOrBoosting(ctx, accountID) | 
					
						
							| 
									
										
										
										
											2021-07-11 16:22:21 +02:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-11 11:18:44 +02:00
										 |  |  | func (m *manager) UnprepareItemFromAllTimelines(ctx context.Context, itemID string) error { | 
					
						
							| 
									
										
										
										
											2023-08-02 17:21:46 +02:00
										 |  |  | 	errs := new(gtserror.MultiError) | 
					
						
							| 
									
										
										
										
											2023-06-11 11:18:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Work through all timelines held by this | 
					
						
							|  |  |  | 	// manager, and call Unprepare for each. | 
					
						
							|  |  |  | 	m.timelines.Range(func(_ any, v any) bool { | 
					
						
							|  |  |  | 		if err := v.(Timeline).Unprepare(ctx, itemID); err != nil { | 
					
						
							| 
									
										
										
										
											2023-08-02 17:21:46 +02:00
										 |  |  | 			errs.Append(err) | 
					
						
							| 
									
										
										
										
											2023-06-11 11:18:44 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return true // always continue range | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-02 17:21:46 +02:00
										 |  |  | 	if err := errs.Combine(); err != nil { | 
					
						
							|  |  |  | 		return gtserror.Newf("error(s) unpreparing status %s: %w", itemID, errs.Combine()) | 
					
						
							| 
									
										
										
										
											2023-06-11 11:18:44 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *manager) UnprepareItem(ctx context.Context, timelineID string, itemID string) error { | 
					
						
							|  |  |  | 	return m.getOrCreateTimeline(ctx, timelineID).Unprepare(ctx, itemID) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-04 18:55:30 +02:00
										 |  |  | func (m *manager) Prune(ctx context.Context, timelineID string, desiredPreparedItemsLength int, desiredIndexedItemsLength int) (int, error) { | 
					
						
							|  |  |  | 	return m.getOrCreateTimeline(ctx, timelineID).Prune(desiredPreparedItemsLength, desiredIndexedItemsLength), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | // getOrCreateTimeline returns a timeline with the given id, | 
					
						
							|  |  |  | // creating a new timeline with that id if necessary. | 
					
						
							|  |  |  | func (m *manager) getOrCreateTimeline(ctx context.Context, timelineID string) Timeline { | 
					
						
							|  |  |  | 	i, ok := m.timelines.Load(timelineID) | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 	if ok { | 
					
						
							|  |  |  | 		// Timeline already existed in sync.Map. | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 		return i.(Timeline) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 	// Timeline did not yet exist in sync.Map. | 
					
						
							|  |  |  | 	// Create + store it. | 
					
						
							| 
									
										
										
										
											2023-05-25 10:37:38 +02:00
										 |  |  | 	timeline := NewTimeline(ctx, timelineID, m.grabFunction, m.filterFunction, m.prepareFunction, m.skipInsertFunction) | 
					
						
							|  |  |  | 	m.timelines.Store(timelineID, timeline) | 
					
						
							| 
									
										
										
										
											2023-04-06 13:43:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return timeline | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } |