Add more optimised code path for checking if we're in a room (#1909)
* Add more optimised code path for checking if we're in a room * Fix database queries * Fix federation API test * Fix logging * Review comments * Make separate API call for room membership
This commit is contained in:
parent
3e50bac944
commit
c8408a6387
|
@ -573,6 +573,23 @@ func (t *txnReq) processEvent(ctx context.Context, e *gomatrixserverlib.Event) e
|
||||||
logger := util.GetLogger(ctx).WithField("event_id", e.EventID()).WithField("room_id", e.RoomID())
|
logger := util.GetLogger(ctx).WithField("event_id", e.EventID()).WithField("room_id", e.RoomID())
|
||||||
t.work = "" // reset from previous event
|
t.work = "" // reset from previous event
|
||||||
|
|
||||||
|
// Ask the roomserver if we know about the room and/or if we're joined
|
||||||
|
// to it. If we aren't then we won't bother processing the event.
|
||||||
|
joinedReq := api.QueryServerJoinedToRoomRequest{
|
||||||
|
RoomID: e.RoomID(),
|
||||||
|
}
|
||||||
|
var joinedRes api.QueryServerJoinedToRoomResponse
|
||||||
|
if err := t.rsAPI.QueryServerJoinedToRoom(ctx, &joinedReq, &joinedRes); err != nil {
|
||||||
|
return fmt.Errorf("t.rsAPI.QueryServerJoinedToRoom: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !joinedRes.RoomExists || !joinedRes.IsInRoom {
|
||||||
|
// We don't believe we're a member of this room, therefore there's
|
||||||
|
// no point in wasting work trying to figure out what to do with
|
||||||
|
// missing auth or prev events. Drop the event.
|
||||||
|
return roomNotFoundError{e.RoomID()}
|
||||||
|
}
|
||||||
|
|
||||||
// Work out if the roomserver knows everything it needs to know to auth
|
// Work out if the roomserver knows everything it needs to know to auth
|
||||||
// the event. This includes the prev_events and auth_events.
|
// the event. This includes the prev_events and auth_events.
|
||||||
// NOTE! This is going to include prev_events that have an empty state
|
// NOTE! This is going to include prev_events that have an empty state
|
||||||
|
@ -589,16 +606,6 @@ func (t *txnReq) processEvent(ctx context.Context, e *gomatrixserverlib.Event) e
|
||||||
return fmt.Errorf("t.rsAPI.QueryMissingAuthPrevEvents: %w", err)
|
return fmt.Errorf("t.rsAPI.QueryMissingAuthPrevEvents: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !stateResp.RoomExists {
|
|
||||||
// TODO: When synapse receives a message for a room it is not in it
|
|
||||||
// asks the remote server for the state of the room so that it can
|
|
||||||
// check if the remote server knows of a join "m.room.member" event
|
|
||||||
// that this server is unaware of.
|
|
||||||
// However generally speaking we should reject events for rooms we
|
|
||||||
// aren't a member of.
|
|
||||||
return roomNotFoundError{e.RoomID()}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare a map of all the events we already had before this point, so
|
// Prepare a map of all the events we already had before this point, so
|
||||||
// that we don't send them to the roomserver again.
|
// that we don't send them to the roomserver again.
|
||||||
for _, eventID := range append(e.AuthEventIDs(), e.PrevEventIDs()...) {
|
for _, eventID := range append(e.AuthEventIDs(), e.PrevEventIDs()...) {
|
||||||
|
|
|
@ -190,7 +190,9 @@ func (t *testRoomserverAPI) QueryServerJoinedToRoom(
|
||||||
request *api.QueryServerJoinedToRoomRequest,
|
request *api.QueryServerJoinedToRoomRequest,
|
||||||
response *api.QueryServerJoinedToRoomResponse,
|
response *api.QueryServerJoinedToRoomResponse,
|
||||||
) error {
|
) error {
|
||||||
return fmt.Errorf("not implemented")
|
response.RoomExists = true
|
||||||
|
response.IsInRoom = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query whether a server is allowed to see an event
|
// Query whether a server is allowed to see an event
|
||||||
|
|
|
@ -170,7 +170,8 @@ type QueryMembershipsForRoomResponse struct {
|
||||||
|
|
||||||
// QueryServerJoinedToRoomRequest is a request to QueryServerJoinedToRoom
|
// QueryServerJoinedToRoomRequest is a request to QueryServerJoinedToRoom
|
||||||
type QueryServerJoinedToRoomRequest struct {
|
type QueryServerJoinedToRoomRequest struct {
|
||||||
// Server name of the server to find
|
// Server name of the server to find. If not specified, we will
|
||||||
|
// default to checking if the local server is joined.
|
||||||
ServerName gomatrixserverlib.ServerName `json:"server_name"`
|
ServerName gomatrixserverlib.ServerName `json:"server_name"`
|
||||||
// ID of the room to see if we are still joined to
|
// ID of the room to see if we are still joined to
|
||||||
RoomID string `json:"room_id"`
|
RoomID string `json:"room_id"`
|
||||||
|
@ -182,7 +183,8 @@ type QueryServerJoinedToRoomResponse struct {
|
||||||
RoomExists bool `json:"room_exists"`
|
RoomExists bool `json:"room_exists"`
|
||||||
// True if we still believe that we are participating in the room
|
// True if we still believe that we are participating in the room
|
||||||
IsInRoom bool `json:"is_in_room"`
|
IsInRoom bool `json:"is_in_room"`
|
||||||
// List of servers that are also in the room
|
// List of servers that are also in the room. This will not be populated
|
||||||
|
// if the queried ServerName is the local server name.
|
||||||
ServerNames []gomatrixserverlib.ServerName `json:"server_names"`
|
ServerNames []gomatrixserverlib.ServerName `json:"server_names"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ func NewRoomserverAPI(
|
||||||
Queryer: &query.Queryer{
|
Queryer: &query.Queryer{
|
||||||
DB: roomserverDB,
|
DB: roomserverDB,
|
||||||
Cache: caches,
|
Cache: caches,
|
||||||
|
ServerName: cfg.Matrix.ServerName,
|
||||||
ServerACLs: serverACLs,
|
ServerACLs: serverACLs,
|
||||||
},
|
},
|
||||||
Inputer: &input.Inputer{
|
Inputer: &input.Inputer{
|
||||||
|
|
|
@ -36,6 +36,7 @@ import (
|
||||||
type Queryer struct {
|
type Queryer struct {
|
||||||
DB storage.Database
|
DB storage.Database
|
||||||
Cache caching.RoomServerCaches
|
Cache caching.RoomServerCaches
|
||||||
|
ServerName gomatrixserverlib.ServerName
|
||||||
ServerACLs *acls.ServerACLs
|
ServerACLs *acls.ServerACLs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,6 +329,16 @@ func (r *Queryer) QueryServerJoinedToRoom(
|
||||||
}
|
}
|
||||||
response.RoomExists = true
|
response.RoomExists = true
|
||||||
|
|
||||||
|
if request.ServerName == r.ServerName || request.ServerName == "" {
|
||||||
|
var joined bool
|
||||||
|
joined, err = r.DB.GetLocalServerInRoom(ctx, info.RoomNID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("r.DB.GetLocalServerInRoom: %w", err)
|
||||||
|
}
|
||||||
|
response.IsInRoom = joined
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
eventNIDs, err := r.DB.GetMembershipEventNIDsForRoom(ctx, info.RoomNID, true, false)
|
eventNIDs, err := r.DB.GetMembershipEventNIDsForRoom(ctx, info.RoomNID, true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("r.DB.GetMembershipEventNIDsForRoom: %w", err)
|
return fmt.Errorf("r.DB.GetMembershipEventNIDsForRoom: %w", err)
|
||||||
|
|
|
@ -154,6 +154,8 @@ type Database interface {
|
||||||
GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error)
|
GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error)
|
||||||
// JoinedUsersSetInRooms returns all joined users in the rooms given, along with the count of how many times they appear.
|
// JoinedUsersSetInRooms returns all joined users in the rooms given, along with the count of how many times they appear.
|
||||||
JoinedUsersSetInRooms(ctx context.Context, roomIDs []string) (map[string]int, error)
|
JoinedUsersSetInRooms(ctx context.Context, roomIDs []string) (map[string]int, error)
|
||||||
|
// GetLocalServerInRoom returns true if we think we're in a given room or false otherwise.
|
||||||
|
GetLocalServerInRoom(ctx context.Context, roomNID types.RoomNID) (bool, error)
|
||||||
// GetKnownUsers searches all users that userID knows about.
|
// GetKnownUsers searches all users that userID knows about.
|
||||||
GetKnownUsers(ctx context.Context, userID, searchString string, limit int) ([]string, error)
|
GetKnownUsers(ctx context.Context, userID, searchString string, limit int) ([]string, error)
|
||||||
// GetKnownRooms returns a list of all rooms we know about.
|
// GetKnownRooms returns a list of all rooms we know about.
|
||||||
|
|
|
@ -124,6 +124,14 @@ var selectKnownUsersSQL = "" +
|
||||||
" SELECT DISTINCT room_nid FROM roomserver_membership WHERE target_nid=$1 AND membership_nid = " + fmt.Sprintf("%d", tables.MembershipStateJoin) +
|
" SELECT DISTINCT room_nid FROM roomserver_membership WHERE target_nid=$1 AND membership_nid = " + fmt.Sprintf("%d", tables.MembershipStateJoin) +
|
||||||
") AND membership_nid = " + fmt.Sprintf("%d", tables.MembershipStateJoin) + " AND event_state_key LIKE $2 LIMIT $3"
|
") AND membership_nid = " + fmt.Sprintf("%d", tables.MembershipStateJoin) + " AND event_state_key LIKE $2 LIMIT $3"
|
||||||
|
|
||||||
|
// selectLocalServerInRoomSQL is an optimised case for checking if we, the local server,
|
||||||
|
// are in the room by using the target_local column of the membership table. Normally when
|
||||||
|
// we want to know if a server is in a room, we have to unmarshal the entire room state which
|
||||||
|
// is expensive. The presence of a single row from this query suggests we're still in the
|
||||||
|
// room, no rows returned suggests we aren't.
|
||||||
|
const selectLocalServerInRoomSQL = "" +
|
||||||
|
"SELECT room_nid FROM roomserver_membership WHERE target_local = true AND membership_nid = $1 AND room_nid = $2 LIMIT 1"
|
||||||
|
|
||||||
type membershipStatements struct {
|
type membershipStatements struct {
|
||||||
insertMembershipStmt *sql.Stmt
|
insertMembershipStmt *sql.Stmt
|
||||||
selectMembershipForUpdateStmt *sql.Stmt
|
selectMembershipForUpdateStmt *sql.Stmt
|
||||||
|
@ -137,6 +145,7 @@ type membershipStatements struct {
|
||||||
selectJoinedUsersSetForRoomsStmt *sql.Stmt
|
selectJoinedUsersSetForRoomsStmt *sql.Stmt
|
||||||
selectKnownUsersStmt *sql.Stmt
|
selectKnownUsersStmt *sql.Stmt
|
||||||
updateMembershipForgetRoomStmt *sql.Stmt
|
updateMembershipForgetRoomStmt *sql.Stmt
|
||||||
|
selectLocalServerInRoomStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMembershipTable(db *sql.DB) error {
|
func createMembershipTable(db *sql.DB) error {
|
||||||
|
@ -160,6 +169,7 @@ func prepareMembershipTable(db *sql.DB) (tables.Membership, error) {
|
||||||
{&s.selectJoinedUsersSetForRoomsStmt, selectJoinedUsersSetForRoomsSQL},
|
{&s.selectJoinedUsersSetForRoomsStmt, selectJoinedUsersSetForRoomsSQL},
|
||||||
{&s.selectKnownUsersStmt, selectKnownUsersSQL},
|
{&s.selectKnownUsersStmt, selectKnownUsersSQL},
|
||||||
{&s.updateMembershipForgetRoomStmt, updateMembershipForgetRoom},
|
{&s.updateMembershipForgetRoomStmt, updateMembershipForgetRoom},
|
||||||
|
{&s.selectLocalServerInRoomStmt, selectLocalServerInRoomSQL},
|
||||||
}.Prepare(db)
|
}.Prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,3 +334,16 @@ func (s *membershipStatements) UpdateForgetMembership(
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *membershipStatements) SelectLocalServerInRoom(ctx context.Context, roomNID types.RoomNID) (bool, error) {
|
||||||
|
var nid types.RoomNID
|
||||||
|
err := s.selectLocalServerInRoomStmt.QueryRowContext(ctx, tables.MembershipStateJoin, roomNID).Scan(&nid)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
found := nid > 0
|
||||||
|
return found, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1059,6 +1059,11 @@ func (d *Database) JoinedUsersSetInRooms(ctx context.Context, roomIDs []string)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLocalServerInRoom returns true if we think we're in a given room or false otherwise.
|
||||||
|
func (d *Database) GetLocalServerInRoom(ctx context.Context, roomNID types.RoomNID) (bool, error) {
|
||||||
|
return d.MembershipTable.SelectLocalServerInRoom(ctx, roomNID)
|
||||||
|
}
|
||||||
|
|
||||||
// GetKnownUsers searches all users that userID knows about.
|
// GetKnownUsers searches all users that userID knows about.
|
||||||
func (d *Database) GetKnownUsers(ctx context.Context, userID, searchString string, limit int) ([]string, error) {
|
func (d *Database) GetKnownUsers(ctx context.Context, userID, searchString string, limit int) ([]string, error) {
|
||||||
stateKeyNID, err := d.EventStateKeysTable.SelectEventStateKeyNID(ctx, nil, userID)
|
stateKeyNID, err := d.EventStateKeysTable.SelectEventStateKeyNID(ctx, nil, userID)
|
||||||
|
|
|
@ -100,6 +100,14 @@ var selectKnownUsersSQL = "" +
|
||||||
" SELECT DISTINCT room_nid FROM roomserver_membership WHERE target_nid=$1 AND membership_nid = " + fmt.Sprintf("%d", tables.MembershipStateJoin) +
|
" SELECT DISTINCT room_nid FROM roomserver_membership WHERE target_nid=$1 AND membership_nid = " + fmt.Sprintf("%d", tables.MembershipStateJoin) +
|
||||||
") AND membership_nid = " + fmt.Sprintf("%d", tables.MembershipStateJoin) + " AND event_state_key LIKE $2 LIMIT $3"
|
") AND membership_nid = " + fmt.Sprintf("%d", tables.MembershipStateJoin) + " AND event_state_key LIKE $2 LIMIT $3"
|
||||||
|
|
||||||
|
// selectLocalServerInRoomSQL is an optimised case for checking if we, the local server,
|
||||||
|
// are in the room by using the target_local column of the membership table. Normally when
|
||||||
|
// we want to know if a server is in a room, we have to unmarshal the entire room state which
|
||||||
|
// is expensive. The presence of a single row from this query suggests we're still in the
|
||||||
|
// room, no rows returned suggests we aren't.
|
||||||
|
const selectLocalServerInRoomSQL = "" +
|
||||||
|
"SELECT room_nid FROM roomserver_membership WHERE target_local = 1 AND membership_nid = $1 AND room_nid = $2 LIMIT 1"
|
||||||
|
|
||||||
type membershipStatements struct {
|
type membershipStatements struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
insertMembershipStmt *sql.Stmt
|
insertMembershipStmt *sql.Stmt
|
||||||
|
@ -113,6 +121,7 @@ type membershipStatements struct {
|
||||||
updateMembershipStmt *sql.Stmt
|
updateMembershipStmt *sql.Stmt
|
||||||
selectKnownUsersStmt *sql.Stmt
|
selectKnownUsersStmt *sql.Stmt
|
||||||
updateMembershipForgetRoomStmt *sql.Stmt
|
updateMembershipForgetRoomStmt *sql.Stmt
|
||||||
|
selectLocalServerInRoomStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMembershipTable(db *sql.DB) error {
|
func createMembershipTable(db *sql.DB) error {
|
||||||
|
@ -137,6 +146,7 @@ func prepareMembershipTable(db *sql.DB) (tables.Membership, error) {
|
||||||
{&s.selectRoomsWithMembershipStmt, selectRoomsWithMembershipSQL},
|
{&s.selectRoomsWithMembershipStmt, selectRoomsWithMembershipSQL},
|
||||||
{&s.selectKnownUsersStmt, selectKnownUsersSQL},
|
{&s.selectKnownUsersStmt, selectKnownUsersSQL},
|
||||||
{&s.updateMembershipForgetRoomStmt, updateMembershipForgetRoom},
|
{&s.updateMembershipForgetRoomStmt, updateMembershipForgetRoom},
|
||||||
|
{&s.selectLocalServerInRoomStmt, selectLocalServerInRoomSQL},
|
||||||
}.Prepare(db)
|
}.Prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,3 +314,16 @@ func (s *membershipStatements) UpdateForgetMembership(
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *membershipStatements) SelectLocalServerInRoom(ctx context.Context, roomNID types.RoomNID) (bool, error) {
|
||||||
|
var nid types.RoomNID
|
||||||
|
err := s.selectLocalServerInRoomStmt.QueryRowContext(ctx, tables.MembershipStateJoin, roomNID).Scan(&nid)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
found := nid > 0
|
||||||
|
return found, nil
|
||||||
|
}
|
||||||
|
|
|
@ -135,6 +135,7 @@ type Membership interface {
|
||||||
SelectJoinedUsersSetForRooms(ctx context.Context, roomNIDs []types.RoomNID) (map[types.EventStateKeyNID]int, error)
|
SelectJoinedUsersSetForRooms(ctx context.Context, roomNIDs []types.RoomNID) (map[types.EventStateKeyNID]int, error)
|
||||||
SelectKnownUsers(ctx context.Context, userID types.EventStateKeyNID, searchString string, limit int) ([]string, error)
|
SelectKnownUsers(ctx context.Context, userID types.EventStateKeyNID, searchString string, limit int) ([]string, error)
|
||||||
UpdateForgetMembership(ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, targetUserNID types.EventStateKeyNID, forget bool) error
|
UpdateForgetMembership(ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, targetUserNID types.EventStateKeyNID, forget bool) error
|
||||||
|
SelectLocalServerInRoom(ctx context.Context, roomNID types.RoomNID) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Published interface {
|
type Published interface {
|
||||||
|
|
Loading…
Reference in a new issue