start adding page validation

This commit is contained in:
kim 2025-03-23 14:18:24 +00:00
parent f6072a7c8f
commit f3c05a9145
5 changed files with 80 additions and 84 deletions

View file

@ -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))

View file

@ -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 <http://www.gnu.org/licenses/>.
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
}
}

31
internal/id/page.go Normal file
View file

@ -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
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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
}
}

View file

@ -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,