[bugfix] repeated posts on timeline endpoints (#4494)
- updates the go-structr library to add support for getting the current HEAD and TAIL primary key values, which allows us to not insert statuses if they're older than the current oldest timeline item - ensures the nextPg parameters get updated after loading from the cache before then performing a database query closes: https://codeberg.org/superseriousbusiness/gotosocial/issues/4491 Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4494 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
@@ -31,7 +31,7 @@ require (
|
||||
codeberg.org/gruf/go-sched v1.2.4
|
||||
codeberg.org/gruf/go-split v1.2.0
|
||||
codeberg.org/gruf/go-storage v0.3.1
|
||||
codeberg.org/gruf/go-structr v0.9.12
|
||||
codeberg.org/gruf/go-structr v0.9.13
|
||||
github.com/DmitriyVTitov/size v1.5.0
|
||||
github.com/KimMachineGun/automemlimit v0.7.4
|
||||
github.com/SherClockHolmes/webpush-go v1.4.0
|
||||
|
||||
@@ -54,8 +54,8 @@ codeberg.org/gruf/go-split v1.2.0 h1:PmzL23nVEVHm8VxjsJmv4m4wGQz2bGgQw52dgSSj65c
|
||||
codeberg.org/gruf/go-split v1.2.0/go.mod h1:0rejWJpqvOoFAd7nwm5tIXYKaAqjtFGOXmTqQV+VO38=
|
||||
codeberg.org/gruf/go-storage v0.3.1 h1:g66UIM/xXnEk9ejT+W0T9s/PODBZhXa/8ajzeY/MELI=
|
||||
codeberg.org/gruf/go-storage v0.3.1/go.mod h1:r43n/zi7YGOCl2iSl7AMI27D1zcWS65Bi2+5xDzypeo=
|
||||
codeberg.org/gruf/go-structr v0.9.12 h1:yMopvexnuKgZme9WgvIhrJaAuAjfper/x38xsVuJOOo=
|
||||
codeberg.org/gruf/go-structr v0.9.12/go.mod h1:sP2ZSjM5X5XKlxuhAbTKuVQm9DWbHsrQRuTl3MUwbHw=
|
||||
codeberg.org/gruf/go-structr v0.9.13 h1:tTAR30OnSKrcvBomlGZdwE2nE+cfWhkX8UhX0GTAbMo=
|
||||
codeberg.org/gruf/go-structr v0.9.13/go.mod h1:sP2ZSjM5X5XKlxuhAbTKuVQm9DWbHsrQRuTl3MUwbHw=
|
||||
codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73 h1:pRaOwIOS1WSZoPCAvE0H1zpv+D4gF37OVppybffqdI8=
|
||||
codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73/go.mod h1:9wkq+dmHjUhB/0ZxDUWAwsWuXwwGyx5N1dDCB9hpWs8=
|
||||
codeberg.org/superseriousbusiness/go-swagger v0.32.3-gts-go1.23-fix h1:k76/Th+bruqU/d+dB0Ru466ctTF2aVjKpisy/471ILE=
|
||||
|
||||
Vendored
+10
@@ -387,6 +387,9 @@ func (t *StatusTimeline) Load(
|
||||
return nil, "", "", gtserror.Newf("error loading statuses: %w", err)
|
||||
}
|
||||
|
||||
// Update nextPg cursor parameter for database query.
|
||||
nextPageParams(nextPg, metas[len(metas)-1].ID, order)
|
||||
|
||||
// Prepare frontend API models for
|
||||
// the cached statuses. For now this
|
||||
// also does its own extra filtering.
|
||||
@@ -536,6 +539,13 @@ func (t *StatusTimeline) InsertOne(status *gtsmodel.Status, prepared *apimodel.S
|
||||
return false
|
||||
}
|
||||
|
||||
// If item is beyond end of the
|
||||
// timeline, don't bother adding.
|
||||
if tailID := t.cache.Tail(); //
|
||||
tailID == nil || status.ID < *tailID {
|
||||
return false
|
||||
}
|
||||
|
||||
if status.BoostOfID != "" {
|
||||
// Check through top $repeatBoostDepth number of items.
|
||||
for i, value := range t.cache.RangeUnsafe(structr.Desc) {
|
||||
|
||||
+33
@@ -7,6 +7,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"codeberg.org/gruf/go-mempool"
|
||||
@@ -83,6 +84,11 @@ type Timeline[StructType any, PK cmp.Ordered] struct {
|
||||
// types by user defined sets of fields.
|
||||
indices []Index
|
||||
|
||||
// atomically updated head
|
||||
// / tail primary key values.
|
||||
headPK unsafe.Pointer
|
||||
tailPK unsafe.Pointer
|
||||
|
||||
// protective mutex, guards:
|
||||
// - Timeline{}.*
|
||||
// - Index{}.data
|
||||
@@ -150,6 +156,16 @@ func (t *Timeline[T, PK]) Index(name string) *Index {
|
||||
panic("unknown index: " + name)
|
||||
}
|
||||
|
||||
// Head returns the current head primary key.
|
||||
func (t *Timeline[T, PK]) Head() *PK {
|
||||
return (*PK)(atomic.LoadPointer(&t.headPK))
|
||||
}
|
||||
|
||||
// Tail returns the current tail primary key.
|
||||
func (t *Timeline[T, PK]) Tail() *PK {
|
||||
return (*PK)(atomic.LoadPointer(&t.tailPK))
|
||||
}
|
||||
|
||||
// Select allows you to retrieve a slice of values, in order, from the timeline.
|
||||
// This slice is defined by the minimum and maximum primary key parameters, up to
|
||||
// a given length in size. The direction in which you select will determine which
|
||||
@@ -570,6 +586,10 @@ func (t *Timeline[T, PK]) Trim(max int, dir Direction) {
|
||||
bottom := t.list.tail
|
||||
if bottom == nil {
|
||||
|
||||
// Zero head + tail primary keys.
|
||||
atomic.StorePointer(&t.headPK, nil)
|
||||
atomic.StorePointer(&t.tailPK, nil)
|
||||
|
||||
// reached
|
||||
// end.
|
||||
break
|
||||
@@ -589,6 +609,10 @@ func (t *Timeline[T, PK]) Trim(max int, dir Direction) {
|
||||
top := t.list.head
|
||||
if top == nil {
|
||||
|
||||
// Zero head + tail primary keys.
|
||||
atomic.StorePointer(&t.headPK, nil)
|
||||
atomic.StorePointer(&t.tailPK, nil)
|
||||
|
||||
// reached
|
||||
// end.
|
||||
break
|
||||
@@ -1034,6 +1058,15 @@ func (t *Timeline[T, PK]) store_one(last *list_elem, value value_with_pk[T, PK])
|
||||
goto indexing
|
||||
|
||||
indexing:
|
||||
// Set new head / tail
|
||||
// primary key values.
|
||||
switch last {
|
||||
case t.list.head:
|
||||
atomic.StorePointer(&t.headPK, value.kptr)
|
||||
case t.list.tail:
|
||||
atomic.StorePointer(&t.tailPK, value.kptr)
|
||||
}
|
||||
|
||||
// Append already-extracted
|
||||
// primary key to 0th index.
|
||||
_ = idx0.add(key, i_item)
|
||||
|
||||
Vendored
+1
-1
@@ -293,7 +293,7 @@ codeberg.org/gruf/go-storage/disk
|
||||
codeberg.org/gruf/go-storage/internal
|
||||
codeberg.org/gruf/go-storage/memory
|
||||
codeberg.org/gruf/go-storage/s3
|
||||
# codeberg.org/gruf/go-structr v0.9.12
|
||||
# codeberg.org/gruf/go-structr v0.9.13
|
||||
## explicit; go 1.24.5
|
||||
codeberg.org/gruf/go-structr
|
||||
# codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73
|
||||
|
||||
Reference in New Issue
Block a user