alternative method of selecting home timeline

This commit is contained in:
tsmethurst 2023-08-22 14:15:09 +02:00
parent 5da8160d31
commit 054f286cae

View file

@ -19,7 +19,6 @@
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"time" "time"
@ -31,6 +30,7 @@
"github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun" "github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
@ -103,38 +103,67 @@ func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxI
q = q.Order("status.id ASC") q = q.Order("status.id ASC")
} }
// As this is the home timeline, it should be // Create a temp table containing just the
// populated by statuses from accounts followed // IDs of accounts that accountID follows.
// by accountID, and posts from accountID itself. var (
// tempTableName = "#temp_follows_" + id.NewULID()
// So, begin by seeing who accountID follows. err error
// It should be a little cheaper to do this in
// a separate query like this, rather than using
// a join, since followIDs are cached in memory.
follows, err := t.state.DB.GetAccountFollows(
gtscontext.SetBarebones(ctx),
accountID,
) )
if err != nil && !errors.Is(err, db.ErrNoEntries) {
return nil, gtserror.Newf("db error getting follows for account %s: %w", accountID, err) switch t.db.Dialect().Name() {
case dialect.PG:
_, err = t.db.NewRaw(
"SELECT ? INTO ? FROM ? AS ? WHERE ? = ?",
bun.Ident("follow.target_account_id"),
bun.Ident(tempTableName),
bun.Ident("follows"), bun.Ident("follow"),
bun.Ident("follow.account_id"), accountID,
).Exec(ctx)
case dialect.SQLite:
_, err = t.db.NewRaw(
"CREATE TABLE ? AS SELECT ? FROM ? AS ? WHERE ? = ?",
bun.Ident(tempTableName),
bun.Ident("follow.target_account_id"),
bun.Ident("follows"), bun.Ident("follow"),
bun.Ident("follow.account_id"), accountID,
).Exec(ctx)
default:
log.Panic(ctx, "db dialect was neither pg nor sqlite")
} }
// Extract just the accountID from each follow. if err != nil {
targetAccountIDs := make([]string, len(follows)+1) return nil, gtserror.Newf("db error creating temp follows table: %w", err)
for i, f := range follows {
targetAccountIDs[i] = f.TargetAccountID
} }
// Clean up temp
// table on exit.
defer func() {
if _, err := t.db.
NewDropTable().
Table(tempTableName).
IfExists().
Exec(ctx); err != nil {
log.Errorf(ctx, "db error dropping temp follows table: %q", err)
}
}()
// Add accountID itself as a pseudo follow so that // Add accountID itself as a pseudo follow so that
// accountID can see its own posts in the timeline. // accountID can see its own posts in the timeline.
targetAccountIDs[len(targetAccountIDs)-1] = accountID if _, err := t.db.
NewRaw(
"INSERT INTO ? VALUES (?)",
bun.Ident(tempTableName), accountID,
).
Exec(ctx); err != nil {
return nil, gtserror.Newf("db error inserting accountID into temp follows table: %w", err)
}
// Select only statuses authored by // Select only statuses authored by
// accounts with IDs in the slice. // accounts with IDs in the slice.
q = q.Where( q = q.Where(
"? IN (?)", "? IN (?)",
bun.Ident("status.account_id"), bun.Ident("status.account_id"),
bun.In(targetAccountIDs), t.db.NewSelect().Table(tempTableName),
) )
if err := q.Scan(ctx, &statusIDs); err != nil { if err := q.Scan(ctx, &statusIDs); err != nil {