-
-
Notifications
You must be signed in to change notification settings - Fork 585
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 leaking room state objects on limited sync responses #395
Conversation
event[prop] = interns[event[prop]]; | ||
}); | ||
|
||
["membership", "avatar_url", "displayname"].forEach((prop) => { |
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 wonder if we should worry about interns
filling up with old displaynames and avatar_urls.
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.
We never remove events either, so the strings which are there should be net positive.
If we deleted m.room.member
events then absolutely, we probably would need to start caring. We still keep all previous names/avatar URLs when the user changes their name/avatar, so frequent name changes will still be referenced somewhere in the code.
src/models/room.js
Outdated
@@ -207,7 +207,12 @@ Room.prototype.getLiveTimeline = function() { | |||
*/ | |||
Room.prototype.resetLiveTimeline = function(backPaginationToken) { | |||
for (let i = 0; i < this._timelineSets.length; i++) { | |||
this._timelineSets[i].resetLiveTimeline(backPaginationToken); | |||
// Flush out non-live timelines. We do this to reduce the amount of memory |
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.
this implies we're only flushing out the non-live timelines; I don't think that is correct?
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.
Oops, right you are
src/models/room.js
Outdated
// can be 10s of MBs) for each timeline is expensive. This is particularly | ||
// noticeable when the client unsleeps and there are a lot of 'limited: true' | ||
// responses. https://github.com/vector-im/riot-web/issues/3307#issuecomment-282895568 | ||
this._timelineSets[i].resetLiveTimeline(backPaginationToken, true); |
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.
you're going to need to convince me that you've properly thought through the consequences of clearing _eventIdToTimeline
while the user is still potentially viewing events in an old timeline. This feels like it's going to break as much as it fixes right now :/.
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.
Tested it by spamming #matrix-test whilst offline and viewing the room (and making sure it jumps down on reconnect)
If you want more convincing then state what you would like.
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.
it shouldn't jump down if you're viewing historical events.
A handful of examples: what happens when you scroll down so that the historical timeline merges with the new one? what happens if you then scroll back up? what happens if you jump to a permalink to the events that are now in a timeline which is attached to the live timeline, but are no longer in _eventIdToTimeline
?
Mostly though, I think that a change that deliberately leaves the datastructures in an inconsistent state is a bad idea.
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.
a change that deliberately leaves the datastructures in an inconsistent state is a bad idea.
I'm not, I'm removing all the events in the timeline then adding the ones I got from a limited
sync. Where is the inconsistency? The event ID to timeline map is cleared when we flush
to preserve consistency: https://github.com/matrix-org/matrix-js-sdk/blob/master/src/models/event-timeline-set.js#L163
it shouldn't jump down if you're viewing historical events.
Why? This strictly happens on a limited
/sync only, which restricts this to startup/load/unsleep. If we don't jump down, then the events needs to hang around until some indeterminate point in time when they can be reaped, and it's unclear when that should happen or how that should work, and sounds exceptionally prone to leaking the events beyond the point where you've removed them from the JS SDK object model. Your examples focus on the assumption that the events are still around, and the entire point of this PR is to make them NOT hang around.
If they don't hang around, then it's really quite simple: we drop all events from the room and only populate the most recent 20 messages. The things to check are:
- Make sure that the other events are GCed.
- Make sure that if you were viewing some of the older events, that the screen jumps to the bottom as they no longer exist.
Both of those cases I have manually tested and confirmed. I think we may have philosophical differences on how this should behave, so may need @ara4n's opinion on how to proceed.
If the user is viewing historical events, and then forward paginates to the live timeline, the historical events will get joined onto the live timeline, hence: "what happens if you jump to a permalink to the events that are now in a timeline which is attached to the live timeline, but are no longer in _eventIdToTimeline ?"
... or any period where more than 20 messages arrive between /sync calls. If I've carefully scrolled back to a bit of history, and a flurry of activity combined with a network glitch means that I get a limited sync, I am going to be cursing your name if my view suddenly jumps to the bottom. |
Anyway: if the application layer is obliged to make sure it has dropped all references to historical timelines on a limited sync (which I still think is a bad idea for the UX), you need to plaster that big and bold across the APIs, rather than discovering that it appears to be ok in Riot and calling it job done. |
The user cannot forward paginate in this scenario, because the timelines were dropped. It causes the timeline to fire http://matrix-org.github.io/matrix-js-sdk/0.7.5/module-client.html#~event:MatrixClient%2522Room.timelineReset%2522 - It's wholly unclear what we expect clients to do when this event fires. The docs don't say.
I cannot test this because the timelines are dropped on a
How do you want me to proceed? |
I'm not quite sure where the disconnect is happening here - we are obviously still somehow at cross-purposes. So let me try to be absolutely clear. I fired up a riot build with your branch of the js-sdk, and did the following:
This process used to work fine. TBH, it still does, modulo the scary error in the console, but I think that errors like that in the console logs are unacceptable, and in this case symptomatic of other problems. Let's discuss what to do about it when we meet in person. |
This is the bit where we are crossing purposes. I can't scroll down, since the instant it syncs the timeline flashes and jumps to the bottom. Other than that, your steps sound the same as mine. |
Weird. It used to work, and it worked for me in testing last night.
…On 22 March 2017 08:32:15 GMT+00:00, Kegsay ***@***.***> wrote:
> scroll back down.
This is the bit where we are crossing purposes. I can't scroll down,
since the instant it syncs the timeline flashes and jumps to the
bottom. Other than that, your steps sound the same as mine.
--
You are receiving this because you were assigned.
Reply to this email directly or view it on GitHub:
#395 (comment)
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
|
Right, I think I know why. We do different things depending on the scroll position when the timeline resyncs. If it's at the bottom things work correctly and the timeline reloads, else it ignores the reset request. It sounds like we've been hitting one or the other case which is why we don't agree on the results. |
Yes, that's what I've been trying to tell you... |
Conclusions from IRL:
React SDK will implement this function and will always return
@richvdh Does this sound accurate? |
ja think so Anything you can do to improve crappy jsdocs while you are in there would be very much appreciated |
…r than a startClient opt for ease of gluing code in
@richvdh PTAL |
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.
looks good except for grumbling about names and docs
src/models/event-timeline-set.js
Outdated
@@ -187,7 +187,7 @@ EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flu | |||
newTimeline.setPaginationToken(backPaginationToken, EventTimeline.BACKWARDS); | |||
|
|||
this._liveTimeline = newTimeline; | |||
this.emit("Room.timelineReset", this.room, this); | |||
this.emit("Room.timelineReset", this.room, this, flush); |
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.
any chance we can improve the name of flush
(resetAll
seems fine) and clarify what it means to flush the non-live timelines?
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.
also, should be flush || !this._timelineSupport
. Or better yet, factor out the condition so that they don't get out of sync.
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.
Done. 14727d7
src/models/event-timeline-set.js
Outdated
@@ -655,4 +655,6 @@ module.exports = EventTimelineSet; | |||
* @event module:client~MatrixClient#"Room.timelineReset" | |||
* @param {Room} room The room whose live timeline was reset, if any | |||
* @param {EventTimelineSet} timelineSet timelineSet room whose live timeline was reset | |||
* @param {boolean} resetAll The return value for canResetEntireTimeline() for this room. |
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.
this feels like a bit of a backwards description to me. At this point, as an app, I am interested in what the js-sdk did (ie, reset all the timelines - again, needs a better description), rather than why it did it.
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.
Done. 14727d7
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.
lgtm
This PR does 2 things to save memory:
It forces strings to be shared between
MatrixEvent
objects. By default they are NOT shared in events because we doJSON.parse(someString)
, so we do a no-op assignment into a globalinterns
map to force V8 to share strings. Strings are immutable so this has no practical effect other than a bit of space saving. We never remove events either, so the strings which are there should be net positive. On my account, this reduces the amount of memory used from 350MB to 320MB.It force clears the non-live timelines on a
limited
sync response. I was pleased to see there was already a flag for this:flush
. Tested it by spamming#matrix-test
whilst offline and:Can confirm via heap snapshots it is now reaping correctly. This should fix Riot-web is very greedy on RAM (and can make chrome crash) element-hq/element-web#3307 (comment)