From dc1cf2c7b0d9af7535f909a6a19e76ea1d59613b Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 12 Mar 2025 22:45:17 +0000 Subject: [PATCH] add local timeline, fix up merge conflicts --- internal/cache/cache.go | 1 + internal/cache/timeline.go | 11 ++++ internal/cache/timeline/status.go | 38 +++++++++-- internal/db/bundb/timeline.go | 103 +----------------------------- 4 files changed, 47 insertions(+), 106 deletions(-) diff --git a/internal/cache/cache.go b/internal/cache/cache.go index a63c35ae9..9f8113c70 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -216,6 +216,7 @@ func (c *Caches) Sweep(threshold float64) { c.Timelines.Home.Trim(threshold) c.Timelines.List.Trim(threshold) c.Timelines.Public.Trim(threshold) + c.Timelines.Local.Trim(threshold) c.Visibility.Trim(threshold) } diff --git a/internal/cache/timeline.go b/internal/cache/timeline.go index 14c91bcb9..3ff56b597 100644 --- a/internal/cache/timeline.go +++ b/internal/cache/timeline.go @@ -32,6 +32,9 @@ type TimelineCaches struct { // Public ... Public timeline.StatusTimeline + + // Local ... + Local timeline.StatusTimeline } func (c *Caches) initHomeTimelines() { @@ -57,3 +60,11 @@ func (c *Caches) initPublicTimeline() { c.Timelines.Public.Init(cap) } + +func (c *Caches) initLocalTimeline() { + cap := 1000 + + log.Infof(nil, "cache size = %d", cap) + + c.Timelines.Local.Init(cap) +} diff --git a/internal/cache/timeline/status.go b/internal/cache/timeline/status.go index fdb801dbe..6bec16a25 100644 --- a/internal/cache/timeline/status.go +++ b/internal/cache/timeline/status.go @@ -30,6 +30,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/paging" + "github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) @@ -247,6 +248,12 @@ type StatusTimeline struct { idx_AccountID *structr.Index //nolint:revive idx_BoostOfID *structr.Index //nolint:revive idx_BoostOfAccountID *structr.Index //nolint:revive + + // ... + last atomic.Pointer[structr.Direction] + + // ... + max int } // Init ... @@ -330,9 +337,15 @@ func (t *StatusTimeline) Load( ord := page.Order() dir := toDirection(ord) - // First we attempt to load status metadata - // entries from the timeline cache, up to lim. - metas := t.cache.Select(min, max, lim, dir) + // First we attempt to load status + // metadata entries from the timeline + // cache, up to given limit. + metas := t.cache.Select( + util.PtrIf(min), + util.PtrIf(max), + util.PtrIf(lim), + dir, + ) // Set the starting lo / hi ID paging // values. We continually update these @@ -584,11 +597,24 @@ func (t *StatusTimeline) UnprepareByAccountIDs(accountIDs ...string) { // Trim ... func (t *StatusTimeline) Trim(threshold float64) { - panic("TODO") + + // ... + dir := structr.Asc + + // ... + max := threshold * float64(t.max) + + // ... + if p := t.last.Load(); p != nil { + dir = !(*p) + } + + // ... + t.cache.Trim(int(max), dir) } -// Clear will remove all cached entries from timeline. -func (t *StatusTimeline) Clear() { t.cache.Clear() } +// Clear will remove all cached entries from underlying timeline. +func (t *StatusTimeline) Clear() { t.cache.Trim(0, structr.Desc) } // prepare will take a slice of cached (or, freshly loaded!) StatusMeta{} // models, and use given functions to return prepared frontend API models. diff --git a/internal/db/bundb/timeline.go b/internal/db/bundb/timeline.go index 05db33d1a..9504e9d0d 100644 --- a/internal/db/bundb/timeline.go +++ b/internal/db/bundb/timeline.go @@ -21,11 +21,13 @@ import ( "context" "errors" "slices" + "time" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/paging" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/uptrace/bun" @@ -126,104 +128,6 @@ func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, page ) } -<<<<<<< HEAD -func (t *timelineDB) GetPublicTimeline( - ctx context.Context, - maxID string, - sinceID string, - minID string, - limit int, - local bool, -) ([]*gtsmodel.Status, error) { - // Ensure reasonable - if limit < 0 { - limit = 0 - } - - if local { - return t.getLocalTimeline( - ctx, - maxID, - sinceID, - minID, - limit, - ) - } - - // Make educated guess for slice size - var ( - statusIDs = make([]string, 0, limit) - frontToBack = true - ) - - q := t.db. - NewSelect(). - TableExpr("? AS ?", bun.Ident("statuses"), bun.Ident("status")). - // Public only. - Where("? = ?", bun.Ident("status.visibility"), gtsmodel.VisibilityPublic). - // Ignore boosts. - Where("? IS NULL", bun.Ident("status.boost_of_id")). - // Only include statuses that aren't pending approval. - Where("? = ?", bun.Ident("status.pending_approval"), false). - // Select only IDs from table - Column("status.id") - - if maxID == "" || maxID >= id.Highest { - const future = 24 * time.Hour - - // don't return statuses more than 24hr in the future - maxID = id.NewULIDFromTime(time.Now().Add(future)) - } - - // return only statuses LOWER (ie., older) than maxID - q = q.Where("? < ?", bun.Ident("status.id"), maxID) - - if sinceID != "" { - // return only statuses HIGHER (ie., newer) than sinceID - q = q.Where("? > ?", bun.Ident("status.id"), sinceID) - } - - if minID != "" { - // return only statuses HIGHER (ie., newer) than minID - q = q.Where("? > ?", bun.Ident("status.id"), minID) - - // page up - frontToBack = false - } - - if limit > 0 { - // limit amount of statuses returned - q = q.Limit(limit) - } - - if frontToBack { - // Page down. - q = q.Order("status.id DESC") - } else { - // Page up. - q = q.Order("status.id ASC") - } - - if err := q.Scan(ctx, &statusIDs); err != nil { - return nil, err - } - - if len(statusIDs) == 0 { - return nil, nil - } - - // If we're paging up, we still want statuses - // to be sorted by ID desc, so reverse ids slice. - // https://zchee.github.io/golang-wiki/SliceTricks/#reversing - if !frontToBack { - for l, r := 0, len(statusIDs)-1; l < r; l, r = l+1, r-1 { - statusIDs[l], statusIDs[r] = statusIDs[r], statusIDs[l] - } - } - - // Return status IDs loaded from cache + db. - return t.state.DB.GetStatusesByIDs(ctx, statusIDs) -======= func (t *timelineDB) GetPublicTimeline(ctx context.Context, page *paging.Page) ([]*gtsmodel.Status, error) { return loadStatusTimelinePage(ctx, t.db, t.state, @@ -239,12 +143,11 @@ func (t *timelineDB) GetPublicTimeline(ctx context.Context, page *paging.Page) ( q = q.Where("? IS NULL", bun.Ident("boost_of_id")) // Only include statuses that aren't pending approval. - q = q.Where("NOT ? = ?", bun.Ident("pending_approval"), true) + q = q.Where("? = ?", bun.Ident("pending_approval"), false) return q, nil }, ) ->>>>>>> 6f0abe7fb (start work rewriting timeline cache type) } func (t *timelineDB) getLocalTimeline(