@@ -19,7 +19,7 @@ use matrix_sdk_base::{
19
19
event_cache:: store:: DEFAULT_CHUNK_CAPACITY ,
20
20
linked_chunk:: {
21
21
lazy_loader:: { self , LazyLoaderError } ,
22
- ChunkContent , ChunkIdentifierGenerator , RawChunk ,
22
+ ChunkContent , ChunkIdentifierGenerator , ChunkMetadata , OrderTracker , RawChunk ,
23
23
} ,
24
24
} ;
25
25
use matrix_sdk_common:: linked_chunk:: {
@@ -38,6 +38,9 @@ pub(in crate::event_cache) struct EventLinkedChunk {
38
38
///
39
39
/// [`Update`]: matrix_sdk_base::linked_chunk::Update
40
40
chunks_updates_as_vectordiffs : AsVector < Event , Gap > ,
41
+
42
+ /// Tracker of the events ordering in this room.
43
+ pub order_tracker : OrderTracker < Event , Gap > ,
41
44
}
42
45
43
46
impl Default for EventLinkedChunk {
@@ -49,22 +52,27 @@ impl Default for EventLinkedChunk {
49
52
impl EventLinkedChunk {
50
53
/// Build a new [`EventLinkedChunk`] struct with zero events.
51
54
pub fn new ( ) -> Self {
52
- Self :: with_initial_linked_chunk ( None )
55
+ Self :: with_initial_linked_chunk ( None , None )
53
56
}
54
57
55
58
/// Build a new [`EventLinkedChunk`] struct with prior chunks knowledge.
56
59
///
57
60
/// The provided [`LinkedChunk`] must have been built with update history.
58
61
pub fn with_initial_linked_chunk (
59
62
linked_chunk : Option < LinkedChunk < DEFAULT_CHUNK_CAPACITY , Event , Gap > > ,
63
+ full_linked_chunk_metadata : Option < Vec < ChunkMetadata > > ,
60
64
) -> Self {
61
65
let mut linked_chunk = linked_chunk. unwrap_or_else ( LinkedChunk :: new_with_update_history) ;
62
66
63
67
let chunks_updates_as_vectordiffs = linked_chunk
64
68
. as_vector ( )
65
69
. expect ( "`LinkedChunk` must have been built with `new_with_update_history`" ) ;
66
70
67
- Self { chunks : linked_chunk, chunks_updates_as_vectordiffs }
71
+ let order_tracker = linked_chunk
72
+ . order_tracker ( full_linked_chunk_metadata)
73
+ . expect ( "`LinkedChunk` must have been built with `new_with_update_history`" ) ;
74
+
75
+ Self { chunks : linked_chunk, chunks_updates_as_vectordiffs, order_tracker }
68
76
}
69
77
70
78
/// Clear all events.
@@ -186,6 +194,36 @@ impl EventLinkedChunk {
186
194
self . chunks . items ( )
187
195
}
188
196
197
+ /// Return the order of an event in the room linked chunk.
198
+ ///
199
+ /// Can return `None` if the event can't be found in the linked chunk.
200
+ pub fn event_order ( & self , event_pos : Position ) -> Option < usize > {
201
+ self . order_tracker . ordering ( event_pos)
202
+ }
203
+
204
+ #[ cfg( any( test, debug_assertions) ) ]
205
+ #[ allow( dead_code) ] // Temporarily, until we figure out why it's crashing production builds.
206
+ fn assert_event_ordering ( & self ) {
207
+ let mut iter = self . chunks . items ( ) . enumerate ( ) ;
208
+ let Some ( ( i, ( first_event_pos, _) ) ) = iter. next ( ) else {
209
+ return ;
210
+ } ;
211
+
212
+ // Sanity check.
213
+ assert_eq ! ( i, 0 ) ;
214
+
215
+ // That's the offset in the full linked chunk. Will be 0 if the linked chunk is
216
+ // entirely loaded, may be non-zero otherwise.
217
+ let offset =
218
+ self . event_order ( first_event_pos) . expect ( "first event's ordering must be known" ) ;
219
+
220
+ for ( i, ( next_pos, _) ) in iter {
221
+ let next_index =
222
+ self . event_order ( next_pos) . expect ( "next event's ordering must be known" ) ;
223
+ assert_eq ! ( offset + i, next_index, "event ordering must be continuous" ) ;
224
+ }
225
+ }
226
+
189
227
/// Get all updates from the room events as [`VectorDiff`].
190
228
///
191
229
/// Be careful that each `VectorDiff` is returned only once!
@@ -194,7 +232,11 @@ impl EventLinkedChunk {
194
232
///
195
233
/// [`Update`]: matrix_sdk_base::linked_chunk::Update
196
234
pub fn updates_as_vector_diffs ( & mut self ) -> Vec < VectorDiff < Event > > {
197
- self . chunks_updates_as_vectordiffs . take ( )
235
+ let updates = self . chunks_updates_as_vectordiffs . take ( ) ;
236
+
237
+ self . order_tracker . flush_updates ( false ) ;
238
+
239
+ updates
198
240
}
199
241
200
242
/// Get a mutable reference to the [`LinkedChunk`] updates, aka
@@ -214,7 +256,8 @@ impl EventLinkedChunk {
214
256
let mut result = Vec :: new ( ) ;
215
257
216
258
for chunk in self . chunks . chunks ( ) {
217
- let content = chunk_debug_string ( chunk. content ( ) ) ;
259
+ let content =
260
+ chunk_debug_string ( chunk. identifier ( ) , chunk. content ( ) , & self . order_tracker ) ;
218
261
let lazy_previous = if let Some ( cid) = chunk. lazy_previous ( ) {
219
262
format ! ( " (lazy previous = {})" , cid. index( ) )
220
263
} else {
@@ -357,6 +400,28 @@ impl EventLinkedChunk {
357
400
358
401
// Methods related to lazy-loading.
359
402
impl EventLinkedChunk {
403
+ /// Inhibits all the linked chunk updates caused by the function `f` on the
404
+ /// ordering tracker.
405
+ ///
406
+ /// Updates to the linked chunk that happen because of lazy loading must not
407
+ /// be taken into account by the order tracker, otherwise the
408
+ /// fully-loaded state (tracked by the order tracker) wouldn't match
409
+ /// reality anymore. This provides a facility to help applying such
410
+ /// updates.
411
+ fn inhibit_updates_to_ordering_tracker < F : FnOnce ( & mut Self ) -> R , R > ( & mut self , f : F ) -> R {
412
+ // Start by flushing previous pending updates to the chunk ordering, if any.
413
+ self . order_tracker . flush_updates ( false ) ;
414
+
415
+ // Call the function.
416
+ let r = f ( self ) ;
417
+
418
+ // Now, flush other pending updates which have been caused by the function, and
419
+ // ignore them.
420
+ self . order_tracker . flush_updates ( true ) ;
421
+
422
+ r
423
+ }
424
+
360
425
/// Replace the events with the given last chunk of events and generator.
361
426
///
362
427
/// Happens only during lazy loading.
@@ -368,33 +433,51 @@ impl EventLinkedChunk {
368
433
last_chunk : Option < RawChunk < Event , Gap > > ,
369
434
chunk_identifier_generator : ChunkIdentifierGenerator ,
370
435
) -> Result < ( ) , LazyLoaderError > {
371
- lazy_loader:: replace_with ( & mut self . chunks , last_chunk, chunk_identifier_generator)
436
+ // Since `replace_with` is used only to unload some chunks, we don't want it to
437
+ // affect the chunk ordering.
438
+ self . inhibit_updates_to_ordering_tracker ( move |this| {
439
+ lazy_loader:: replace_with ( & mut this. chunks , last_chunk, chunk_identifier_generator)
440
+ } )
372
441
}
373
442
374
443
/// Prepends a lazily-loaded chunk at the beginning of the linked chunk.
375
444
pub ( super ) fn insert_new_chunk_as_first (
376
445
& mut self ,
377
446
raw_new_first_chunk : RawChunk < Event , Gap > ,
378
447
) -> Result < ( ) , LazyLoaderError > {
379
- lazy_loader:: insert_new_first_chunk ( & mut self . chunks , raw_new_first_chunk)
448
+ // This is only used when reinserting a chunk that was in persisted storage, so
449
+ // we don't need to touch the chunk ordering for this.
450
+ self . inhibit_updates_to_ordering_tracker ( move |this| {
451
+ lazy_loader:: insert_new_first_chunk ( & mut this. chunks , raw_new_first_chunk)
452
+ } )
380
453
}
381
454
}
382
455
383
456
/// Create a debug string for a [`ChunkContent`] for an event/gap pair.
384
- fn chunk_debug_string ( content : & ChunkContent < Event , Gap > ) -> String {
457
+ fn chunk_debug_string (
458
+ chunk_id : ChunkIdentifier ,
459
+ content : & ChunkContent < Event , Gap > ,
460
+ order_tracker : & OrderTracker < Event , Gap > ,
461
+ ) -> String {
385
462
match content {
386
463
ChunkContent :: Gap ( Gap { prev_token } ) => {
387
464
format ! ( "gap['{prev_token}']" )
388
465
}
389
466
ChunkContent :: Items ( vec) => {
390
467
let items = vec
391
468
. iter ( )
392
- . map ( |event| {
469
+ . enumerate ( )
470
+ . map ( |( i, event) | {
393
471
event. event_id ( ) . map_or_else (
394
472
|| "<no event id>" . to_owned ( ) ,
395
473
|id| {
474
+ let pos = Position :: new ( chunk_id, i) ;
475
+ let order = format ! ( "#{}: " , order_tracker. ordering( pos) . unwrap( ) ) ;
476
+
396
477
// Limit event ids to 8 chars *after* the $.
397
- id. as_str ( ) . chars ( ) . take ( 1 + 8 ) . collect :: < String > ( )
478
+ let event_id = id. as_str ( ) . chars ( ) . take ( 1 + 8 ) . collect :: < String > ( ) ;
479
+
480
+ format ! ( "{order}{event_id}" )
398
481
} ,
399
482
)
400
483
} )
@@ -679,10 +762,13 @@ mod tests {
679
762
] ) ;
680
763
linked_chunk. chunks . push_gap_back ( Gap { prev_token : "raclette" . to_owned ( ) } ) ;
681
764
765
+ // Flush updates to the order tracker.
766
+ let _ = linked_chunk. updates_as_vector_diffs ( ) ;
767
+
682
768
let output = linked_chunk. debug_string ( ) ;
683
769
684
770
assert_eq ! ( output. len( ) , 2 ) ;
685
- assert_eq ! ( & output[ 0 ] , "chunk #0: events[$12345678, $2]" ) ;
771
+ assert_eq ! ( & output[ 0 ] , "chunk #0: events[#0: $12345678, #1: $2]" ) ;
686
772
assert_eq ! ( & output[ 1 ] , "chunk #1: gap['raclette']" ) ;
687
773
}
688
774
0 commit comments