[bugfix] Deref stats async, serve stub collections if handshaking (#2990)

* [bugfix] Deref stats async, allow peek if handshaking

* don't return totalItems when handshaking or hiding collections

* use GetLimit()

* use StubAccountStats
This commit is contained in:
tobi 2024-06-11 11:54:59 +02:00 committed by GitHub
commit 611f9de39b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 412 additions and 261 deletions

View file

@ -140,10 +140,23 @@ type Account interface {
// Update local account settings.
UpdateAccountSettings(ctx context.Context, settings *gtsmodel.AccountSettings, columns ...string) error
// PopulateAccountStats gets (or creates and gets) account stats for
// the given account, and attaches them to the account model.
// PopulateAccountStats either creates account stats for the given
// account by performing COUNT(*) database queries, or retrieves
// existing stats from the database, and attaches stats to account.
//
// If account is local and stats were last regenerated > 48 hours ago,
// stats will always be regenerated using COUNT(*) queries, to prevent drift.
PopulateAccountStats(ctx context.Context, account *gtsmodel.Account) error
// StubAccountStats creates zeroed account stats for the given account,
// skipping COUNT(*) queries, upserts them in the DB, and attaches them
// to the account model.
//
// Useful following fresh dereference of a remote account, or fresh
// creation of a local account, when you know all COUNT(*) queries
// would return 0 anyway.
StubAccountStats(ctx context.Context, account *gtsmodel.Account) error
// RegenerateAccountStats creates, upserts, and returns stats
// for the given account, and attaches them to the account model.
//

View file

@ -1217,6 +1217,35 @@ func (a *accountDB) PopulateAccountStats(ctx context.Context, account *gtsmodel.
return nil
}
func (a *accountDB) StubAccountStats(ctx context.Context, account *gtsmodel.Account) error {
stats := &gtsmodel.AccountStats{
AccountID: account.ID,
RegeneratedAt: time.Now(),
FollowersCount: util.Ptr(0),
FollowingCount: util.Ptr(0),
FollowRequestsCount: util.Ptr(0),
StatusesCount: util.Ptr(0),
StatusesPinnedCount: util.Ptr(0),
}
// Upsert this stats in case a race
// meant someone else inserted it first.
if err := a.state.Caches.GTS.AccountStats.Store(stats, func() error {
if _, err := NewUpsert(a.db).
Model(stats).
Constraint("account_id").
Exec(ctx); err != nil {
return err
}
return nil
}); err != nil {
return err
}
account.Stats = stats
return nil
}
func (a *accountDB) RegenerateAccountStats(ctx context.Context, account *gtsmodel.Account) error {
// Initialize a new stats struct.
stats := &gtsmodel.AccountStats{

View file

@ -120,16 +120,6 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (
return nil, err
}
settings := &gtsmodel.AccountSettings{
AccountID: accountID,
Privacy: gtsmodel.VisibilityDefault,
}
// Insert the settings!
if err := a.state.DB.PutAccountSettings(ctx, settings); err != nil {
return nil, err
}
account = &gtsmodel.Account{
ID: accountID,
Username: newSignup.Username,
@ -145,13 +135,26 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (
PrivateKey: privKey,
PublicKey: &privKey.PublicKey,
PublicKeyURI: uris.PublicKeyURI,
Settings: settings,
}
// Insert the new account!
if err := a.state.DB.PutAccount(ctx, account); err != nil {
return nil, err
}
// Insert basic settings for new account.
account.Settings = &gtsmodel.AccountSettings{
AccountID: accountID,
Privacy: gtsmodel.VisibilityDefault,
}
if err := a.state.DB.PutAccountSettings(ctx, account.Settings); err != nil {
return nil, err
}
// Stub empty stats for new account.
if err := a.state.DB.StubAccountStats(ctx, account); err != nil {
return nil, err
}
}
// Created or already had an account.