element-x-ios icon indicating copy to clipboard operation
element-x-ios copied to clipboard

Gaps in timelines block older content loading from the event cache, massively undermining its benefits

Open ara4n opened this issue 9 months ago • 10 comments

Steps to reproduce

  1. Open the gallery for a room, and let it populate up the event & media cache for rapid access to the gallery. Could easily take a few minutes as it spiders the room.
  2. Use the app for a bit
  3. A few days later, go back to the room and open up its gallery again.
  4. A few pieces of recent content might appear instantly (if they just got downloaded on opening the room), but if there has been a gappy sync since you last viewed the room (which will likely be the case), then the cached events + content will not be displayed.
  5. Instead, the client presumably goes through filling in the gap by slowly calling /messages.
  6. The user feels that the cache isn't working (this might be what was happening in https://github.com/element-hq/element-x-ios/issues/3832)
  7. It's only once the gap has been filled in that the existing cached content gets rapidly loaded and displayed (although in practice i'm not sure that it's actually rapidly loading & displaying; it seems to be pulling it all back in from /messages again).

This is really bad on the gallery, as perceptually it feels like the cache got flushed, given as a user you end up staring at a blank gallery and a spinner while it paginates back through the gap. Even if eventually it hits the previously cached content and displays it rapidly.

The same problem exists on the normal timeline:

  1. Open a room
  2. Read a few pages of scrollback
  3. Go back to the room a while later
  4. If there's been a gap in the room history... we show the most recent message(s) in the room, but all the cached content isn't shown. (If it was, it'd seem like the client had lost messages in the gap)
  5. As a result, the user has to slowly wait while the client fills in the gap before they can see any of the prior history in the room - at which point the cache isn't fulfilling its function of letting users rapidly see information without having to talk to the server.

I suspect the solution may be to display a spinner in the timeline (or in the gallery) whenever there's a gap in the timeline which the client is currently 'filling in' via /messages. This lets the user see all the locally cached content asap, while being aware that there's a gap which is being populated - rather than being forced to hide the older cached content to avoid confusion while the gap is being populated.

Outcome

What did you expect?

Client should rapidly show me the messages & media in its cache so I can make good use of the cache

What happened instead?

Client blocks showing me older cached content in a gallery or room timeline until it has paginated through all the history that happened since I last looked at that room.

There may also be an additional bug where it doesn't retrieve older events from the cache at all after there's been a gap, but keeps paginating them in from the server (based on eyeballing performance in the gallery).

Application version

855

ara4n avatar Mar 05 '25 13:03 ara4n

This is a known problem. We have formulated this problem since we are working on the event cache lazy-loader. Two solutions exist:

  • automatic back pagination
  • render gaps in the timeline.

These solutions are not mutually exclusive. Actually we need both for various reasons. Automatic back pagination is required to get a correct app badge counter and nice text search. On the other hand, rendering gaps allows to display what we have in the cache without blocking the timeline nor the media gallery (which is a timeline).

Hywan avatar Mar 06 '25 12:03 Hywan

See Apollo e Dafne in https://github.com/matrix-org/matrix-rust-sdk/pull/4632

Hywan avatar Mar 06 '25 12:03 Hywan

i guess the problem with automatic back pagination is that if hasn't kicked in yet to fill a gap for a given room (assuming automatic back pagination even existed), you'd still be stuck staring at the void rather than displaying the cached history.

ara4n avatar Mar 07 '25 00:03 ara4n

cc @mxandreas just in case there's a quick fix to be had here by saying "sure, let's render timeline gaps as spinners"

ara4n avatar Mar 07 '25 00:03 ara4n

I tend to agree that there's probably no better solution than to show the gap. It seems very confusing if suddenly you do not have a lot of messages that you used to have. I will check with the designers.

mxandreas avatar Mar 07 '25 15:03 mxandreas

Automatic back-pagination + showing a gap in the timeline are two solutions that must be implemented anyway.

Hywan avatar Mar 10 '25 08:03 Hywan

Right, but to be clear: "showing gaps" is needed irrespective of automatic back-pagination - they aren't really overlapping.

In terms of design, we already have a spinner UI showing backpagination happening; my proposal would be to show that spinner in the timeline at the point that the gap is (i.e. with msgs above it too).

ara4n avatar Mar 10 '25 09:03 ara4n

For what it's worth: at this point the timeline will NOT show any gap that's NOT at the beginning of the timeline. i.e. we can't have this situation:

  • some event
  • a gap
  • some event

As a matter of fact: if there's a gap, it MUST be at the beginning of the room, meaning that the beginning of the room is one of both:

  • either the actual start of the room; there's nothing else to back-paginate
  • or it's not the actual start of the room (and we should know about this, uh, modulo bugs), and in this case we can preemptively show a loading spinner, because there's going to be a pagination (either from the cache or from network). Only when the loading spinner is actually rendered and displayed on screen, can we trigger the back-pagination.

In the current state, described above, we don't have to think about rendering all gaps, in general. It would be nice to think about it for the future:

  • then we could maximally reuse the event cache, and show older events even if we haven't resolved gaps in between (as in the initial example at the top of this comment)
  • in offline mode, since we can't resolve a gap using network, we would display a spinner loading forever, which is bad (also note that in offline mode, we can't know where we have gaps, so we'd need to display some kind of banner "there might be missing events, since you're disconnected", to make it super clear).

So there are potentially two phases:

  1. show a spinner with "loading…" at the top of the timeline, if we're not at the start of the room yet
  2. think about how to render gaps, for offline mode + being able to display events / gap / events timelines

I think implementing (1) would be a nice win in the short term, until we have design support for (2).

bnjbvr avatar Mar 10 '25 09:03 bnjbvr

We had a small conversation with @amshakal yesterday and we would likely show the "placeholder" image (e.g. similar to what Slack does when it is trying to fetch the messages) so it would be a bit more subtle than spinner but @americanrefugee will take a deeper look when he is back. Besides being subtle, I think we need to choose something that works for both - the beginning of the timeline and the middle of timeline as 2 different ones is probably the worst option.

mxandreas avatar Mar 11 '25 08:03 mxandreas

Adding a note that older content when the device is offline is only blocked when there is a bad timing: e.g. the app becomes aware that there is a gap but does not manage to fill this gap before the device goes offline. It is not that older content is never loaded whenever you're offline.

mxandreas avatar Mar 25 '25 13:03 mxandreas

Technical Rust SDK note: supporting gaps in the timeline code will require taking into account the relative ordering of related events (that is, have an absolute idea of the relative position of two edits, for instance, to determine which is the "latest" one). Right now, we mostly don't need to do this, because either we're getting events from sync (so a related event has to be the latest one), or we're getting them from back-pagination (so we might see a related event before the event it relates to, in which case we stash it on the side, and apply it later when the related event's back).

bnjbvr avatar Apr 24 '25 08:04 bnjbvr