mirror of
https://github.com/hoernschen/dendrite.git
synced 2025-08-01 13:52:46 +00:00
Fix newly joined users presence (#2854)
Fixes #2803 Also refactors the presence stream to not hit the database for every user, instead queries all users at once now.
This commit is contained in:
parent
0351618ff4
commit
c136a450d5
11 changed files with 263 additions and 75 deletions
|
@ -207,7 +207,7 @@ type Ignores interface {
|
|||
|
||||
type Presence interface {
|
||||
UpsertPresence(ctx context.Context, txn *sql.Tx, userID string, statusMsg *string, presence types.Presence, lastActiveTS gomatrixserverlib.Timestamp, fromSync bool) (pos types.StreamPosition, err error)
|
||||
GetPresenceForUser(ctx context.Context, txn *sql.Tx, userID string) (presence *types.PresenceInternal, err error)
|
||||
GetPresenceForUsers(ctx context.Context, txn *sql.Tx, userIDs []string) (presence []*types.PresenceInternal, err error)
|
||||
GetMaxPresenceID(ctx context.Context, txn *sql.Tx) (pos types.StreamPosition, err error)
|
||||
GetPresenceAfter(ctx context.Context, txn *sql.Tx, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (presences map[string]*types.PresenceInternal, err error)
|
||||
}
|
||||
|
|
136
syncapi/storage/tables/presence_table_test.go
Normal file
136
syncapi/storage/tables/presence_table_test.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
package tables_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage/postgres"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage/sqlite3"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage/tables"
|
||||
"github.com/matrix-org/dendrite/syncapi/types"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
)
|
||||
|
||||
func mustPresenceTable(t *testing.T, dbType test.DBType) (tables.Presence, func()) {
|
||||
t.Helper()
|
||||
connStr, close := test.PrepareDBConnectionString(t, dbType)
|
||||
db, err := sqlutil.Open(&config.DatabaseOptions{
|
||||
ConnectionString: config.DataSource(connStr),
|
||||
}, sqlutil.NewExclusiveWriter())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open db: %s", err)
|
||||
}
|
||||
|
||||
var tab tables.Presence
|
||||
switch dbType {
|
||||
case test.DBTypePostgres:
|
||||
tab, err = postgres.NewPostgresPresenceTable(db)
|
||||
case test.DBTypeSQLite:
|
||||
var stream sqlite3.StreamIDStatements
|
||||
if err = stream.Prepare(db); err != nil {
|
||||
t.Fatalf("failed to prepare stream stmts: %s", err)
|
||||
}
|
||||
tab, err = sqlite3.NewSqlitePresenceTable(db, &stream)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make new table: %s", err)
|
||||
}
|
||||
return tab, close
|
||||
}
|
||||
|
||||
func TestPresence(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
ctx := context.Background()
|
||||
|
||||
statusMsg := "Hello World!"
|
||||
timestamp := gomatrixserverlib.AsTimestamp(time.Now())
|
||||
|
||||
var txn *sql.Tx
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
tab, closeDB := mustPresenceTable(t, dbType)
|
||||
defer closeDB()
|
||||
|
||||
// Insert some presences
|
||||
pos, err := tab.UpsertPresence(ctx, txn, alice.ID, &statusMsg, types.PresenceOnline, timestamp, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
wantPos := types.StreamPosition(1)
|
||||
if pos != wantPos {
|
||||
t.Errorf("expected pos to be %d, got %d", wantPos, pos)
|
||||
}
|
||||
pos, err = tab.UpsertPresence(ctx, txn, bob.ID, &statusMsg, types.PresenceOnline, timestamp, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
wantPos = 2
|
||||
if pos != wantPos {
|
||||
t.Errorf("expected pos to be %d, got %d", wantPos, pos)
|
||||
}
|
||||
|
||||
// verify the expected max presence ID
|
||||
maxPos, err := tab.GetMaxPresenceID(ctx, txn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if maxPos != wantPos {
|
||||
t.Errorf("expected max pos to be %d, got %d", wantPos, maxPos)
|
||||
}
|
||||
|
||||
// This should increment the position
|
||||
pos, err = tab.UpsertPresence(ctx, txn, bob.ID, &statusMsg, types.PresenceOnline, timestamp, true)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
wantPos = pos
|
||||
if wantPos <= maxPos {
|
||||
t.Errorf("expected pos to be %d incremented, got %d", wantPos, pos)
|
||||
}
|
||||
|
||||
// This should return only Bobs status
|
||||
presences, err := tab.GetPresenceAfter(ctx, txn, maxPos, gomatrixserverlib.EventFilter{Limit: 10})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if c := len(presences); c > 1 {
|
||||
t.Errorf("expected only one presence, got %d", c)
|
||||
}
|
||||
|
||||
// Validate the response
|
||||
wantPresence := &types.PresenceInternal{
|
||||
UserID: bob.ID,
|
||||
Presence: types.PresenceOnline,
|
||||
StreamPos: wantPos,
|
||||
LastActiveTS: timestamp,
|
||||
ClientFields: types.PresenceClientResponse{
|
||||
LastActiveAgo: 0,
|
||||
Presence: types.PresenceOnline.String(),
|
||||
StatusMsg: &statusMsg,
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(wantPresence, presences[bob.ID]) {
|
||||
t.Errorf("unexpected presence result:\n%+v, want\n%+v", presences[bob.ID], wantPresence)
|
||||
}
|
||||
|
||||
// Try getting presences for existing and non-existing users
|
||||
getUsers := []string{alice.ID, bob.ID, "@doesntexist:test"}
|
||||
presencesForUsers, err := tab.GetPresenceForUsers(ctx, nil, getUsers)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(presencesForUsers) >= len(getUsers) {
|
||||
t.Errorf("expected less presences, but they are the same/more as requested: %d >= %d", len(presencesForUsers), len(getUsers))
|
||||
}
|
||||
})
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue