Skip to content

Commit add73ec

Browse files
authored
Speed up loading ACLs on startup (#3469)
Currently d.m.org struggles to start, as it is fetching several thousand ACLs on startup. The reason is that we're loading the entire room state into memory, only to filter out the majority of it, because we only care about certain types. This change filters the types (tuples) directly when querying the database, so we don't end up with unneeded state.
1 parent 294f3d2 commit add73ec

File tree

4 files changed

+88
-13
lines changed

4 files changed

+88
-13
lines changed

roomserver/acls/acls.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import (
1414
"regexp"
1515
"strings"
1616
"sync"
17+
"time"
1718

1819
"github.com/element-hq/dendrite/roomserver/storage/tables"
19-
"github.com/matrix-org/gomatrixserverlib"
2020
"github.com/matrix-org/gomatrixserverlib/spec"
2121
"github.com/sirupsen/logrus"
2222
)
@@ -27,9 +27,8 @@ type ServerACLDatabase interface {
2727
// RoomsWithACLs returns all room IDs for rooms with ACLs
2828
RoomsWithACLs(ctx context.Context) ([]string, error)
2929

30-
// GetBulkStateContent returns all state events which match a given room ID and a given state key tuple. Both must be satisfied for a match.
31-
// If a tuple has the StateKey of '*' and allowWildcards=true then all state events with the EventType should be returned.
32-
GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error)
30+
// GetBulkStateACLs returns all server ACLs for the given rooms.
31+
GetBulkStateACLs(ctx context.Context, roomIDs []string) ([]tables.StrippedEvent, error)
3332
}
3433

