From 4e80d0bf796f2d23b3a97f75ea899ebcf4059d57 Mon Sep 17 00:00:00 2001 From: kim Date: Sun, 23 Mar 2025 14:18:24 +0000 Subject: [PATCH] start adding page validation --- internal/cache/timeline/status.go | 26 ++++++--- internal/cache/timeline/timeline.go | 31 +++++++++-- internal/id/page.go | 31 +++++++++++ internal/processing/timeline/common.go | 71 ------------------------ internal/processing/timeline/timeline.go | 5 ++ 5 files changed, 80 insertions(+), 84 deletions(-) create mode 100644 internal/id/page.go delete mode 100644 internal/processing/timeline/common.go diff --git a/internal/cache/timeline/status.go b/internal/cache/timeline/status.go index 408bc39fd..dd8820bea 100644 --- a/internal/cache/timeline/status.go +++ b/internal/cache/timeline/status.go @@ -159,11 +159,6 @@ func (t *StatusTimelines) Delete(key string) { } } -// InsertInto allows you to bulk insert many statuses into timeline mapped by key. -func (t *StatusTimelines) InsertInto(key string, statuses ...*gtsmodel.Status) { - t.MustGet(key).Insert(statuses...) -} - // RemoveByStatusIDs ... func (t *StatusTimelines) RemoveByStatusIDs(statusIDs ...string) { if p := t.ptr.Load(); p != nil { @@ -200,6 +195,15 @@ func (t *StatusTimelines) UnprepareByAccountIDs(accountIDs ...string) { } } +// Unprepare ... +func (t *StatusTimelines) Unprepare(key string) { + if p := t.ptr.Load(); p != nil { + if tt := (*p)[key]; tt != nil { + tt.UnprepareAll() + } + } +} + // UnprepareAll ... func (t *StatusTimelines) UnprepareAll() { if p := t.ptr.Load(); p != nil { @@ -270,7 +274,7 @@ func (t *StatusTimeline) Init(cap int) { // ID as primary key is inherently an index. // {Fields: "ID"}, {Fields: "AccountID", Multiple: true}, - {Fields: "BoostOfStatusID", Multiple: true}, + {Fields: "BoostOfID", Multiple: true}, {Fields: "BoostOfAccountID", Multiple: true}, }, @@ -370,7 +374,7 @@ func (t *StatusTimeline) Load( // Before we can do any filtering, we need // to load status models for cached entries. - err := loadStatuses(ctx, metas, loadIDs) + err := loadStatuses(metas, loadIDs) if err != nil { return nil, "", "", gtserror.Newf("error loading statuses: %w", err) } @@ -710,12 +714,16 @@ func (t *StatusTimeline) prepare( return apiStatuses, nil } -// loadStatuses ... +// loadStatuses loads statuses using provided callback +// for the statuses in meta slice that aren't loaded. +// the amount very much depends on whether meta objects +// are yet-to-be-cached (i.e. newly loaded, with status), +// or are from the timeline cache (unloaded status). func loadStatuses( - ctx context.Context, metas []*StatusMeta, loadIDs func([]string) ([]*gtsmodel.Status, error), ) error { + // Determine which of our passed status // meta objects still need statuses loading. toLoadIDs := make([]string, len(metas)) diff --git a/internal/cache/timeline/timeline.go b/internal/cache/timeline/timeline.go index a52bb6e2d..2a80d9099 100644 --- a/internal/cache/timeline/timeline.go +++ b/internal/cache/timeline/timeline.go @@ -1,3 +1,20 @@ +// 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 . + package timeline import ( @@ -5,15 +22,21 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/paging" ) +// nextPageParams gets the next set of paging +// parameters to use based on the current set, +// and the next set of lo / hi values. This will +// correctly handle making sure that, depending +// on the paging order, the cursor value gets +// updated while maintaining the boundary value. func nextPageParams( - curLo, curHi string, // current page params - nextLo, nextHi string, // next lo / hi values + curLo, curHi string, + nextLo, nextHi string, order paging.Order, ) (lo string, hi string) { if order.Ascending() { - + return nextLo, curHi } else /* i.e. descending */ { - + return curLo, nextHi } } diff --git a/internal/id/page.go b/internal/id/page.go new file mode 100644 index 000000000..36ce5a065 --- /dev/null +++ b/internal/id/page.go @@ -0,0 +1,31 @@ +package id + +import ( + "github.com/superseriousbusiness/gotosocial/internal/paging" +) + +// ValidatePage ... +func ValidatePage(page *paging.Page) { + if page == nil { + // unpaged + return + } + + switch page.Order() { + // If the page order is ascending, + // ensure that a minimum value is set. + // This will be used as the cursor. + case paging.OrderAscending: + if page.Min.Value == "" { + page.Min.Value = Lowest + } + + // If the page order is descending, + // ensure that a maximum value is set. + // This will be used as the cursor. + case paging.OrderDescending: + if page.Max.Value == "" { + page.Max.Value = Highest + } + } +} diff --git a/internal/processing/timeline/common.go b/internal/processing/timeline/common.go deleted file mode 100644 index 6d29d81d6..000000000 --- a/internal/processing/timeline/common.go +++ /dev/null @@ -1,71 +0,0 @@ -// 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 . - -package timeline - -import ( - "context" - - "github.com/superseriousbusiness/gotosocial/internal/timeline" -) - -// SkipInsert returns a function that satisifes SkipInsertFunction. -func SkipInsert() timeline.SkipInsertFunction { - // Gap to allow between a status or boost of status, - // and reinsertion of a new boost of that status. - // This is useful to avoid a heavily boosted status - // showing up way too often in a user's timeline. - const boostReinsertionDepth = 50 - - return func( - ctx context.Context, - newItemID string, - newItemAccountID string, - newItemBoostOfID string, - newItemBoostOfAccountID string, - nextItemID string, - nextItemAccountID string, - nextItemBoostOfID string, - nextItemBoostOfAccountID string, - depth int, - ) (bool, error) { - if newItemID == nextItemID { - // Don't insert duplicates. - return true, nil - } - - if newItemBoostOfID != "" { - if newItemBoostOfID == nextItemBoostOfID && - depth < boostReinsertionDepth { - // Don't insert boosts of items - // we've seen boosted recently. - return true, nil - } - - if newItemBoostOfID == nextItemID && - depth < boostReinsertionDepth { - // Don't insert boosts of items when - // we've seen the original recently. - return true, nil - } - } - - // Proceed with insertion - // (that's what she said!). - return false, nil - } -} diff --git a/internal/processing/timeline/timeline.go b/internal/processing/timeline/timeline.go index 4f4f0f95e..ce1d33f50 100644 --- a/internal/processing/timeline/timeline.go +++ b/internal/processing/timeline/timeline.go @@ -31,6 +31,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "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/superseriousbusiness/gotosocial/internal/typeutils" @@ -106,6 +107,10 @@ func (p *Processor) getStatusTimeline( mutes = usermute.NewCompiledUserMuteList(allMutes) } + // Ensure we have valid + // input paging data. + id.ValidatePage(page) + // ... apiStatuses, lo, hi, err := timeline.Load(ctx,