Skip to content
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

refactor: RoomEvents::reset really clears the linked chunk #4321

Merged
merged 2 commits into from
Nov 26, 2024

Conversation

Hywan
Copy link
Member

@Hywan Hywan commented Nov 25, 2024

This PR is twofold:

  1. It fixes a bug in AsVector: when an Update::Clear was received, the internal state of AsVector wasn't cleared,
  2. It updates RoomEvents::reset to use LinkedChunk::clear so that events are correctly cleared and not dropped silently, i.e. the Updates and VectorDiffs are correctly reflecting that. That's important to clear the stores (implementations of EventCacheStore, like MemoryStore or SqliteStore). A new RoomEvents::updates_as_vector_diffs() method is added, it's going to be used later and it's useful to test the new behaviour now.

@Hywan Hywan changed the title efactor: RoomEvents::reset really clear the linked chunk refactor: RoomEvents::reset really clear the linked chunk Nov 25, 2024
@Hywan Hywan changed the title refactor: RoomEvents::reset really clear the linked chunk refactor: RoomEvents::reset really clear the linked chunk Nov 25, 2024
@Hywan Hywan changed the title refactor: RoomEvents::reset really clear the linked chunk refactor: RoomEvents::reset really clears the linked chunk Nov 25, 2024
@Hywan Hywan force-pushed the feat-sdk-event-cache-room-events-clear branch 2 times, most recently from 20b61fe to 64e8114 Compare November 26, 2024 09:42
@Hywan Hywan marked this pull request as ready for review November 26, 2024 09:45
@Hywan Hywan requested a review from a team as a code owner November 26, 2024 09:45
@Hywan Hywan requested review from poljar, stefanceriu and a team and removed request for a team and poljar November 26, 2024 09:45
@Hywan Hywan force-pushed the feat-sdk-event-cache-room-events-clear branch from 64e8114 to 19a86e9 Compare November 26, 2024 09:48
// An `Update::Clear` happens when `LinkedChunk::clear` is called, which removes
// all chunks, and re-adds one `Chunk` with `FIRST_IDENTIFIER` as its chunk
// identifier.
self.chunks.push_back((ChunkIdentifierGenerator::FIRST_IDENTIFIER, 0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's the wrong layer to do that, the linked chunk should save that update and propagate it instead; see also #4314 which describes the same issue at construction.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sure! Good idea. I'll update this PR to address that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, this PR has the correct approach now, but another PR is necessary to fully fix #4314. I'm already on it, but I think it's nice to keep them separate, otherwise the diff will be a bit confusing.

/// [`Update`]: matrix_sdk_base::linked_chunk::Update
#[allow(unused)] // gonna be useful very soon! but we need it now for test purposes
pub fn updates_as_vector_diffs(&mut self) -> Vec<VectorDiff<Event>> {
self.chunks_updates_as_vectordiffs.take()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have to store a chunks_updates_as_vectordiffs field, and why is not sufficient to expose the underlying method? Now that the underlying linked chunk is properly reset and not re-created, the stream won't close after a reset, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a Stream :-). If we recreate the AsVector everytime, it will lose all its states, and the VectorDiff would be pretty messy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we have to re-create an AsVector? I'm not sure about that.

Imagine we had:

pub fn as_vector(&self) -> ... {
  self.chunks.as_vector()
}

The resulting AsVector could live as long as the caller wants, and it would always be sync'd with the underlying linked chunk, as long as it lived. In that case, we wouldn't have to store anything new in the linked chunk. Is there any reason we cannot do this?

Copy link

codecov bot commented Nov 26, 2024

Codecov Report

Attention: Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.

Project coverage is 85.06%. Comparing base (c61f707) to head (149ca7d).
Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
crates/matrix-sdk-common/src/linked_chunk/mod.rs 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4321      +/-   ##
==========================================
- Coverage   85.07%   85.06%   -0.01%     
==========================================
  Files         275      275              
  Lines       30314    30325      +11     
==========================================
+ Hits        25789    25797       +8     
- Misses       4525     4528       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Hywan Hywan force-pushed the feat-sdk-event-cache-room-events-clear branch from 19a86e9 to c6e6705 Compare November 26, 2024 10:06
This patch fixes a bug in `AsVector`: when an `Update::Clear` value
is received, `AsVector`'s internal state must be cleared too, i.e. the
`UpdateToVectorDiff::chunks` field should be reset to an initial value!

This patch adds a test to ensure this works as expected.
This patch updates `RoomEvents::reset` to not drop the `LinkedChunk` to
clear it.
@Hywan Hywan force-pushed the feat-sdk-event-cache-room-events-clear branch from c6e6705 to 149ca7d Compare November 26, 2024 10:25
@Hywan Hywan requested a review from bnjbvr November 26, 2024 10:27
Copy link
Member

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still an open question before I approve, but this looks like this is on the right track!

Comment on lines +66 to +67
/// All events, all gaps, everything is dropped, move into the void, into
/// the ether, forever.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a blast, are you? :D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have no idea!

Comment on lines +57 to +59
// SAFETY: The `LinkedChunk` has been built with `new_with_update_history`, so
// `as_vector` must return `Some(…)`.
.expect("`LinkedChunk` must have been constructor with `new_with_update_history`");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid this expect, would it make sense that new_with_update_history() return the AsVector data structure too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, new_with_update_history() could return (LinkedChunk, Update), but I don't see why it should return an AsVector directly. Problem is: to compute an AsVector, it needs an &mut Update, so the constructor would have to return (LinkedChunk, &mut Update), where the &mut is a mutable reference to LinkedChunk itself. Er, that's not possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, too bad!

/// [`Update`]: matrix_sdk_base::linked_chunk::Update
#[allow(unused)] // gonna be useful very soon! but we need it now for test purposes
pub fn updates_as_vector_diffs(&mut self) -> Vec<VectorDiff<Event>> {
self.chunks_updates_as_vectordiffs.take()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we have to re-create an AsVector? I'm not sure about that.

Imagine we had:

pub fn as_vector(&self) -> ... {
  self.chunks.as_vector()
}

The resulting AsVector could live as long as the caller wants, and it would always be sync'd with the underlying linked chunk, as long as it lived. In that case, we wouldn't have to store anything new in the linked chunk. Is there any reason we cannot do this?

@Hywan
Copy link
Member Author

Hywan commented Nov 26, 2024

Would we have to re-create an AsVector?

First off, we create AsVector right after LinkedChunk because AsVector drains all its updates when constructed. What it means is: updates that are past the creation of AsVector are ignored. And that's mandatory because AsVector computes its initial states by looking at all chunks, that's much more efficient. AsVector will map Updates to VectorDiffs starting from the state of the LinkedChunk at the creation time of AsVector.

If we re-create an AsVector every time, I mean, it's possible and it would be correct, but that's clearly a waste of time and resources. AsVector would have to re-calculate its initial states by looping over all chunks. That's not huge, but still.

Also, this AsVector will be used by RoomEvents to broadcast the VectorDiff to the Timeline via RoomEventCacheInner. So the caller is technically RoomEventCacherInner, which holds a RoomEvents, but I think it's much simpler for RoomEvents to hold AsVector rather than RoomEventCacheInner to fetch a new AsVector every time a RoomEventCacheUpdate must be sent.
Either way, AsVector has to be stored somewhere: either by RoomEventCacheInner or by RoomEvents. I thought it was easier on RoomEvents because it's more confined and easier to test (one less layer).

I've always seen RoomEvents as a bridge between EventCache and LinkedChunk. Within this frame, it makes sense to have AsVector inside RoomEvents. RoomEventCacheInner and RoomEvents have the same lifetime since the former holds the latter.

@Hywan Hywan requested a review from bnjbvr November 26, 2024 14:11
Copy link
Member

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this AsVector will be used by RoomEvents to broadcast the VectorDiff to the Timeline via RoomEventCacheInner. So the caller is technically RoomEventCacherInner, which holds a RoomEvents, but I think it's much simpler for RoomEvents to hold AsVector rather than RoomEventCacheInner to fetch a new AsVector every time a RoomEventCacheUpdate must be sent.
Either way, AsVector has to be stored somewhere: either by RoomEventCacheInner or by RoomEvents. I thought it was easier on RoomEvents because it's more confined and easier to test (one less layer).

Thanks, I'm convinced by this 👍

Comment on lines +57 to +59
// SAFETY: The `LinkedChunk` has been built with `new_with_update_history`, so
// `as_vector` must return `Some(…)`.
.expect("`LinkedChunk` must have been constructor with `new_with_update_history`");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, too bad!

@Hywan Hywan merged commit cc8bc05 into matrix-org:main Nov 26, 2024
40 checks passed
@Hywan Hywan mentioned this pull request Nov 28, 2024
37 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants