fix: BREAKING! Cache members for encrypted rooms in database

This fixes a huge performance
leak as for every encrypted room
the whole member list is loaded
every time you start the client.
This also changes the default value of `cache` in
this method to if the room is encrypted or not.
For encrypted rooms we always want a complete
member list locally.
This commit is contained in:
Krille 2024-11-11 09:19:49 +01:00
parent 52381cecfb
commit f46e76295c
No known key found for this signature in database
GPG Key ID: E067ECD60F1A0652
3 changed files with 76 additions and 18 deletions

View File

@ -1816,19 +1816,62 @@ class FakeMatrixApi extends BaseClient {
'/client/v3/rooms/!localpart%3Aserver.abc/members': (var req) => { '/client/v3/rooms/!localpart%3Aserver.abc/members': (var req) => {
'chunk': [ 'chunk': [
{ {
'type': 'm.room.member',
'content': {'membership': 'join', 'displayname': 'YOU'},
'sender': '@test:fakeServer.notExisting',
'state_key': '@test:fakeServer.notExisting',
'room_id': '!localpart%3Aserver.abc',
'event_id': '§143273582443PhrSn2:example.org',
'origin_server_ts': 1432735824653,
'unsigned': {'age': 1234},
},
{
'type': 'm.room.member',
'content': {
'membership': 'join',
'displayname': 'Alice Margatroid',
},
'sender': '@alice:matrix.org',
'state_key': '@alice:matrix.org',
'room_id': '!localpart%3Aserver.abc',
'event_id': '§143273582443PhrSn3:example.org',
'origin_server_ts': 1432735824653,
'unsigned': {'age': 1234},
},
{
'type': 'm.room.member',
'content': {'membership': 'invite', 'displayname': 'Bob'},
'sender': '@bob:example.com',
'state_key': '@bob:example.com',
'room_id': '!localpart%3Aserver.abc',
'event_id': '§143273582443PhrSn4:example.org',
'origin_server_ts': 1432735824653,
'unsigned': {'age': 1234},
},
{
'type': 'm.room.member',
'content': {'membership': 'invite', 'displayname': 'Charley'},
'sender': '@charley:example.org',
'state_key': '@charley:example.org',
'room_id': '!localpart%3Aserver.abc',
'event_id': '§143273582443PhrSn5:example.org',
'origin_server_ts': 1432735824653,
'unsigned': {'age': 1234},
},
{
'type': 'm.room.member',
'content': { 'content': {
'membership': 'join', 'membership': 'join',
'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF',
'displayname': 'Alice Margatroid', 'displayname': 'Alice Margatroid',
}, },
'type': 'm.room.member',
'event_id': '§143273582443PhrSn:example.org',
'room_id': '!636q39766251:example.com',
'sender': '@example:example.org', 'sender': '@example:example.org',
'state_key': '@alice:example.org',
'room_id': '!localpart%3Aserver.abc',
'event_id': '§143273582443PhrSn6:example.org',
'origin_server_ts': 1432735824653, 'origin_server_ts': 1432735824653,
'unsigned': {'age': 1234}, 'unsigned': {'age': 1234},
'state_key': '@alice:example.org', },
}
], ],
}, },
'/client/v3/pushrules/global/content/nocake': (var req) => { '/client/v3/pushrules/global/content/nocake': (var req) => {

View File

@ -1607,6 +1607,8 @@ class Room {
/// [[Membership.join, Membership.invite, Membership.knock]] /// [[Membership.join, Membership.invite, Membership.knock]]
/// Set [cache] to `false` if you do not want to cache the users in memory /// Set [cache] to `false` if you do not want to cache the users in memory
/// for this session which is highly recommended for large public rooms. /// for this session which is highly recommended for large public rooms.
/// By default users are only cached in encrypted rooms as encrypted rooms
/// need a full member list.
Future<List<User>> requestParticipants([ Future<List<User>> requestParticipants([
List<Membership> membershipFilter = const [ List<Membership> membershipFilter = const [
Membership.join, Membership.join,
@ -1614,7 +1616,7 @@ class Room {
Membership.knock, Membership.knock,
], ],
bool suppressWarning = false, bool suppressWarning = false,
bool cache = true, bool? cache,
]) async { ]) async {
if (!participantListComplete || partial) { if (!participantListComplete || partial) {
// we aren't fully loaded, maybe the users are in the database // we aren't fully loaded, maybe the users are in the database
@ -1633,6 +1635,8 @@ class Room {
return getParticipants(membershipFilter); return getParticipants(membershipFilter);
} }
cache ??= encrypted;
final memberCount = summary.mJoinedMemberCount; final memberCount = summary.mJoinedMemberCount;
if (!suppressWarning && cache && memberCount != null && memberCount > 100) { if (!suppressWarning && cache && memberCount != null && memberCount > 100) {
Logs().w(''' Logs().w('''
@ -1651,6 +1655,14 @@ class Room {
if (cache) { if (cache) {
for (final user in users) { for (final user in users) {
setState(user); // at *least* cache this in-memory setState(user); // at *least* cache this in-memory
await client.database?.storeEventUpdate(
EventUpdate(
roomID: id,
type: EventUpdateType.state,
content: user.toJson(),
),
client,
);
} }
} }

View File

@ -657,14 +657,17 @@ void main() {
}); });
test('requestParticipants', () async { test('requestParticipants', () async {
final participants = await room.requestParticipants(); final oldParticipants = room.getParticipants();
expect(participants.length, 4); expect(oldParticipants.length, 4);
final user = participants.singleWhere((u) => u.id == '@alice:matrix.org'); room.summary.mJoinedMemberCount = 5;
expect(user.id, '@alice:matrix.org'); final fetchedParticipants = await room.requestParticipants(
expect(user.displayName, 'Alice Margatroid'); const [Membership.join, Membership.invite, Membership.knock],
expect(user.membership, Membership.join); true,
//expect(user.avatarUrl.toString(), 'mxc://example.org/SEsfnsuifSDFSSEF'); true,
expect(user.room.id, '!localpart:server.abc'); );
final newParticipants = room.getParticipants();
expect(oldParticipants.length < newParticipants.length, true);
expect(fetchedParticipants.length, newParticipants.length);
}); });
test('calcEncryptionHealthState', () async { test('calcEncryptionHealthState', () async {
@ -823,7 +826,7 @@ void main() {
test('getParticipants', () async { test('getParticipants', () async {
var userList = room.getParticipants(); var userList = room.getParticipants();
expect(userList.length, 4); expect(userList.length, 5);
// add new user // add new user
room.setState( room.setState(
Event( Event(
@ -837,8 +840,8 @@ void main() {
), ),
); );
userList = room.getParticipants(); userList = room.getParticipants();
expect(userList.length, 5); expect(userList.length, 6);
expect(userList[4].displayName, 'alice'); expect(userList[5].displayName, 'alice');
}); });
test('addToDirectChat', () async { test('addToDirectChat', () async {
@ -1660,7 +1663,7 @@ void main() {
matrixToLink = await room.matrixToInviteLink(); matrixToLink = await room.matrixToInviteLink();
expect( expect(
matrixToLink.toString(), matrixToLink.toString(),
'https://matrix.to/#/!localpart%3Aserver.abc?via=fakeServer.notExisting&via=matrix.org&via=test.abc', 'https://matrix.to/#/!localpart%3Aserver.abc?via=fakeServer.notExisting&via=matrix.org&via=example.org',
); );
}); });