mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-05 22:47:02 -04:00
task(event cache): address review points
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
CREATE TABLE "linked_chunks" (
|
||||
-- Identifier of the chunk, unique per room.
|
||||
-- Identifier of the chunk, unique per room. Corresponds to a `ChunkIdentifier`.
|
||||
"id" INTEGER,
|
||||
-- Which room does this chunk belong to? (hashed key shared with the two other tables)
|
||||
"room_id" BLOB NOT NULL,
|
||||
|
||||
-- Previous chunk in the linked list.
|
||||
-- Previous chunk in the linked list. Corresponds to a `ChunkIdentifier`.
|
||||
"previous" INTEGER,
|
||||
-- Next chunk in the linked list.
|
||||
-- Next chunk in the linked list. Corresponds to a `ChunkIdentifier`.
|
||||
"next" INTEGER,
|
||||
-- Type of underlying entries: E for event, G for gaps
|
||||
-- Type of underlying entries: E for events, G for gaps
|
||||
"type" TEXT CHECK("type" IN ('E', 'G')) NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "linked_chunks_id_and_room_id" ON linked_chunks (id, room_id);
|
||||
|
||||
CREATE TABLE "gaps" (
|
||||
-- Which chunk does this gap refer to?
|
||||
-- Which chunk does this gap refer to? Corresponds to a `ChunkIdentifier`.
|
||||
"chunk_id" INTEGER NOT NULL,
|
||||
-- Which room does this event belong to? (hashed key shared with linked_chunks)
|
||||
"room_id" BLOB NOT NULL,
|
||||
@@ -29,7 +29,7 @@ CREATE TABLE "gaps" (
|
||||
|
||||
-- Items for an event chunk.
|
||||
CREATE TABLE "events" (
|
||||
-- Which chunk does this event refer to?
|
||||
-- Which chunk does this event refer to? Corresponds to a `ChunkIdentifier`.
|
||||
"chunk_id" INTEGER NOT NULL,
|
||||
-- Which room does this event belong to? (hashed key shared with linked_chunks)
|
||||
"room_id" BLOB NOT NULL,
|
||||
|
||||
@@ -50,6 +50,13 @@ mod keys {
|
||||
/// the [`run_migrations`] function.
|
||||
const DATABASE_VERSION: u8 = 3;
|
||||
|
||||
/// The string used to identify a chunk of type events, in the `type` field in
|
||||
/// the database.
|
||||
const CHUNK_TYPE_EVENT_TYPE_STRING: &str = "E";
|
||||
/// The string used to identify a chunk of type gap, in the `type` field in the
|
||||
/// database.
|
||||
const CHUNK_TYPE_GAP_TYPE_STRING: &str = "G";
|
||||
|
||||
/// A SQLite-based event cache store.
|
||||
#[derive(Clone)]
|
||||
pub struct SqliteEventCacheStore {
|
||||
@@ -149,6 +156,7 @@ impl SqliteEventCacheStore {
|
||||
.with_transaction(move |txn| -> Result<_> {
|
||||
let mut items = Vec::new();
|
||||
|
||||
// Use `ORDER BY id` to get a deterministic ordering for testing purposes.
|
||||
for data in txn
|
||||
.prepare(
|
||||
"SELECT id, previous, next, type FROM linked_chunks WHERE room_id = ? ORDER BY id",
|
||||
@@ -238,14 +246,14 @@ impl TransactionExtForLinkedChunks for Transaction<'_> {
|
||||
let id = ChunkIdentifier::new(id);
|
||||
|
||||
match chunk_type {
|
||||
"G" => {
|
||||
CHUNK_TYPE_GAP_TYPE_STRING => {
|
||||
// It's a gap! There's at most one row for it in the database, so a
|
||||
// call to `query_row` is sufficient.
|
||||
let gap = self.load_gap_content(store, room_id, id)?;
|
||||
Ok(RawLinkedChunk { content: ChunkContent::Gap(gap), previous, id, next })
|
||||
}
|
||||
|
||||
"E" => {
|
||||
CHUNK_TYPE_EVENT_TYPE_STRING => {
|
||||
// It's events!
|
||||
let events = self.load_events_content(store, room_id, id)?;
|
||||
Ok(RawLinkedChunk { content: ChunkContent::Items(events), previous, id, next })
|
||||
@@ -421,7 +429,14 @@ impl EventCacheStore for SqliteEventCacheStore {
|
||||
self.acquire()
|
||||
.await?
|
||||
.with_transaction(move |txn| {
|
||||
insert_chunk(txn, &hashed_room_id, previous, new, next, "E")
|
||||
insert_chunk(
|
||||
txn,
|
||||
&hashed_room_id,
|
||||
previous,
|
||||
new,
|
||||
next,
|
||||
CHUNK_TYPE_EVENT_TYPE_STRING,
|
||||
)
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
@@ -445,11 +460,16 @@ impl EventCacheStore for SqliteEventCacheStore {
|
||||
.await?
|
||||
.with_transaction(move |txn| -> rusqlite::Result<()> {
|
||||
// Insert the chunk as a gap.
|
||||
insert_chunk(txn, &hashed_room_id, previous, new, next, "G")?;
|
||||
insert_chunk(
|
||||
txn,
|
||||
&hashed_room_id,
|
||||
previous,
|
||||
new,
|
||||
next,
|
||||
CHUNK_TYPE_GAP_TYPE_STRING,
|
||||
)?;
|
||||
|
||||
// Insert the gap's value.
|
||||
// XXX(bnjbvr): might as well inline in the linked_chunks table? better
|
||||
// for flexibility to use another table though.
|
||||
txn.execute(
|
||||
r#"
|
||||
INSERT INTO gaps(chunk_id, room_id, prev_token)
|
||||
@@ -967,27 +987,64 @@ mod tests {
|
||||
next: None,
|
||||
gap: Gap { prev_token: "raclette".to_owned() },
|
||||
},
|
||||
Update::RemoveChunk(ChunkIdentifier::new(42)),
|
||||
Update::NewGapChunk {
|
||||
previous: Some(ChunkIdentifier::new(42)),
|
||||
new: ChunkIdentifier::new(43),
|
||||
next: None,
|
||||
gap: Gap { prev_token: "fondue".to_owned() },
|
||||
},
|
||||
Update::NewGapChunk {
|
||||
previous: Some(ChunkIdentifier::new(43)),
|
||||
new: ChunkIdentifier::new(44),
|
||||
next: None,
|
||||
gap: Gap { prev_token: "tartiflette".to_owned() },
|
||||
},
|
||||
Update::RemoveChunk(ChunkIdentifier::new(43)),
|
||||
],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chunks = store.load_chunks(room_id).await.unwrap();
|
||||
assert!(chunks.is_empty());
|
||||
let mut chunks = store.load_chunks(room_id).await.unwrap();
|
||||
|
||||
assert_eq!(chunks.len(), 2);
|
||||
|
||||
// Chunks are ordered from smaller to bigger IDs.
|
||||
let c = chunks.remove(0);
|
||||
assert_eq!(c.id, ChunkIdentifier::new(42));
|
||||
assert_eq!(c.previous, None);
|
||||
assert_eq!(c.next, Some(ChunkIdentifier::new(44)));
|
||||
assert_matches!(c.content, ChunkContent::Gap(gap) => {
|
||||
assert_eq!(gap.prev_token, "raclette");
|
||||
});
|
||||
|
||||
let c = chunks.remove(0);
|
||||
assert_eq!(c.id, ChunkIdentifier::new(44));
|
||||
assert_eq!(c.previous, Some(ChunkIdentifier::new(42)));
|
||||
assert_eq!(c.next, None);
|
||||
assert_matches!(c.content, ChunkContent::Gap(gap) => {
|
||||
assert_eq!(gap.prev_token, "tartiflette");
|
||||
});
|
||||
|
||||
// Check that cascading worked. Yes, sqlite, I doubt you.
|
||||
let num_gaps: u64 = store
|
||||
let gaps = store
|
||||
.acquire()
|
||||
.await
|
||||
.unwrap()
|
||||
.with_transaction(|txn| {
|
||||
txn.query_row("SELECT COUNT(*) FROM gaps", (), |row| row.get(0))
|
||||
.with_transaction(|txn| -> rusqlite::Result<_> {
|
||||
let mut gaps = Vec::new();
|
||||
for data in txn
|
||||
.prepare("SELECT chunk_id FROM gaps ORDER BY chunk_id")?
|
||||
.query_map((), |row| row.get::<_, u64>(0))?
|
||||
{
|
||||
gaps.push(data?);
|
||||
}
|
||||
Ok(gaps)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(num_gaps, 0);
|
||||
assert_eq!(gaps, vec![42, 44]);
|
||||
}
|
||||
|
||||
fn make_test_event(content: &str) -> SyncTimelineEvent {
|
||||
|
||||
Reference in New Issue
Block a user