diff --git a/src/github.com/matrix-org/dendrite/roomserver/input/membership.go b/src/github.com/matrix-org/dendrite/roomserver/input/membership.go index f306697ff..788b1e30a 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/input/membership.go +++ b/src/github.com/matrix-org/dendrite/roomserver/input/membership.go @@ -95,10 +95,9 @@ func updateMembership( return nil, err } } - if old == new { + if old == new && new != "join" { // If the membership is the same then nothing changed and we can return - // immediately. This should help speed up processing for display name - // changes where the membership is "join" both before and after. + // immediately. return updates, nil } @@ -152,16 +151,20 @@ func updateToInviteMembership( func updateToJoinMembership( mu types.MembershipUpdater, add *gomatrixserverlib.Event, updates []api.OutputEvent, ) ([]api.OutputEvent, error) { - // If the user is already marked as being joined then we can return immediately. - // TODO: Is this code reachable given the "old != new" guard in updateMembership? + // If the user is already marked as being joined, we call SetToJoin to update + // the event ID then we can return immediately. Retired is ignored as if mu.IsJoin() { + _, err := mu.SetToJoin(add.Sender(), add.EventID(), true) + if err != nil { + return nil, err + } return updates, nil } // When we mark a user as being joined we will invalidate any invites that // are active for that user. We notify the consumers that the invites have // been retired using a special event, even though they could infer this // by studying the state changes in the room event stream. - retired, err := mu.SetToJoin(add.Sender()) + retired, err := mu.SetToJoin(add.Sender(), add.EventID(), false) if err != nil { return nil, err } diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/membership_table.go b/src/github.com/matrix-org/dendrite/roomserver/storage/membership_table.go index 725e5b8d9..fa38d1881 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/membership_table.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/membership_table.go @@ -33,7 +33,7 @@ const membershipSchema = ` -- and the room state tables. -- This table is updated in one of 3 ways: -- 1) The membership of a user changes within the current state of the room. --- 2) An invite is received outside of a room over federation. +-- 2) An invite is received outside of a room over federation. -- 3) An invite is rejected outside of a room over federation. CREATE TABLE IF NOT EXISTS roomserver_membership ( room_nid BIGINT NOT NULL, @@ -46,6 +46,11 @@ CREATE TABLE IF NOT EXISTS roomserver_membership ( -- The state the user is in within this room. -- Default value is "membershipStateLeaveOrBan" membership_nid BIGINT NOT NULL DEFAULT 1, + -- The ID of the "join" membership event. + -- This ID is updated if the join event gets updated (e.g. profile update). + -- This column is set to NULL if the user hasn't joined the room yet, e.g. + -- if the user was invited but hasn't joined yet. + event_id TEXT, UNIQUE (room_nid, target_nid) ); ` @@ -62,7 +67,7 @@ const selectMembershipForUpdateSQL = "" + " WHERE room_nid = $1 AND target_nid = $2 FOR UPDATE" const updateMembershipSQL = "" + - "UPDATE roomserver_membership SET sender_nid = $3, membership_nid = $4" + + "UPDATE roomserver_membership SET sender_nid = $3, membership_nid = $4, event_id = $5" + " WHERE room_nid = $1 AND target_nid = $2" type membershipStatements struct { @@ -103,9 +108,14 @@ func (s *membershipStatements) selectMembershipForUpdate( func (s *membershipStatements) updateMembership( txn *sql.Tx, roomNID types.RoomNID, targetUserNID types.EventStateKeyNID, senderUserNID types.EventStateKeyNID, membership membershipState, + eventID string, ) error { + eID := sql.NullString{ + String: eventID, + Valid: len(eventID) > 0, + } _, err := txn.Stmt(s.updateMembershipStmt).Exec( - roomNID, targetUserNID, senderUserNID, membership, + roomNID, targetUserNID, senderUserNID, membership, eID, ) return err } diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go index d323fd139..57cb9ff3f 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go @@ -435,7 +435,7 @@ func (u *membershipUpdater) SetToInvite(event gomatrixserverlib.Event) (bool, er } if u.membership != membershipStateInvite { if err = u.d.statements.updateMembership( - u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateInvite, + u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateInvite, "", ); err != nil { return false, err } @@ -444,24 +444,32 @@ func (u *membershipUpdater) SetToInvite(event gomatrixserverlib.Event) (bool, er } // SetToJoin implements types.MembershipUpdater -func (u *membershipUpdater) SetToJoin(senderUserID string) ([]string, error) { +func (u *membershipUpdater) SetToJoin(senderUserID string, eventID string, isUpdate bool) ([]string, error) { + var inviteEventIDs []string + senderUserNID, err := u.d.assignStateKeyNID(u.txn, senderUserID) if err != nil { return nil, err } - inviteEventIDs, err := u.d.statements.updateInviteRetired( - u.txn, u.roomNID, u.targetUserNID, - ) - if err != nil { - return nil, err + + // If this is a join event update, there is no invite to update + if !isUpdate { + inviteEventIDs, err = u.d.statements.updateInviteRetired( + u.txn, u.roomNID, u.targetUserNID, + ) + if err != nil { + return nil, err + } } - if u.membership != membershipStateJoin { + + if u.membership != membershipStateJoin || isUpdate { if err = u.d.statements.updateMembership( - u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateJoin, + u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateJoin, eventID, ); err != nil { return nil, err } } + return inviteEventIDs, nil } @@ -479,7 +487,7 @@ func (u *membershipUpdater) SetToLeave(senderUserID string) ([]string, error) { } if u.membership != membershipStateLeaveOrBan { if err = u.d.statements.updateMembership( - u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateLeaveOrBan, + u.txn, u.roomNID, u.targetUserNID, senderUserNID, membershipStateLeaveOrBan, "", ); err != nil { return nil, err } diff --git a/src/github.com/matrix-org/dendrite/roomserver/types/types.go b/src/github.com/matrix-org/dendrite/roomserver/types/types.go index 809b6e574..df55fe2ea 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/types/types.go +++ b/src/github.com/matrix-org/dendrite/roomserver/types/types.go @@ -193,9 +193,9 @@ type MembershipUpdater interface { // Set the state to invite. // Returns whether this invite needs to be sent SetToInvite(event gomatrixserverlib.Event) (needsSending bool, err error) - // Set the state to join. + // Set the state to join or updates the event ID in the database. // Returns a list of invite event IDs that this state change retired. - SetToJoin(senderUserID string) (inviteEventIDs []string, err error) + SetToJoin(senderUserID string, eventID string, isUpdate bool) (inviteEventIDs []string, err error) // Set the state to leave. // Returns a list of invite event IDs that this state change retired. SetToLeave(senderUserID string) (inviteEventIDs []string, err error)