sliding-sync/tests-integration/slow_get_all_rooms_test.go

163 lines
6.3 KiB
Go
Raw Normal View History

package syncv3
import (
"encoding/json"
"fmt"
"testing"
"time"
"github.com/matrix-org/sliding-sync/sync2"
"github.com/matrix-org/sliding-sync/sync3"
"github.com/matrix-org/sliding-sync/testutils"
"github.com/matrix-org/sliding-sync/testutils/m"
)
// - Test that you can get all rooms initially for a list
// - Test that you can get all rooms initially for a list with specific filters
// - Test that newly joined rooms still include all state
// - Test that live updates just send the event and no ops
func TestSlowGetAllRoomsInitial(t *testing.T) {
boolTrue := true
numTimelineEventsPerRoom := 3
// setup code
v2 := runTestV2Server(t)
v3 := runTestServer(t, v2, "")
defer v2.close()
defer v3.close()
// make 20 rooms, last room is most recent, and send A,B,C into each room
allRooms := make([]roomEvents, 20)
allRoomIDs := make([]string, len(allRooms))
allRoomMatchers := make(map[string][]m.RoomMatcher)
latestTimestamp := time.Now()
for i := 0; i < len(allRooms); i++ {
ts := time.Now().Add(time.Duration(i) * time.Minute)
roomName := fmt.Sprintf("My Room %d", i)
allRooms[i] = roomEvents{
roomID: fmt.Sprintf("!TestSlowGetAllRoomsInitial_%d:localhost", i),
name: roomName,
events: append(createRoomState(t, alice, ts), []json.RawMessage{
testutils.NewStateEvent(t, "m.room.name", "", alice, map[string]interface{}{"name": roomName}, testutils.WithTimestamp(ts.Add(3*time.Second))),
testutils.NewEvent(t, "m.room.message", alice, map[string]interface{}{"body": "A"}, testutils.WithTimestamp(ts.Add(4*time.Second))),
testutils.NewEvent(t, "m.room.message", alice, map[string]interface{}{"body": "B"}, testutils.WithTimestamp(ts.Add(5*time.Second))),
testutils.NewEvent(t, "m.room.message", alice, map[string]interface{}{"body": "C"}, testutils.WithTimestamp(ts.Add(6*time.Second))),
}...),
}
allRoomIDs[i] = allRooms[i].roomID
if ts.After(latestTimestamp) {
latestTimestamp = ts.Add(10 * time.Second)
}
allRoomMatchers[allRooms[i].roomID] = []m.RoomMatcher{
m.MatchRoomInitial(true),
m.MatchRoomName(allRooms[i].name),
m.MatchRoomTimelineMostRecent(numTimelineEventsPerRoom, allRooms[i].events),
}
}
v2.addAccount(t, alice, aliceToken)
v2.queueResponse(alice, sync2.SyncResponse{
Rooms: sync2.SyncRoomsResponse{
Join: v2JoinTimeline(allRooms...),
},
})
// fetch all the rooms!
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 3}, // these get ignored
},
SlowGetAllRooms: &boolTrue,
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(numTimelineEventsPerRoom),
},
}},
})
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3SyncOp(0, int64(len(allRooms)-1), allRoomIDs, true),
)), m.MatchRoomSubscriptionsStrict(allRoomMatchers))
// now redo this but with a room name filter
res = v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 3}, // these get ignored
},
SlowGetAllRooms: &boolTrue,
Filters: &sync3.RequestFilters{
RoomNameFilter: "My Room 1", // returns 1,10,11,12,13,14 etc
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(numTimelineEventsPerRoom),
},
}},
})
// remove rooms that don't have a leading 1 index as they should be filtered out
for i := 0; i < 10; i++ {
if i == 1 {
continue
}
delete(allRoomMatchers, allRooms[i].roomID)
}
roomIDs := make([]string, 0, len(allRoomMatchers))
for roomID := range allRoomMatchers {
roomIDs = append(roomIDs, roomID)
}
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRoomMatchers)), m.MatchV3Ops(
m.MatchV3SyncOp(0, int64(len(allRoomMatchers)-1), roomIDs, true),
)), m.MatchRoomSubscriptionsStrict(allRoomMatchers))
t.Run("Newly joined rooms get all state", func(t *testing.T) {
ts := latestTimestamp
roomName := "My Room 111111"
newRoom := roomEvents{
add extensions for typing and receipts; bugfixes and additional perf improvements Features: - Add `typing` extension. - Add `receipts` extension. - Add comprehensive prometheus `/metrics` activated via `SYNCV3_PROM`. - Add `SYNCV3_PPROF` support. - Add `by_notification_level` sort order. - Add `include_old_rooms` support. - Add support for `$ME` and `$LAZY`. - Add correct filtering when `*,*` is used as `required_state`. - Add `num_live` to each room response to indicate how many timeline entries are live. Bug fixes: - Use a stricter comparison function on ranges: fixes an issue whereby UTs fail on go1.19 due to change in sorting algorithm. - Send back an `errcode` on HTTP errors (e.g expired sessions). - Remove `unsigned.txn_id` on insertion into the DB. Otherwise other users would see other users txn IDs :( - Improve range delta algorithm: previously it didn't handle cases like `[0,20] -> [20,30]` and would panic. - Send HTTP 400 for invalid range requests. - Don't publish no-op unread counts which just adds extra noise. - Fix leaking DB connections which could eventually consume all available connections. - Ensure we always unblock WaitUntilInitialSync even on invalid access tokens. Other code relies on WaitUntilInitialSync() actually returning at _some_ point e.g on startup we have N workers which bound the number of concurrent pollers made at any one time, we need to not just hog a worker forever. Improvements: - Greatly improve startup times of sync3 handlers by improving `JoinedRoomsTracker`: a modest amount of data would take ~28s to create the handler, now it takes 4s. - Massively improve initial initial v3 sync times, by refactoring `JoinedRoomsTracker`, from ~47s to <1s. - Add `SlidingSyncUntil...` in tests to reduce races. - Tweak the API shape of JoinedUsersForRoom to reduce state block processing time for large rooms from 63s to 39s. - Add trace task for initial syncs. - Include the proxy version in UA strings. - HTTP errors now wait 1s before returning to stop clients tight-looping on error. - Pending event buffer is now 2000. - Index the room ID first to cull the most events when returning timeline entries. Speeds up `SelectLatestEventsBetween` by a factor of 8. - Remove cancelled `m.room_key_requests` from the to-device inbox. Cuts down the amount of events in the inbox by ~94% for very large (20k+) inboxes, ~50% for moderate sized (200 events) inboxes. Adds book-keeping to remember the unacked to-device position for each client.
2022-12-14 18:53:55 +00:00
roomID: fmt.Sprintf("!TestSlowGetAllRoomsInitial_%dNEW:localhost", len(allRooms)),
name: roomName,
events: append(createRoomState(t, alice, ts), []json.RawMessage{
testutils.NewStateEvent(t, "m.room.name", "", alice, map[string]interface{}{"name": roomName}, testutils.WithTimestamp(ts.Add(3*time.Second))),
testutils.NewEvent(t, "m.room.message", alice, map[string]interface{}{"body": "A"}, testutils.WithTimestamp(ts.Add(4*time.Second))),
testutils.NewEvent(t, "m.room.message", alice, map[string]interface{}{"body": "B"}, testutils.WithTimestamp(ts.Add(5*time.Second))),
testutils.NewEvent(t, "m.room.message", alice, map[string]interface{}{"body": "C"}, testutils.WithTimestamp(ts.Add(6*time.Second))),
}...),
}
v2.queueResponse(alice, sync2.SyncResponse{
Rooms: sync2.SyncRoomsResponse{
Join: v2JoinTimeline(newRoom),
},
})
v2.waitUntilEmpty(t, alice)
// reuse the position from the room name filter test, we should get this new room
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: map[string]sync3.RequestList{},
})
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRoomMatchers)+1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
newRoom.roomID: {
m.MatchRoomInitial(true),
m.MatchRoomName(roomName),
m.MatchRoomTimelineMostRecent(numTimelineEventsPerRoom, newRoom.events),
},
}))
})
t.Run("live updates just send event", func(t *testing.T) {
newEvent := testutils.NewEvent(t, "m.room.message", alice, map[string]interface{}{"body": "D"}, testutils.WithTimestamp(latestTimestamp.Add(6*time.Second)))
v2.queueResponse(alice, sync2.SyncResponse{
Rooms: sync2.SyncRoomsResponse{
Join: v2JoinTimeline(roomEvents{
roomID: allRooms[11].roomID, // 11 to be caught in the room name filter
events: []json.RawMessage{newEvent},
}),
},
})
v2.waitUntilEmpty(t, alice)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: map[string]sync3.RequestList{},
})
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRoomMatchers)+1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
allRooms[11].roomID: {
m.MatchRoomInitial(false),
m.MatchRoomTimelineMostRecent(1, []json.RawMessage{newEvent}),
},
}))
})
}