-
-
Notifications
You must be signed in to change notification settings - Fork 606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix room state being updated with old (now overwritten) state and emitting for those updates. #4242
Changes from 6 commits
57a8f32
5a35e3b
4ab2cc1
2e1b08e
3bb9291
d469eb0
b5d5dd3
8ebf04a
161d63e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,7 +43,16 @@ export interface IMarkerFoundOptions { | |
*/ | ||
timelineWasEmpty?: boolean; | ||
} | ||
|
||
export interface ISetStateEventsOptions { | ||
/** Whether the event is inserted at the start of the timeline | ||
* or at the end. This is relevant for doing a sanity check: | ||
* "Is the event id of the added event the same as the replaces_state id of the current event" | ||
* We need to do this because sync sometimes feeds previous state events. | ||
* If set to true the sanity check is inverted | ||
* "Is the event id of the current event the same as the replaces_state id of the added event" | ||
*/ | ||
toStartOfTimeline?: boolean; | ||
} | ||
// possible statuses for out-of-band member loading | ||
enum OobStatus { | ||
NotStarted, | ||
|
@@ -408,7 +417,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap> | |
* Fires {@link RoomStateEvent.Events} | ||
* Fires {@link RoomStateEvent.Marker} | ||
*/ | ||
public setStateEvents(stateEvents: MatrixEvent[], markerFoundOptions?: IMarkerFoundOptions): void { | ||
public setStateEvents(stateEvents: MatrixEvent[], options?: IMarkerFoundOptions & ISetStateEventsOptions): void { | ||
t3chguy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.updateModifiedTime(); | ||
|
||
// update the core event dict | ||
|
@@ -419,13 +428,26 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap> | |
this.setBeacon(event); | ||
} | ||
|
||
const lastStateEvent = this.getStateEventMatching(event); | ||
const lastStateEv = this.getStateEventMatching(event); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did this get renamed to something less clear? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Otherwise, we had a lot more linewraps that made I judged as harder to read overall. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's only because on L435 and L436 you're using the array syntax to set variables which is really redundant. It'd be clearer as 4 separate instantiations. |
||
// Safety measure to not update the room (and emit the update) with older state. | ||
// The sync loop really should not send old events but it does very regularly. | ||
// Logging on return in those two conditions results in a large amount of logging. (on startup and when running element) | ||
const [lastReplaceId, lastId] = [lastStateEv?.event.unsigned?.replaces_state, lastStateEv?.event.event_id]; | ||
const [newReplaceId, newId] = [event.event.unsigned?.replaces_state, event.event.event_id]; | ||
if (options?.toStartOfTimeline) { | ||
// Add an event to the start of the timeline. Its replace id not be the same as the one of the current/last start state event. | ||
if (newReplaceId && newId && newReplaceId === lastId) return; | ||
} else { | ||
// Add an event to the end of the timeline. It should not be the same as the one replaced but the current/last end state event. | ||
if (lastReplaceId && newId && lastReplaceId === newId) return; | ||
toger5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
this.setStateEvent(event); | ||
if (event.getType() === EventType.RoomMember) { | ||
this.updateDisplayNameCache(event.getStateKey()!, event.getContent().displayname ?? ""); | ||
this.updateThirdPartyTokenCache(event); | ||
} | ||
this.emit(RoomStateEvent.Events, event, this, lastStateEvent); | ||
this.emit(RoomStateEvent.Events, event, this, lastStateEv); | ||
}); | ||
|
||
this.onBeaconLivenessChange(); | ||
|
@@ -476,7 +498,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap> | |
// assume all our sentinels are now out-of-date | ||
this.sentinels = {}; | ||
} else if (UNSTABLE_MSC2716_MARKER.matches(event.getType())) { | ||
this.emit(RoomStateEvent.Marker, event, markerFoundOptions); | ||
this.emit(RoomStateEvent.Marker, event, options); | ||
} | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this makes sense, as a RoomState is only ever in one direction. This sounds like if toStartOfTimeline=false then you should always drop the state event as they're never live. It should be a property on a RoomState if required
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good observation. The state itself might be the right location to store if it's a start state or an end state.
It should never change and also not be relevant outside the state object it seems logical to belong to the state.
I have trouble understanding the exact definition of start state anyway however:
If we have a state that is only set at the beginning of the room, events from a time-line in a specific interval cannot be enough to compute the start state because the start state needs all state events BEFORE the start of the time-line but the time-line only includes events after the "start".
This is slightly off topic but i still wanted to being it up. Feel free to DM me about this or point me to where i can lookup how start state is defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as I understand it, it is the state at the start of the timeline, and it walks backwards as you backpaginate and load older events which undo the more recent state events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would also be how i understand start state. But this is not what we are doing.
We use the content to set the state keys. What we would need to do is set the state with the previous_content field.
|
= start of timeline||
= end of timeline---
= time stepsXV>
= state event changing the state (the content of the state event is the current state from that time on (X is an arbitrary label for the state event (f.ex. a member event) X is the type and V is the valueX0>-----|X1>----X2>----||
Here we have an example where the state is changed with the first event in the timeline. But the start imo is before that event. So the state at the start is X0.
This becomes more clear when adding another type:
X0>--A0>--|A1>-A2>-X1>--||
At the start of the timeline we have X0 A0 but definitly not X1 which would be the first X type state event.
If we use the previous_content we have <XV as the previous content.
<X0>--<A0>--|<A1>-<A2>-<X1>--||
Then
<A1
&<X1
is the same asX0>
&A0>
and the correct start state.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest raising this to the wider team as the code & intent predates me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure. Sorry for escalating this but I always found the startState to be arguably the correct name for that variable the way its implemented and this seemed to be relevant for this discussion. Somewhat relevant, you comment is just about where to store start/end.
What I do like about making it part of the setStateEvents property is that is makes it testable in a more isolated env.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To get back to the topic what do you think about the more encapsulated testability with the current approach?