mirror of
https://github.com/element-hq/dendrite.git
synced 2025-09-13 12:52:24 +03:00
Optimise getting local members and membership counts (#3150)
The previous version was getting **ALL** membership events (as `ClientEvents`, so going through `NewEventFromTrustedJSONWithID`) for a given room. Now we are querying only locally joined users as `ClientEvents`, which should **significantly** reduce allocations. Take for example a large room with 2k membership events, but only 1 local user - avoiding 1999 `NewEventFromTrustedJSONWithID` calls just to calculate the `roomSize` which we can also query by other means. This is also getting called for every `OutputRoomEvent` in the userAPI. Benchmark with 1 local user and 100 remote users. ``` pkg: github.com/matrix-org/dendrite/userapi/consumers cpu: 12th Gen Intel(R) Core(TM) i5-12500H │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ LocalRoomMembers-16 375.9µ ± 7% 327.6µ ± 6% -12.85% (p=0.000 n=10) │ old.txt │ new.txt │ │ B/op │ B/op vs base │ LocalRoomMembers-16 79.426Ki ± 0% 8.507Ki ± 0% -89.29% (p=0.000 n=10) │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ LocalRoomMembers-16 1015.0 ± 0% 277.0 ± 0% -72.71% (p=0.000 n=10) ```
This commit is contained in:
parent
f12982472c
commit
5267cc0f54
4 changed files with 109 additions and 19 deletions
|
@ -405,18 +405,25 @@ func newLocalMembership(event *synctypes.ClientEvent) (*localMembership, error)
|
|||
// localRoomMembers fetches the current local members of a room, and
|
||||
// the total number of members.
|
||||
func (s *OutputRoomEventConsumer) localRoomMembers(ctx context.Context, roomID string) ([]*localMembership, int, error) {
|
||||
// Get only locally joined users to avoid unmarshalling and caching
|
||||
// membership events we only use to calculate the room size.
|
||||
req := &rsapi.QueryMembershipsForRoomRequest{
|
||||
RoomID: roomID,
|
||||
JoinedOnly: true,
|
||||
LocalOnly: true,
|
||||
}
|
||||
var res rsapi.QueryMembershipsForRoomResponse
|
||||
|
||||
// XXX: This could potentially race if the state for the event is not known yet
|
||||
// e.g. the event came over federation but we do not have the full state persisted.
|
||||
if err := s.rsAPI.QueryMembershipsForRoom(ctx, req, &res); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Since we only queried locally joined users above,
|
||||
// we also need to ask the roomserver about the joined user count.
|
||||
totalCount, err := s.rsAPI.JoinedUserCount(ctx, roomID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var members []*localMembership
|
||||
for _, event := range res.JoinEvents {
|
||||
// Filter out invalid join events
|
||||
|
@ -426,31 +433,18 @@ func (s *OutputRoomEventConsumer) localRoomMembers(ctx context.Context, roomID s
|
|||
if *event.StateKey == "" {
|
||||
continue
|
||||
}
|
||||
_, serverName, err := gomatrixserverlib.SplitID('@', *event.StateKey)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("failed to get servername from statekey")
|
||||
continue
|
||||
}
|
||||
// Only get memberships for our server
|
||||
if serverName != s.serverName {
|
||||
continue
|
||||
}
|
||||
// We're going to trust the Query from above to really just return
|
||||
// local users
|
||||
member, err := newLocalMembership(&event)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("Parsing MemberContent")
|
||||
continue
|
||||
}
|
||||
if member.Membership != spec.Join {
|
||||
continue
|
||||
}
|
||||
if member.Domain != s.cfg.Matrix.ServerName {
|
||||
continue
|
||||
}
|
||||
|
||||
members = append(members, member)
|
||||
}
|
||||
|
||||
return members, len(res.JoinEvents), nil
|
||||
return members, totalCount, nil
|
||||
}
|
||||
|
||||
// roomName returns the name in the event (if type==m.room.name), or
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue