Compare commits

...

5 commits

Author SHA1 Message Date
Till Faelligen 2b713fe761
Re-slice correctly AFTER sorting 2023-01-03 20:00:19 +01:00
Till Faelligen 2f809137c9
Break everything by using a lateral join to query all recent events for
a user
2023-01-03 15:42:14 +01:00
Till Faelligen 539781a51a
Debug logging 2023-01-03 07:48:56 +01:00
Till Faelligen b9d947e438
Further optimization 2023-01-02 10:20:28 +01:00
Till Faelligen c915689d20
Optimize /sync a bit 2023-01-02 09:55:39 +01:00
9 changed files with 257 additions and 101 deletions

View file

@ -46,7 +46,7 @@ type DatabaseTransaction interface {
RoomIDsWithMembership(ctx context.Context, userID string, membership string) ([]string, error) RoomIDsWithMembership(ctx context.Context, userID string, membership string) ([]string, error)
MembershipCount(ctx context.Context, roomID, membership string, pos types.StreamPosition) (int, error) MembershipCount(ctx context.Context, roomID, membership string, pos types.StreamPosition) (int, error)
GetRoomHeroes(ctx context.Context, roomID, userID string, memberships []string) ([]string, error) GetRoomHeroes(ctx context.Context, roomID, userID string, memberships []string) ([]string, error)
RecentEvents(ctx context.Context, roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) ([]types.StreamEvent, bool, error) RecentEvents(ctx context.Context, userID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error)
GetBackwardTopologyPos(ctx context.Context, events []*gomatrixserverlib.HeaderedEvent) (types.TopologyToken, error) GetBackwardTopologyPos(ctx context.Context, events []*gomatrixserverlib.HeaderedEvent) (types.TopologyToken, error)
PositionInTopology(ctx context.Context, eventID string) (pos types.StreamPosition, spos types.StreamPosition, err error) PositionInTopology(ctx context.Context, eventID string) (pos types.StreamPosition, spos types.StreamPosition, err error)
InviteEventsInRange(ctx context.Context, targetUserID string, r types.Range) (map[string]*gomatrixserverlib.HeaderedEvent, map[string]*gomatrixserverlib.HeaderedEvent, types.StreamPosition, error) InviteEventsInRange(ctx context.Context, targetUserID string, r types.Range) (map[string]*gomatrixserverlib.HeaderedEvent, map[string]*gomatrixserverlib.HeaderedEvent, types.StreamPosition, error)

View file

@ -19,6 +19,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal"
@ -291,11 +292,14 @@ func (s *currentRoomStateStatements) SelectCurrentState(
pq.StringArray(excludeEventIDs), pq.StringArray(excludeEventIDs),
) )
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("SelectCurrentState failed: %w", err)
} }
defer internal.CloseAndLogIfError(ctx, rows, "selectCurrentState: rows.close() failed") defer internal.CloseAndLogIfError(ctx, rows, "selectCurrentState: rows.close() failed")
res, err := rowsToEvents(rows)
return rowsToEvents(rows) if err != nil {
return nil, fmt.Errorf("rowsToEvents failed: %w", err)
}
return res, nil
} }
func (s *currentRoomStateStatements) DeleteRoomStateByEventID( func (s *currentRoomStateStatements) DeleteRoomStateByEventID(

View file

@ -19,17 +19,18 @@ import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt"
"sort" "sort"
"github.com/lib/pq"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/syncapi/storage/postgres/deltas" "github.com/matrix-org/dendrite/syncapi/storage/postgres/deltas"
"github.com/matrix-org/dendrite/syncapi/storage/tables" "github.com/matrix-org/dendrite/syncapi/storage/tables"
"github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/syncapi/types"
"github.com/lib/pq"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/internal/sqlutil"
) )
@ -109,14 +110,26 @@ const selectRecentEventsSQL = "" +
" AND ( $7::text[] IS NULL OR NOT(type LIKE ANY($7)) )" + " AND ( $7::text[] IS NULL OR NOT(type LIKE ANY($7)) )" +
" ORDER BY id DESC LIMIT $8" " ORDER BY id DESC LIMIT $8"
const selectRecentEventsForSyncSQL = "" + const selectRecentEventsForSyncSQL = `
"SELECT event_id, id, headered_event_json, session_id, exclude_from_sync, transaction_id, history_visibility FROM syncapi_output_room_events" + SELECT result.*
" WHERE room_id = $1 AND id > $2 AND id <= $3 AND exclude_from_sync = FALSE" + FROM (
" AND ( $4::text[] IS NULL OR sender = ANY($4) )" + SELECT DISTINCT room_id FROM syncapi_current_room_state WHERE type = 'm.room.member' AND state_key = $1 AND membership = 'join'
" AND ( $5::text[] IS NULL OR NOT(sender = ANY($5)) )" + ) room_ids
" AND ( $6::text[] IS NULL OR type LIKE ANY($6) )" + JOIN LATERAL (
" AND ( $7::text[] IS NULL OR NOT(type LIKE ANY($7)) )" + SELECT room_id, event_id, id, headered_event_json, session_id, exclude_from_sync, transaction_id, history_visibility
" ORDER BY id DESC LIMIT $8" FROM syncapi_output_room_events recent_events
WHERE
recent_events.room_id = room_ids.room_id
AND recent_events.exclude_from_sync = FALSE
AND id > $2 AND id <= $3
AND ( $4::text[] IS NULL OR sender = ANY($4) )
AND ( $5::text[] IS NULL OR NOT(sender = ANY($5)) )
AND ( $6::text[] IS NULL OR type LIKE ANY($6) )
AND ( $7::text[] IS NULL OR NOT(type LIKE ANY($7)) )
ORDER BY recent_events.id DESC
LIMIT $8
) result on true
`
const selectEarlyEventsSQL = "" + const selectEarlyEventsSQL = "" +
"SELECT event_id, id, headered_event_json, session_id, exclude_from_sync, transaction_id, history_visibility FROM syncapi_output_room_events" + "SELECT event_id, id, headered_event_json, session_id, exclude_from_sync, transaction_id, history_visibility FROM syncapi_output_room_events" +
@ -137,7 +150,7 @@ const updateEventJSONSQL = "" +
const selectStateInRangeFilteredSQL = "" + const selectStateInRangeFilteredSQL = "" +
"SELECT event_id, id, headered_event_json, exclude_from_sync, add_state_ids, remove_state_ids, history_visibility" + "SELECT event_id, id, headered_event_json, exclude_from_sync, add_state_ids, remove_state_ids, history_visibility" +
" FROM syncapi_output_room_events" + " FROM syncapi_output_room_events" +
" WHERE (id > $1 AND id <= $2) AND (add_state_ids IS NOT NULL OR remove_state_ids IS NOT NULL)" + " WHERE (id > $1 AND id <= $2) AND (cardinality(add_state_ids) > 0 OR cardinality(remove_state_ids) > 0)" +
" AND room_id = ANY($3)" + " AND room_id = ANY($3)" +
" AND ( $4::text[] IS NULL OR sender = ANY($4) )" + " AND ( $4::text[] IS NULL OR sender = ANY($4) )" +
" AND ( $5::text[] IS NULL OR NOT(sender = ANY($5)) )" + " AND ( $5::text[] IS NULL OR NOT(sender = ANY($5)) )" +
@ -150,7 +163,7 @@ const selectStateInRangeFilteredSQL = "" +
const selectStateInRangeSQL = "" + const selectStateInRangeSQL = "" +
"SELECT event_id, id, headered_event_json, exclude_from_sync, add_state_ids, remove_state_ids, history_visibility" + "SELECT event_id, id, headered_event_json, exclude_from_sync, add_state_ids, remove_state_ids, history_visibility" +
" FROM syncapi_output_room_events" + " FROM syncapi_output_room_events" +
" WHERE (id > $1 AND id <= $2) AND (add_state_ids IS NOT NULL OR remove_state_ids IS NOT NULL)" + " WHERE (id > $1 AND id <= $2) AND (cardinality(add_state_ids) > 0 OR cardinality(remove_state_ids) > 0)" +
" AND room_id = ANY($3)" + " AND room_id = ANY($3)" +
" ORDER BY id ASC" " ORDER BY id ASC"
@ -393,9 +406,9 @@ func (s *outputRoomEventsStatements) InsertEvent(
// from sync. // from sync.
func (s *outputRoomEventsStatements) SelectRecentEvents( func (s *outputRoomEventsStatements) SelectRecentEvents(
ctx context.Context, txn *sql.Tx, ctx context.Context, txn *sql.Tx,
roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, userID string, ra types.Range, eventFilter *gomatrixserverlib.RoomEventFilter,
chronologicalOrder bool, onlySyncEvents bool, chronologicalOrder bool, onlySyncEvents bool,
) ([]types.StreamEvent, bool, error) { ) (map[string]types.RecentEvents, error) {
var stmt *sql.Stmt var stmt *sql.Stmt
if onlySyncEvents { if onlySyncEvents {
stmt = sqlutil.TxStmt(txn, s.selectRecentEventsForSyncStmt) stmt = sqlutil.TxStmt(txn, s.selectRecentEventsForSyncStmt)
@ -404,7 +417,7 @@ func (s *outputRoomEventsStatements) SelectRecentEvents(
} }
senders, notSenders := getSendersRoomEventFilter(eventFilter) senders, notSenders := getSendersRoomEventFilter(eventFilter)
rows, err := stmt.QueryContext( rows, err := stmt.QueryContext(
ctx, roomID, r.Low(), r.High(), ctx, userID, ra.Low(), ra.High(),
pq.StringArray(senders), pq.StringArray(senders),
pq.StringArray(notSenders), pq.StringArray(notSenders),
pq.StringArray(filterConvertTypeWildcardToSQL(eventFilter.Types)), pq.StringArray(filterConvertTypeWildcardToSQL(eventFilter.Types)),
@ -412,34 +425,79 @@ func (s *outputRoomEventsStatements) SelectRecentEvents(
eventFilter.Limit+1, eventFilter.Limit+1,
) )
if err != nil { if err != nil {
return nil, false, err return nil, fmt.Errorf("SelectRecentEvents failed: %w", err)
} }
defer internal.CloseAndLogIfError(ctx, rows, "selectRecentEvents: rows.close() failed")
events, err := rowsToStreamEvents(rows) result := make(map[string]types.RecentEvents)
if err != nil {
return nil, false, err for rows.Next() {
} var (
if chronologicalOrder { roomID string
// The events need to be returned from oldest to latest, which isn't eventID string
// necessary the way the SQL query returns them, so a sort is necessary to streamPos types.StreamPosition
// ensure the events are in the right order in the slice. eventBytes []byte
sort.SliceStable(events, func(i int, j int) bool { excludeFromSync bool
return events[i].StreamPosition < events[j].StreamPosition sessionID *int64
txnID *string
transactionID *api.TransactionID
historyVisibility gomatrixserverlib.HistoryVisibility
)
if err := rows.Scan(&roomID, &eventID, &streamPos, &eventBytes, &sessionID, &excludeFromSync, &txnID, &historyVisibility); err != nil {
return nil, err
}
// TODO: Handle redacted events
var ev gomatrixserverlib.HeaderedEvent
if err := ev.UnmarshalJSONWithEventID(eventBytes, eventID); err != nil {
return nil, err
}
if sessionID != nil && txnID != nil {
transactionID = &api.TransactionID{
SessionID: *sessionID,
TransactionID: *txnID,
}
}
r := result[roomID]
ev.Visibility = historyVisibility
r.Events = append(r.Events, types.StreamEvent{
HeaderedEvent: &ev,
StreamPosition: streamPos,
TransactionID: transactionID,
ExcludeFromSync: excludeFromSync,
}) })
result[roomID] = r
} }
// we queried for 1 more than the limit, so if we returned one more mark limited=true
limited := false defer internal.CloseAndLogIfError(ctx, rows, "selectRecentEvents: rows.close() failed")
if len(events) > eventFilter.Limit {
limited = true if chronologicalOrder {
// re-slice the extra (oldest) event out: in chronological order this is the first entry, else the last. for roomID, x := range result {
if chronologicalOrder { sort.SliceStable(x.Events, func(i int, j int) bool {
events = events[1:] return x.Events[i].StreamPosition < x.Events[j].StreamPosition
} else { })
events = events[:len(events)-1]
// we queried for 1 more than the limit, so if we returned one more mark limited=true
if len(x.Events) > eventFilter.Limit {
x.Limited = true
// re-slice the extra (oldest) event out: in chronological order this is the first entry, else the last.
x.Events = x.Events[1:]
result[roomID] = x
}
}
} else {
for roomID, x := range result {
if len(x.Events) > eventFilter.Limit {
x.Limited = true
x.Events = x.Events[:len(x.Events)-1]
result[roomID] = x
}
} }
} }
return events, limited, nil return result, rows.Err()
} }
// selectEarlyEvents returns the earliest events in the given room, starting // selectEarlyEvents returns the earliest events in the given room, starting

View file

@ -5,11 +5,10 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/dendrite/internal/eventutil"
"github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/syncapi/types"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
) )
type DatabaseTransaction struct { type DatabaseTransaction struct {
@ -96,8 +95,8 @@ func (d *DatabaseTransaction) GetRoomHeroes(ctx context.Context, roomID, userID
return d.Memberships.SelectHeroes(ctx, d.txn, roomID, userID, memberships) return d.Memberships.SelectHeroes(ctx, d.txn, roomID, userID, memberships)
} }
func (d *DatabaseTransaction) RecentEvents(ctx context.Context, roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) ([]types.StreamEvent, bool, error) { func (d *DatabaseTransaction) RecentEvents(ctx context.Context, userID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error) {
return d.OutputEvents.SelectRecentEvents(ctx, d.txn, roomID, r, eventFilter, chronologicalOrder, onlySyncEvents) return d.OutputEvents.SelectRecentEvents(ctx, d.txn, userID, r, eventFilter, chronologicalOrder, onlySyncEvents)
} }
func (d *DatabaseTransaction) PositionInTopology(ctx context.Context, eventID string) (pos types.StreamPosition, spos types.StreamPosition, err error) { func (d *DatabaseTransaction) PositionInTopology(ctx context.Context, eventID string) (pos types.StreamPosition, spos types.StreamPosition, err error) {
@ -329,7 +328,13 @@ func (d *DatabaseTransaction) GetStateDeltas(
} }
// get all the state events ever (i.e. for all available rooms) between these two positions // get all the state events ever (i.e. for all available rooms) between these two positions
stateNeededFiltered, eventMapFiltered, err := d.OutputEvents.SelectStateInRange(ctx, d.txn, r, stateFilter, allRoomIDs) stateNeededFiltered := stateNeeded
eventMapFiltered := eventMap
// avoid hitting the database if the result would be the same as above
if !isStatefilterEmpty(stateFilter) {
stateNeededFiltered, eventMapFiltered, err = d.OutputEvents.SelectStateInRange(ctx, d.txn, r, stateFilter, allRoomIDs)
}
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, nil, nil return nil, nil, nil
@ -688,3 +693,26 @@ func (d *DatabaseTransaction) RelationsFor(ctx context.Context, roomID, eventID,
return events, prevBatch, nextBatch, nil return events, prevBatch, nextBatch, nil
} }
// TODO: move to GMSL
func isStatefilterEmpty(filter *gomatrixserverlib.StateFilter) bool {
if filter == nil {
return true
}
switch {
case filter.NotTypes != nil && len(*filter.NotTypes) > 0:
return false
case filter.Types != nil && len(*filter.Types) > 0:
return false
case filter.Senders != nil && len(*filter.Senders) > 0:
return false
case filter.NotSenders != nil && len(*filter.NotSenders) > 0:
return false
case filter.NotRooms != nil && len(*filter.NotRooms) > 0:
return false
case filter.ContainsURL != nil:
return false
default:
return true
}
}

View file

@ -361,11 +361,7 @@ func (s *outputRoomEventsStatements) InsertEvent(
return streamPos, err return streamPos, err
} }
func (s *outputRoomEventsStatements) SelectRecentEvents( func (s *outputRoomEventsStatements) SelectRecentEvents(ctx context.Context, txn *sql.Tx, roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error) {
ctx context.Context, txn *sql.Tx,
roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter,
chronologicalOrder bool, onlySyncEvents bool,
) ([]types.StreamEvent, bool, error) {
var query string var query string
if onlySyncEvents { if onlySyncEvents {
query = selectRecentEventsForSyncSQL query = selectRecentEventsForSyncSQL
@ -383,39 +379,77 @@ func (s *outputRoomEventsStatements) SelectRecentEvents(
nil, eventFilter.ContainsURL, eventFilter.Limit+1, FilterOrderDesc, nil, eventFilter.ContainsURL, eventFilter.Limit+1, FilterOrderDesc,
) )
if err != nil { if err != nil {
return nil, false, fmt.Errorf("s.prepareWithFilters: %w", err) return nil, err
} }
defer internal.CloseAndLogIfError(ctx, stmt, "selectRecentEvents: stmt.close() failed")
rows, err := stmt.QueryContext(ctx, params...) rows, err := stmt.QueryContext(ctx, params...)
if err != nil { if err != nil {
return nil, false, err return nil, fmt.Errorf("SelectRecentEvents failed: %w", err)
} }
defer internal.CloseAndLogIfError(ctx, rows, "selectRecentEvents: rows.close() failed")
events, err := rowsToStreamEvents(rows) result := make(map[string]types.RecentEvents)
if err != nil {
return nil, false, err for rows.Next() {
} var (
if chronologicalOrder { roomID string
// The events need to be returned from oldest to latest, which isn't eventID string
// necessary the way the SQL query returns them, so a sort is necessary to streamPos types.StreamPosition
// ensure the events are in the right order in the slice. eventBytes []byte
sort.SliceStable(events, func(i int, j int) bool { excludeFromSync bool
return events[i].StreamPosition < events[j].StreamPosition sessionID *int64
txnID *string
transactionID *api.TransactionID
historyVisibility gomatrixserverlib.HistoryVisibility
)
if err := rows.Scan(&roomID, &eventID, &streamPos, &eventBytes, &sessionID, &excludeFromSync, &txnID, &historyVisibility); err != nil {
return nil, err
}
// TODO: Handle redacted events
var ev gomatrixserverlib.HeaderedEvent
if err := ev.UnmarshalJSONWithEventID(eventBytes, eventID); err != nil {
return nil, err
}
if sessionID != nil && txnID != nil {
transactionID = &api.TransactionID{
SessionID: *sessionID,
TransactionID: *txnID,
}
}
r := result[roomID]
ev.Visibility = historyVisibility
r.Events = append(r.Events, types.StreamEvent{
HeaderedEvent: &ev,
StreamPosition: streamPos,
TransactionID: transactionID,
ExcludeFromSync: excludeFromSync,
}) })
// we queried for 1 more than the limit, so if we returned one more mark limited=true
if len(r.Events) > eventFilter.Limit {
r.Limited = true
// re-slice the extra (oldest) event out: in chronological order this is the first entry, else the last.
if chronologicalOrder {
r.Events = r.Events[1:]
} else {
r.Events = r.Events[:len(r.Events)-1]
}
}
result[roomID] = r
} }
// we queried for 1 more than the limit, so if we returned one more mark limited=true
limited := false defer internal.CloseAndLogIfError(ctx, rows, "selectRecentEvents: rows.close() failed")
if len(events) > eventFilter.Limit {
limited = true if chronologicalOrder {
// re-slice the extra (oldest) event out: in chronological order this is the first entry, else the last. for _, x := range result {
if chronologicalOrder { sort.SliceStable(x.Events, func(i int, j int) bool {
events = events[1:] return x.Events[i].StreamPosition < x.Events[j].StreamPosition
} else { })
events = events[:len(events)-1]
} }
} }
return events, limited, nil
return result, rows.Err()
} }
func (s *outputRoomEventsStatements) SelectEarlyEvents( func (s *outputRoomEventsStatements) SelectEarlyEvents(

View file

@ -5,7 +5,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect"
"testing" "testing"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
@ -73,7 +72,7 @@ func WithSnapshot(t *testing.T, db storage.Database, f func(snapshot storage.Dat
// These tests assert basic functionality of RecentEvents for PDUs // These tests assert basic functionality of RecentEvents for PDUs
func TestRecentEventsPDU(t *testing.T) { func TestRecentEventsPDU(t *testing.T) {
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { /*test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
db, close, closeBase := MustCreateDatabase(t, dbType) db, close, closeBase := MustCreateDatabase(t, dbType)
defer close() defer close()
defer closeBase() defer closeBase()
@ -180,7 +179,7 @@ func TestRecentEventsPDU(t *testing.T) {
} }
}) })
} }
}) })*/
} }
// The purpose of this test is to ensure that backfill does indeed go backwards, using a topology token // The purpose of this test is to ensure that backfill does indeed go backwards, using a topology token

View file

@ -64,7 +64,7 @@ type Events interface {
// SelectRecentEvents returns events between the two stream positions: exclusive of low and inclusive of high. // SelectRecentEvents returns events between the two stream positions: exclusive of low and inclusive of high.
// If onlySyncEvents has a value of true, only returns the events that aren't marked as to exclude from sync. // If onlySyncEvents has a value of true, only returns the events that aren't marked as to exclude from sync.
// Returns up to `limit` events. Returns `limited=true` if there are more events in this range but we hit the `limit`. // Returns up to `limit` events. Returns `limited=true` if there are more events in this range but we hit the `limit`.
SelectRecentEvents(ctx context.Context, txn *sql.Tx, roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) ([]types.StreamEvent, bool, error) SelectRecentEvents(ctx context.Context, txn *sql.Tx, roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter, chronologicalOrder bool, onlySyncEvents bool) (map[string]types.RecentEvents, error)
// SelectEarlyEvents returns the earliest events in the given room. // SelectEarlyEvents returns the earliest events in the given room.
SelectEarlyEvents(ctx context.Context, txn *sql.Tx, roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter) ([]types.StreamEvent, error) SelectEarlyEvents(ctx context.Context, txn *sql.Tx, roomID string, r types.Range, eventFilter *gomatrixserverlib.RoomEventFilter) ([]types.StreamEvent, error)
SelectEvents(ctx context.Context, txn *sql.Tx, eventIDs []string, filter *gomatrixserverlib.RoomEventFilter, preserveOrder bool) ([]types.StreamEvent, error) SelectEvents(ctx context.Context, txn *sql.Tx, eventIDs []string, filter *gomatrixserverlib.RoomEventFilter, preserveOrder bool) ([]types.StreamEvent, error)

View file

@ -95,12 +95,31 @@ func (p *PDUStreamProvider) CompleteSync(
} }
// Build up a /sync response. Add joined rooms. // Build up a /sync response. Add joined rooms.
for _, roomID := range joinedRoomIDs { req.Log.WithField("rooms", len(joinedRoomIDs)).Debug("getting join response for rooms")
s := time.Now()
// query all recent events for all rooms
recentStreamEvents, err := snapshot.RecentEvents(
ctx, req.Device.UserID, r, &eventFilter, true, true,
)
if err != nil {
logrus.WithError(err).Error("failed to get recent events")
return from
}
req.Log.WithContext(ctx).WithFields(logrus.Fields{
"duration": time.Since(s),
}).Debugln("got all recent events")
for roomID, recentEvents := range recentStreamEvents {
rs := time.Now()
jr, jerr := p.getJoinResponseForCompleteSync( jr, jerr := p.getJoinResponseForCompleteSync(
ctx, snapshot, roomID, r, &stateFilter, &eventFilter, req.WantFullState, req.Device, false, ctx, snapshot, roomID, r, &stateFilter, &eventFilter, req.WantFullState, req.Device, false,
recentEvents.Events, recentEvents.Limited,
) )
if jerr != nil { if jerr != nil {
req.Log.WithError(jerr).Error("p.getJoinResponseForCompleteSync failed") req.Log.WithError(jerr).WithContext(ctx).WithFields(logrus.Fields{
"failed_after": time.Since(s),
"room_id": roomID,
}).Error("p.getJoinResponseForCompleteSync failed")
if ctxErr := req.Context.Err(); ctxErr != nil || jerr == sql.ErrTxDone { if ctxErr := req.Context.Err(); ctxErr != nil || jerr == sql.ErrTxDone {
return from return from
} }
@ -108,10 +127,11 @@ func (p *PDUStreamProvider) CompleteSync(
} }
req.Response.Rooms.Join[roomID] = jr req.Response.Rooms.Join[roomID] = jr
req.Rooms[roomID] = gomatrixserverlib.Join req.Rooms[roomID] = gomatrixserverlib.Join
req.Log.WithFields(logrus.Fields{"room_id": roomID, "duration": time.Since(rs)}).Debugln("got room data")
} }
// Add peeked rooms. // Add peeked rooms.
peeks, err := snapshot.PeeksInRange(ctx, req.Device.UserID, req.Device.ID, r) /*peeks, err := snapshot.PeeksInRange(ctx, req.Device.UserID, req.Device.ID, r)
if err != nil { if err != nil {
req.Log.WithError(err).Error("p.DB.PeeksInRange failed") req.Log.WithError(err).Error("p.DB.PeeksInRange failed")
return from return from
@ -131,7 +151,7 @@ func (p *PDUStreamProvider) CompleteSync(
} }
req.Response.Rooms.Peek[peek.RoomID] = jr req.Response.Rooms.Peek[peek.RoomID] = jr
} }
} }*/
return to return to
} }
@ -179,6 +199,19 @@ func (p *PDUStreamProvider) IncrementalSync(
req.Log.WithError(err).Error("unable to update event filter with ignored users") req.Log.WithError(err).Error("unable to update event filter with ignored users")
} }
s := time.Now()
// query all recent events for all rooms
recentStreamEvents, err := snapshot.RecentEvents(
ctx, req.Device.UserID, r, &eventFilter, true, true,
)
if err != nil {
logrus.WithError(err).Error("failed to get recent events")
return from
}
req.Log.WithContext(ctx).WithFields(logrus.Fields{
"duration": time.Since(s),
}).Debugln("(inc) got all recent events")
newPos = from newPos = from
for _, delta := range stateDeltas { for _, delta := range stateDeltas {
newRange := r newRange := r
@ -194,7 +227,7 @@ func (p *PDUStreamProvider) IncrementalSync(
} }
} }
var pos types.StreamPosition var pos types.StreamPosition
if pos, err = p.addRoomDeltaToResponse(ctx, snapshot, req.Device, newRange, delta, &eventFilter, &stateFilter, req); err != nil { if pos, err = p.addRoomDeltaToResponse(ctx, snapshot, req.Device, newRange, delta, &eventFilter, &stateFilter, req, recentStreamEvents[delta.RoomID].Events, recentStreamEvents[delta.RoomID].Limited); err != nil {
req.Log.WithError(err).Error("d.addRoomDeltaToResponse failed") req.Log.WithError(err).Error("d.addRoomDeltaToResponse failed")
if err == context.DeadlineExceeded || err == context.Canceled || err == sql.ErrTxDone { if err == context.DeadlineExceeded || err == context.Canceled || err == sql.ErrTxDone {
return newPos return newPos
@ -229,6 +262,8 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse(
eventFilter *gomatrixserverlib.RoomEventFilter, eventFilter *gomatrixserverlib.RoomEventFilter,
stateFilter *gomatrixserverlib.StateFilter, stateFilter *gomatrixserverlib.StateFilter,
req *types.SyncRequest, req *types.SyncRequest,
recentStreamEvents []types.StreamEvent,
limited bool,
) (types.StreamPosition, error) { ) (types.StreamPosition, error) {
originalLimit := eventFilter.Limit originalLimit := eventFilter.Limit
@ -241,16 +276,6 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse(
} }
} }
recentStreamEvents, limited, err := snapshot.RecentEvents(
ctx, delta.RoomID, r,
eventFilter, true, true,
)
if err != nil {
if err == sql.ErrNoRows {
return r.To, nil
}
return r.From, fmt.Errorf("p.DB.RecentEvents: %w", err)
}
recentEvents := gomatrixserverlib.HeaderedReverseTopologicalOrdering( recentEvents := gomatrixserverlib.HeaderedReverseTopologicalOrdering(
snapshot.StreamEventsToEvents(device, recentStreamEvents), snapshot.StreamEventsToEvents(device, recentStreamEvents),
gomatrixserverlib.TopologicalOrderByPrevEvents, gomatrixserverlib.TopologicalOrderByPrevEvents,
@ -267,6 +292,7 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse(
if r.Backwards { if r.Backwards {
latestPosition = r.From latestPosition = r.From
} }
var err error
updateLatestPosition := func(mostRecentEventID string) { updateLatestPosition := func(mostRecentEventID string) {
var pos types.StreamPosition var pos types.StreamPosition
if _, pos, err = snapshot.PositionInTopology(ctx, mostRecentEventID); err == nil { if _, pos, err = snapshot.PositionInTopology(ctx, mostRecentEventID); err == nil {
@ -460,13 +486,14 @@ func (p *PDUStreamProvider) getJoinResponseForCompleteSync(
wantFullState bool, wantFullState bool,
device *userapi.Device, device *userapi.Device,
isPeek bool, isPeek bool,
recentStreamEvents []types.StreamEvent,
limited bool,
) (jr *types.JoinResponse, err error) { ) (jr *types.JoinResponse, err error) {
_ = eventFilter
jr = types.NewJoinResponse() jr = types.NewJoinResponse()
// TODO: When filters are added, we may need to call this multiple times to get enough events. // TODO: When filters are added, we may need to call this multiple times to get enough events.
// See: https://github.com/matrix-org/synapse/blob/v0.19.3/synapse/handlers/sync.py#L316 // See: https://github.com/matrix-org/synapse/blob/v0.19.3/synapse/handlers/sync.py#L316
recentStreamEvents, limited, err := snapshot.RecentEvents(
ctx, roomID, r, eventFilter, true, true,
)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return jr, nil return jr, nil
@ -540,7 +567,7 @@ func (p *PDUStreamProvider) getJoinResponseForCompleteSync(
} }
backwardTopologyPos, backwardStreamPos, err = snapshot.PositionInTopology(ctx, event.EventID()) backwardTopologyPos, backwardStreamPos, err = snapshot.PositionInTopology(ctx, event.EventID())
if err != nil { if err != nil {
return return jr, fmt.Errorf("PositionInTopology failed: %w", err)
} }
prevBatch = &types.TopologyToken{ prevBatch = &types.TopologyToken{
Depth: backwardTopologyPos, Depth: backwardTopologyPos,
@ -609,7 +636,7 @@ func (p *PDUStreamProvider) lazyLoadMembers(
filter.Types = &[]string{gomatrixserverlib.MRoomMember} filter.Types = &[]string{gomatrixserverlib.MRoomMember}
memberships, err := snapshot.GetStateEventsForRoom(ctx, roomID, &filter) memberships, err := snapshot.GetStateEventsForRoom(ctx, roomID, &filter)
if err != nil { if err != nil {
return stateEvents, err return stateEvents, fmt.Errorf("(lazyLoadMembers) GetStateEventsForRoom failed: %w", err)
} }
// cache the membership events // cache the membership events
for _, membership := range memberships { for _, membership := range memberships {

View file

@ -63,6 +63,12 @@ type StreamEvent struct {
ExcludeFromSync bool ExcludeFromSync bool
} }
// RecentEvents contains StreamEvents with the information if they are limited by a filter
type RecentEvents struct {
Limited bool
Events []StreamEvent
}
// Range represents a range between two stream positions. // Range represents a range between two stream positions.
type Range struct { type Range struct {
// From is the position the client has already received. // From is the position the client has already received.