Skip to content

Commit 8c1d3f4

Browse files
committed
feat(sdk): RoomEventCacheInner::backpaginate always return Ok for unknown token.
Prior to this patch, in `RoomEventCacheInner::backpaginate`, when the `token` validity was checked, and it was invalid: * before calling `/messages`, `Err(EventCacheError::UnknownBackpaginationToken)` was returned, * after calling `/messages`, `Ok(BackPaginationOutput::UnknownBackpaginationToken)` was returned. This patch tries to uniformize this by only returning `Ok(BackPaginationOutput::UnknownBackpaginationToken)`. That's a tradeoff. It will probably be refactor later. The idea is also to call `/messages` **before** taking the write-lock of `RoomEvents`, otherwise it can keep the lock for up to 30secs in this case. Also, checking the validity of the `token` **before** and **after** `/messages` is not necessary: it can be done only after.
1 parent a623215 commit 8c1d3f4

File tree

2 files changed

+60
-45
lines changed

2 files changed

+60
-45
lines changed

crates/matrix-sdk/src/event_cache/mod.rs

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -632,9 +632,18 @@ impl RoomEventCacheInner {
632632
// Make sure there's at most one back-pagination request.
633633
let _guard = self.pagination_lock.lock().await;
634634

635-
// Make sure the `RoomEvents` isn't updated while we are back-paginating. This
636-
// is really important, for example if a sync is happening while we are
637-
// back-paginating.
635+
// Get messages.
636+
let messages = self
637+
.room
638+
.messages(assign!(MessagesOptions::backward(), {
639+
from: token.as_ref().map(|token| token.0.clone()),
640+
limit: batch_size.into()
641+
}))
642+
.await
643+
.map_err(EventCacheError::SdkError)?;
644+
645+
// Make sure the `RoomEvents` isn't updated while we are saving events from
646+
// backpagination.
638647
let mut room_events = self.events.write().await;
639648

640649
// Check that the `token` exists if any.
@@ -646,24 +655,14 @@ impl RoomEventCacheInner {
646655
// The method has been called with `token` but it doesn't exist in `RoomEvents`,
647656
// it's an error.
648657
if gap_identifier.is_none() {
649-
return Err(EventCacheError::UnknownBackpaginationToken);
658+
return Ok(BackPaginationOutcome::UnknownBackpaginationToken);
650659
}
651660

652661
gap_identifier
653662
} else {
654663
None
655664
};
656665

657-
// Get messages.
658-
let messages = self
659-
.room
660-
.messages(assign!(MessagesOptions::backward(), {
661-
from: token.as_ref().map(|token| token.0.clone()),
662-
limit: batch_size.into()
663-
}))
664-
.await
665-
.map_err(EventCacheError::SdkError)?;
666-
667666
// Would we want to backpaginate again, we'd start from the `end` token as the
668667
// next `from` token.
669668

@@ -834,7 +833,7 @@ mod tests {
834833
use matrix_sdk_test::{async_test, sync_timeline_event};
835834
use ruma::room_id;
836835

837-
use super::EventCacheError;
836+
use super::{BackPaginationOutcome, EventCacheError};
838837
use crate::{event_cache::store::PaginationToken, test_utils::logged_in_client};
839838

840839
#[async_test]
@@ -853,35 +852,57 @@ mod tests {
853852
assert_matches!(result, Err(EventCacheError::NotSubscribedYet));
854853
}
855854

856-
#[async_test]
857-
async fn test_unknown_pagination_token() {
858-
let client = logged_in_client(None).await;
859-
let room_id = room_id!("!galette:saucisse.bzh");
860-
client.base_client().get_or_create_room(room_id, matrix_sdk_base::RoomState::Joined);
861-
862-
client.event_cache().subscribe().unwrap();
863-
864-
let (room_event_cache, _drop_handles) =
865-
client.event_cache().for_room(room_id).await.unwrap();
866-
let room_event_cache = room_event_cache.unwrap();
867-
868-
// If I try to back-paginate with an unknown back-pagination token,
869-
let token = PaginationToken("old".to_owned());
870-
871-
// Then I run into an error.
872-
let res = room_event_cache.backpaginate(20, Some(token)).await;
873-
assert_matches!(res.unwrap_err(), EventCacheError::UnknownBackpaginationToken);
874-
}
875-
876855
// Those tests require time to work, and it does not on wasm32.
877856
#[cfg(not(target_arch = "wasm32"))]
878857
mod time_tests {
879858
use std::time::{Duration, Instant};
880859

881860
use matrix_sdk_base::RoomState;
861+
use serde_json::json;
882862
use tokio::time::sleep;
863+
use wiremock::{
864+
matchers::{header, method, path_regex, query_param},
865+
Mock, ResponseTemplate,
866+
};
883867

884868
use super::{super::store::Gap, *};
869+
use crate::test_utils::logged_in_client_with_server;
870+
871+
#[async_test]
872+
async fn test_unknown_pagination_token() {
873+
let (client, server) = logged_in_client_with_server().await;
874+
875+
let room_id = room_id!("!galette:saucisse.bzh");
876+
client.base_client().get_or_create_room(room_id, matrix_sdk_base::RoomState::Joined);
877+
878+
client.event_cache().subscribe().unwrap();
879+
880+
let (room_event_cache, _drop_handles) =
881+
client.event_cache().for_room(room_id).await.unwrap();
882+
let room_event_cache = room_event_cache.unwrap();
883+
884+
// If I try to back-paginate with an unknown back-pagination token,
885+
let token_name = "unknown";
886+
let token = PaginationToken(token_name.to_owned());
887+
888+
// Then I run into an error.
889+
Mock::given(method("GET"))
890+
.and(path_regex(r"^/_matrix/client/r0/rooms/.*/messages$"))
891+
.and(header("authorization", "Bearer 1234"))
892+
.and(query_param("from", token_name))
893+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
894+
"start": token_name,
895+
"chunk": [],
896+
})))
897+
.expect(1)
898+
.mount(&server)
899+
.await;
900+
901+
let res = room_event_cache.backpaginate(20, Some(token)).await;
902+
assert_matches!(res, Ok(BackPaginationOutcome::UnknownBackpaginationToken));
903+
904+
server.verify().await
905+
}
885906

886907
#[async_test]
887908
async fn test_wait_no_pagination_token() {

crates/matrix-sdk/tests/integration/event_cache.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -515,17 +515,11 @@ async fn test_reset_while_backpaginating() {
515515
mock_sync(&server, sync_response_body, None).await;
516516
client.sync_once(Default::default()).await.unwrap();
517517

518-
let outcome = backpagination.await.expect("join failed").unwrap();
518+
let outcome = backpagination.await.expect("join failed");
519519

520-
// Backpagination should have been executed before the sync, despite the
521-
// concurrency here. The backpagination should have acquired a write lock before
522-
// the sync.
523-
{
524-
assert_let!(BackPaginationOutcome::Success { events, reached_start } = outcome);
525-
assert!(!reached_start);
526-
assert_event_matches_msg(&events[0].clone().into(), "lalala");
527-
assert_eq!(events.len(), 1);
528-
}
520+
// Backpagination should be confused, and the operation should result in an
521+
// unknown token.
522+
assert_matches!(outcome, Ok(BackPaginationOutcome::UnknownBackpaginationToken));
529523

530524
// Now if we retrieve the earliest token, it's not the one we had before.
531525
// Even better, it's the one from the sync, NOT from the backpagination!

0 commit comments

Comments
 (0)