3534
type ServerACLs struct {
@@ -40,6 +39,16 @@ type ServerACLs struct {
4039
}
4140

4241
func NewServerACLs(db ServerACLDatabase) *ServerACLs {
42+
// Add some logging, as this can take a while on larger instances.
43+
logrus.Infof("Loading server ACLs...")
44+
start := time.Now()
45+
aclCount := 0
46+
defer func() {
47+
logrus.WithFields(logrus.Fields{
48+
"duration": time.Since(start),
49+
"acls": aclCount,
50+
}).Info("Finished loading server ACLs")
51+
}()
4352
ctx := context.TODO()
4453
acls := &ServerACLs{
4554
acls: make(map[string]*serverACL),
@@ -48,20 +57,25 @@ func NewServerACLs(db ServerACLDatabase) *ServerACLs {
4857
aclRegexCache: make(map[string]**regexp.Regexp, 100),
4958
}
5059

51-
// Look up all of the rooms that the current state server knows about.
60+
// Look up all rooms with ACLs.
5261
rooms, err := db.RoomsWithACLs(ctx)
5362
if err != nil {
5463
logrus.WithError(err).Fatalf("Failed to get known rooms")
5564
}
56-
// For each room, let's see if we have a server ACL state event. If we
57-
// do then we'll process it into memory so that we have the regexes to
58-
// hand.
5965

60-
events, err := db.GetBulkStateContent(ctx, rooms, []gomatrixserverlib.StateKeyTuple{{EventType: MRoomServerACL, StateKey: ""}}, false)
66+
// No rooms with ACLs, don't bother hitting the DB again.
67+
if len(rooms) == 0 {
68+
return acls
69+
}
70+
71+
// Get ACLs for the required rooms, bail if we are unable to get them.
72+
events, err := db.GetBulkStateACLs(ctx, rooms)
6173
if err != nil {
62-
logrus.WithError(err).Errorf("Failed to get server ACLs for all rooms: %q", err)
74+
logrus.WithError(err).Fatal("Failed to get server ACLs for all rooms")
6375
}
6476

77+
aclCount = len(events)
78+
6579
for _, event := range events {
6680
acls.OnServerACLUpdate(event)
6781
}

roomserver/acls/acls_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"testing"
1313

1414
"github.com/element-hq/dendrite/roomserver/storage/tables"
15-
"github.com/matrix-org/gomatrixserverlib"
1615
"github.com/matrix-org/gomatrixserverlib/spec"
1716
"github.com/stretchr/testify/assert"
1817
)
@@ -108,11 +107,11 @@ var (
108107

109108
type dummyACLDB struct{}
110109

111-
func (d dummyACLDB) RoomsWithACLs(ctx context.Context) ([]string, error) {
110+
func (d dummyACLDB) RoomsWithACLs(_ context.Context) ([]string, error) {
112111
return []string{"1", "2"}, nil
113112
}
114113

115-
func (d dummyACLDB) GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error) {
114+
func (d dummyACLDB) GetBulkStateACLs(_ context.Context, _ []string) ([]tables.StrippedEvent, error) {
116115
return []tables.StrippedEvent{
117116
{
118117
RoomID: "1",

roomserver/storage/interface.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ type Database interface {
187187

188188
// RoomsWithACLs returns all room IDs for rooms with ACLs
189189
RoomsWithACLs(ctx context.Context) ([]string, error)
190+
// GetBulkStateACLs returns all server ACLs for the given rooms.
191+
GetBulkStateACLs(ctx context.Context, roomIDs []string) ([]tables.StrippedEvent, error)
190192
QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID string, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error)
191193
QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error)
192194
AdminDeleteEventReport(ctx context.Context, reportID uint64) error

roomserver/storage/shared/storage.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,63 @@ func (d *Database) GetRoomsByMembership(ctx context.Context, userID spec.UserID,
14371437
return roomIDs, nil
14381438
}
14391439

1440+
// GetBulkStateACLs is a lighter weight form of GetBulkStateContent, which only returns ACL state events.
1441+
func (d *Database) GetBulkStateACLs(ctx context.Context, roomIDs []string) ([]tables.StrippedEvent, error) {
1442+
tuples := []gomatrixserverlib.StateKeyTuple{{EventType: "m.room.server_acl", StateKey: ""}}
1443+
1444+
var eventNIDs []types.EventNID
1445+
eventNIDToVer := make(map[types.EventNID]gomatrixserverlib.RoomVersion)
1446+
// TODO: This feels like this is going to be really slow...
1447+
for _, roomID := range roomIDs {
1448+
roomInfo, err2 := d.roomInfo(ctx, nil, roomID)
1449+
if err2 != nil {
1450+
return nil, fmt.Errorf("GetBulkStateACLs: failed to load room info for room %s : %w", roomID, err2)
1451+
}
1452+
// for unknown rooms or rooms which we don't have the current state, skip them.
1453+
if roomInfo == nil || roomInfo.IsStub() {
1454+
continue
1455+
}
1456+
// No querier needed, as we don't actually do state resolution
1457+
stateRes := state.NewStateResolution(d, roomInfo, nil)
1458+
entries, err2 := stateRes.LoadStateAtSnapshotForStringTuples(ctx, roomInfo.StateSnapshotNID(), tuples)
1459+
if err2 != nil {
1460+
return nil, fmt.Errorf("GetBulkStateACLs: failed to load state for room %s : %w", roomID, err2)
1461+
}
1462+
for _, entry := range entries {
1463+
eventNIDs = append(eventNIDs, entry.EventNID)
1464+
eventNIDToVer[entry.EventNID] = roomInfo.RoomVersion
1465+
}
1466+
}
1467+
eventIDs, err := d.EventsTable.BulkSelectEventID(ctx, nil, eventNIDs)
1468+
if err != nil {
1469+
eventIDs = map[types.EventNID]string{}
1470+
}
1471+
events, err := d.EventJSONTable.BulkSelectEventJSON(ctx, nil, eventNIDs)
1472+
if err != nil {
1473+
return nil, fmt.Errorf("GetBulkStateACLs: failed to load event JSON for event nids: %w", err)
1474+
}
1475+
result := make([]tables.StrippedEvent, len(events))
1476+
for i := range events {
1477+
roomVer := eventNIDToVer[events[i].EventNID]
1478+
verImpl, err := gomatrixserverlib.GetRoomVersion(roomVer)
1479+
if err != nil {
1480+
return nil, err
1481+
}
1482+
ev, err := verImpl.NewEventFromTrustedJSONWithEventID(eventIDs[events[i].EventNID], events[i].EventJSON, false)
1483+
if err != nil {
1484+
return nil, fmt.Errorf("GetBulkStateACLs: failed to load event JSON for event NID %v : %w", events[i].EventNID, err)
1485+
}
1486+
result[i] = tables.StrippedEvent{
1487+
EventType: ev.Type(),
1488+
RoomID: ev.RoomID().String(),
1489+
StateKey: *ev.StateKey(),
1490+
ContentValue: tables.ExtractContentValue(&types.HeaderedEvent{PDU: ev}),
1491+
}
1492+
}
1493+
1494+
return result, nil
1495+
}
1496+
14401497
// GetBulkStateContent returns all state events which match a given room ID and a given state key tuple. Both must be satisfied for a match.
14411498
// If a tuple has the StateKey of '*' and allowWildcards=true then all state events with the EventType should be returned.
14421499
func (d *Database) GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error) {
@@ -1487,6 +1544,9 @@ func (d *Database) GetBulkStateContent(ctx context.Context, roomIDs []string, tu
14871544
if roomInfo == nil || roomInfo.IsStub() {
14881545
continue
14891546
}
1547+
// TODO: This is inefficient as we're loading the _entire_ state, but only care about a subset of it.
1548+
// This is why GetBulkStateACLs exists. LoadStateAtSnapshotForStringTuples only loads the state we care about,
1549+
// but is unfortunately not able to load wildcard state keys.
14901550
entries, err2 := d.loadStateAtSnapshot(ctx, roomInfo.StateSnapshotNID())
14911551
if err2 != nil {
14921552
return nil, fmt.Errorf("GetBulkStateContent: failed to load state for room %s : %w", roomID, err2)

0 commit comments

Comments
 (0)