From 2e71d2708f7c922dcb6361c8a49022470b8b6e33 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 5 Oct 2020 17:47:08 +0100 Subject: [PATCH] Resolve state after event against current room state when determining latest state changes (#1479) * Resolve state after event against current room state when determining latest state changes * Update sytest-whitelist * Update sytest-whitelist, blacklist --- .../internal/input/input_latest_events.go | 23 ++++++++++++++++--- roomserver/state/state.go | 8 ++++--- sytest-blacklist | 7 +++++- sytest-whitelist | 4 ---- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/roomserver/internal/input/input_latest_events.go b/roomserver/internal/input/input_latest_events.go index 5c2a1de6a..2e9f3b4e4 100644 --- a/roomserver/internal/input/input_latest_events.go +++ b/roomserver/internal/input/input_latest_events.go @@ -215,10 +215,27 @@ func (u *latestEventsUpdater) latestState() error { var err error roomState := state.NewStateResolution(u.api.DB, *u.roomInfo) - // Get a list of the current latest events. - latestStateAtEvents := make([]types.StateAtEvent, len(u.latest)) + // Get a list of the current room state events if available. + var currentState []types.StateEntry + if u.roomInfo.StateSnapshotNID != 0 { + currentState, _ = roomState.LoadStateAtSnapshot(u.ctx, u.roomInfo.StateSnapshotNID) + } + + // Get a list of the current latest events. This will include both + // the current room state and the latest events after the input event. + // The idea is that we will perform state resolution on this set and + // any conflicting events will be resolved properly. + latestStateAtEvents := make([]types.StateAtEvent, len(u.latest)+len(currentState)) + offset := 0 + for i := range currentState { + latestStateAtEvents[i] = types.StateAtEvent{ + BeforeStateSnapshotNID: u.roomInfo.StateSnapshotNID, + StateEntry: currentState[i], + } + offset++ + } for i := range u.latest { - latestStateAtEvents[i] = u.latest[i].StateAtEvent + latestStateAtEvents[offset+i] = u.latest[i].StateAtEvent } // Takes the NIDs of the latest events and creates a state snapshot diff --git a/roomserver/state/state.go b/roomserver/state/state.go index 0663499e7..2944f71c1 100644 --- a/roomserver/state/state.go +++ b/roomserver/state/state.go @@ -118,7 +118,7 @@ func (v StateResolution) LoadCombinedStateAfterEvents( // the snapshot of the room state before them was the same. stateBlockNIDLists, err := v.db.StateBlockNIDs(ctx, uniqueStateSnapshotNIDs(stateNIDs)) if err != nil { - return nil, err + return nil, fmt.Errorf("v.db.StateBlockNIDs: %w", err) } var stateBlockNIDs []types.StateBlockNID @@ -131,7 +131,7 @@ func (v StateResolution) LoadCombinedStateAfterEvents( // multiple snapshots. stateEntryLists, err := v.db.StateEntries(ctx, uniqueStateBlockNIDs(stateBlockNIDs)) if err != nil { - return nil, err + return nil, fmt.Errorf("v.db.StateEntries: %w", err) } stateBlockNIDsMap := stateBlockNIDListMap(stateBlockNIDLists) stateEntriesMap := stateEntryListMap(stateEntryLists) @@ -623,7 +623,7 @@ func (v StateResolution) calculateAndStoreStateAfterManyEvents( v.calculateStateAfterManyEvents(ctx, v.roomInfo.RoomVersion, prevStates) metrics.algorithm = algorithm if err != nil { - return metrics.stop(0, err) + return metrics.stop(0, fmt.Errorf("v.calculateStateAfterManyEvents: %w", err)) } // TODO: Check if we can encode the new state as a delta against the @@ -642,6 +642,7 @@ func (v StateResolution) calculateStateAfterManyEvents( // First stage: load the state after each of the prev events. combined, err = v.LoadCombinedStateAfterEvents(ctx, prevStates) if err != nil { + err = fmt.Errorf("v.LoadCombinedStateAfterEvents: %w", err) algorithm = "_load_combined_state" return } @@ -672,6 +673,7 @@ func (v StateResolution) calculateStateAfterManyEvents( var resolved []types.StateEntry resolved, err = v.resolveConflicts(ctx, roomVersion, notConflicted, conflicts) if err != nil { + err = fmt.Errorf("v.resolveConflits: %w", err) algorithm = "_resolve_conflicts" return } diff --git a/sytest-blacklist b/sytest-blacklist index 2f80fc789..ff7fdf7e0 100644 --- a/sytest-blacklist +++ b/sytest-blacklist @@ -52,4 +52,9 @@ Inbound federation accepts a second soft-failed event Outbound federation requests missing prev_events and then asks for /state_ids and resolves the state # We don't implement lazy membership loading yet. -The only membership state included in a gapped incremental sync is for senders in the timeline \ No newline at end of file +The only membership state included in a gapped incremental sync is for senders in the timeline + +# Blacklisted out of flakiness after #1479 +Invited user can reject local invite after originator leaves +Invited user can reject invite for empty room +If user leaves room, remote user changes device and rejoins we see update in /sync and /keys/changes \ No newline at end of file diff --git a/sytest-whitelist b/sytest-whitelist index a811259fb..9a013cbf3 100644 --- a/sytest-whitelist +++ b/sytest-whitelist @@ -400,8 +400,6 @@ Uninvited users cannot join the room Users cannot invite themselves to a room Users cannot invite a user that is already in the room Invited user can reject invite -Invited user can reject invite for empty room -Invited user can reject local invite after originator leaves PUT /rooms/:room_id/typing/:user_id sets typing notification Typing notification sent to local room members Typing notifications also sent to remote room members @@ -431,7 +429,6 @@ A prev_batch token can be used in the v1 messages API We don't send redundant membership state across incremental syncs by default Typing notifications don't leak Users cannot kick users from a room they are not in -Users cannot kick users who have already left a room User appears in user directory User directory correctly update on display name change User in shared private room does appear in user directory @@ -451,7 +448,6 @@ Banned servers cannot backfill Inbound /v1/send_leave rejects leaves from other servers Guest users can accept invites to private rooms over federation AS user (not ghost) can join room without registering -If user leaves room, remote user changes device and rejoins we see update in /sync and /keys/changes Can search public room list Can get remote public room list Asking for a remote rooms list, but supplying the local server's name, returns the local rooms list