Skip to main content

loro_internal/oplog/
change_store.rs

1use self::block_encode::{
2    decode_block, decode_cids, decode_header, encode_block, ChangesBlockHeader,
3};
4use super::{loro_dag::AppDagNodeInner, AppDagNode};
5use crate::sync::Mutex;
6use crate::{
7    arena::SharedArena,
8    change::Change,
9    estimated_size::EstimatedSize,
10    kv_store::KvStore,
11    op::Op,
12    parent::register_container_and_parent_link,
13    version::{Frontiers, ImVersionVector},
14    InternalString, VersionVector,
15};
16use block_encode::decode_block_range;
17use bytes::Bytes;
18use itertools::Itertools;
19use loro_common::{
20    ContainerID, Counter, HasCounterSpan, HasId, HasIdSpan, HasLamportSpan, IdLp, IdSpan, Lamport,
21    LoroError, LoroResult, PeerID, ID,
22};
23use loro_kv_store::{mem_store::MemKvConfig, MemKvStore};
24use once_cell::sync::OnceCell;
25use rle::{HasLength, Mergable, RlePush, RleVec, Sliceable};
26use rustc_hash::FxHashSet;
27use std::sync::atomic::AtomicI64;
28#[cfg(test)]
29use std::sync::atomic::AtomicUsize;
30use std::{
31    cmp::Ordering,
32    collections::{BTreeMap, VecDeque},
33    ops::{Bound, Deref},
34    sync::Arc,
35};
36use tracing::{info_span, warn};
37mod block_encode;
38mod block_meta_encode;
39pub(super) mod iter;
40
41#[cfg(not(test))]
42const MAX_BLOCK_SIZE: usize = 1024 * 4;
43#[cfg(test)]
44const MAX_BLOCK_SIZE: usize = 128;
45const MAX_ROOT_HISTORY_NAME_BYTES: usize = 256 * 1024;
46
47/// # Invariance
48///
49/// - We don't allow holes in a block or between two blocks with the same peer id.
50///   The [Change] should be continuous for each peer.
51/// - However, the first block of a peer can have counter > 0 so that we can trim the history.
52///
53/// # Encoding Schema
54///
55/// It's based on the underlying KV store.
56///
57/// The entries of the KV store is made up of the following fields
58///
59/// |Key                          |Value             |
60/// |:--                          |:----             |
61/// |b"vv"                        |VersionVector     |
62/// |b"fr"                        |Frontiers         |
63/// |b"sv"                        |Shallow VV        |
64/// |b"sf"                        |Shallow Frontiers |
65/// |12 bytes PeerID + Counter    |Encoded Block     |
66#[derive(Debug, Clone)]
67pub struct ChangeStore {
68    inner: Arc<Mutex<ChangeStoreInner>>,
69    arena: SharedArena,
70    /// A change may be in external_kv or in the mem_parsed_kv.
71    /// mem_parsed_kv is more up-to-date.
72    ///
73    /// We cannot directly write into the external_kv except from the initial load
74    external_kv: Arc<Mutex<dyn KvStore>>,
75    /// The version vector of the external kv store.
76    external_vv: Arc<Mutex<VersionVector>>,
77    merge_interval: Arc<AtomicI64>,
78    root_history_names: Arc<Mutex<RootHistoryNamesState>>,
79    #[cfg(test)]
80    root_history_scan_count: Arc<AtomicUsize>,
81}
82
83/// A conservative, size-capped set of every top-level root name that appears in the store's
84/// history. It only answers "may history have touched this root?", so retaining stale names
85/// (e.g. from a rolled-back import) is harmless — it just forces the general diff path.
86#[derive(Debug, Default)]
87enum RootHistoryNamesState {
88    #[default]
89    Uninitialized,
90    Valid {
91        names: FxHashSet<InternalString>,
92        name_bytes: usize,
93    },
94    Invalid,
95}
96
97/// Return false when retaining the name would exceed the fixed memory bound.
98fn record_root_name(
99    names: &mut FxHashSet<InternalString>,
100    name_bytes: &mut usize,
101    cid: &ContainerID,
102) -> bool {
103    let ContainerID::Root { name, .. } = cid else {
104        return true;
105    };
106    if cid.is_mergeable() || names.contains(name) {
107        return true;
108    }
109
110    let Some(new_bytes) = name_bytes.checked_add(name.len()) else {
111        return false;
112    };
113    if new_bytes > MAX_ROOT_HISTORY_NAME_BYTES {
114        return false;
115    }
116
117    names.insert(name.clone());
118    *name_bytes = new_bytes;
119    true
120}
121
122#[derive(Debug, Clone)]
123struct ChangeStoreInner {
124    /// The start version vector of the first block for each peer.
125    /// It allows us to trim the history
126    start_vv: ImVersionVector,
127    /// The last version of the shallow history.
128    start_frontiers: Frontiers,
129    /// It's more like a parsed cache for binary_kv.
130    mem_parsed_kv: BTreeMap<ID, Arc<ChangesBlock>>,
131}
132
133#[derive(Debug)]
134pub(crate) struct ChangeStoreRollback {
135    old_vv: VersionVector,
136    blocks_before_mutation: BTreeMap<ID, Arc<ChangesBlock>>,
137}
138
139impl ChangeStoreRollback {
140    pub(crate) fn new(old_vv: VersionVector) -> Self {
141        Self {
142            old_vv,
143            blocks_before_mutation: BTreeMap::new(),
144        }
145    }
146
147    fn record_block_before_mutation(&mut self, id: ID, block: Arc<ChangesBlock>) {
148        let old_end = self.old_vv.get(&id.peer).copied().unwrap_or(0);
149        if id.counter >= old_end {
150            return;
151        }
152
153        self.blocks_before_mutation.entry(id).or_insert(block);
154    }
155}
156
157#[derive(Debug, Clone)]
158pub(crate) struct ChangesBlock {
159    peer: PeerID,
160    counter_range: (Counter, Counter),
161    lamport_range: (Lamport, Lamport),
162    /// Estimated size of the block in bytes
163    estimated_size: usize,
164    flushed: bool,
165    content: ChangesBlockContent,
166}
167
168#[derive(Clone)]
169pub(crate) enum ChangesBlockContent {
170    Changes(Arc<Vec<Change>>),
171    Bytes(ChangesBlockBytes),
172    Both(Arc<Vec<Change>>, ChangesBlockBytes),
173}
174
175/// It's cheap to clone this struct because it's cheap to clone the bytes
176#[derive(Clone)]
177pub(crate) struct ChangesBlockBytes {
178    bytes: Bytes,
179    header: OnceCell<Arc<ChangesBlockHeader>>,
180}
181
182pub const START_VV_KEY: &[u8] = b"sv";
183pub const START_FRONTIERS_KEY: &[u8] = b"sf";
184pub const VV_KEY: &[u8] = b"vv";
185pub const FRONTIERS_KEY: &[u8] = b"fr";
186
187impl ChangeStore {
188    pub fn new_mem(a: &SharedArena, merge_interval: Arc<AtomicI64>) -> Self {
189        Self {
190            inner: Arc::new(Mutex::new(ChangeStoreInner {
191                start_vv: ImVersionVector::new(),
192                start_frontiers: Frontiers::default(),
193                mem_parsed_kv: BTreeMap::new(),
194            })),
195            arena: a.clone(),
196            external_vv: Arc::new(Mutex::new(VersionVector::new())),
197            external_kv: Arc::new(Mutex::new(MemKvStore::new(MemKvConfig::default()))),
198            // external_kv: Arc::new(Mutex::new(BTreeMap::default())),
199            merge_interval,
200            root_history_names: Arc::new(Mutex::new(RootHistoryNamesState::Uninitialized)),
201            #[cfg(test)]
202            root_history_scan_count: Arc::new(AtomicUsize::new(0)),
203        }
204    }
205
206    #[cfg(test)]
207    fn new_for_test() -> Self {
208        Self::new_mem(&SharedArena::new(), Arc::new(AtomicI64::new(0)))
209    }
210
211    pub(super) fn encode_all(&self, vv: &VersionVector, frontiers: &Frontiers) -> Bytes {
212        self.flush_and_compact(vv, frontiers);
213        let mut kv = self.external_kv.lock();
214        kv.export_all()
215    }
216
217    #[tracing::instrument(skip(self), level = "debug")]
218    pub(super) fn export_from(
219        &self,
220        start_vv: &VersionVector,
221        start_frontiers: &Frontiers,
222        latest_vv: &VersionVector,
223        latest_frontiers: &Frontiers,
224    ) -> Bytes {
225        let new_store = ChangeStore::new_mem(&self.arena, self.merge_interval.clone());
226        for span in latest_vv.sub_iter(start_vv) {
227            // PERF: this can be optimized by reusing the current encoded blocks
228            // In the current method, it needs to parse and re-encode the blocks
229            for c in self.iter_changes(span) {
230                let start = ((start_vv.get(&c.id.peer).copied().unwrap_or(0) - c.id.counter).max(0)
231                    as usize)
232                    .min(c.atom_len());
233                let end = ((latest_vv.get(&c.id.peer).copied().unwrap_or(0) - c.id.counter).max(0)
234                    as usize)
235                    .min(c.atom_len());
236
237                if start == end {
238                    continue;
239                }
240
241                let ch = c.slice(start, end);
242                new_store.insert_change(ch, false, false);
243            }
244        }
245
246        loro_common::debug!(
247            "start_vv={:?} start_frontiers={:?}",
248            &start_vv,
249            start_frontiers
250        );
251        new_store.encode_from(start_vv, start_frontiers, latest_vv, latest_frontiers)
252    }
253
254    pub(super) fn export_blocks_in_range<W: std::io::Write>(&self, spans: &[IdSpan], w: &mut W) {
255        let new_store = ChangeStore::new_mem(&self.arena, self.merge_interval.clone());
256        for span in spans {
257            let mut span = *span;
258            span.normalize_();
259            if span.counter.end <= 0 {
260                continue;
261            }
262
263            span.counter.start = span.counter.start.max(0);
264            span.counter.end = span.counter.end.max(0);
265            if span.counter.start >= span.counter.end {
266                continue;
267            }
268
269            // PERF: this can be optimized by reusing the current encoded blocks
270            // In the current method, it needs to parse and re-encode the blocks
271            for c in self.iter_changes(span) {
272                let start = ((span.counter.start - c.id.counter).max(0) as usize).min(c.atom_len());
273                let end = ((span.counter.end - c.id.counter).max(0) as usize).min(c.atom_len());
274                if start == end {
275                    continue;
276                }
277
278                let ch = c.slice(start, end);
279                new_store.insert_change(ch, false, false);
280            }
281        }
282
283        encode_blocks_in_store(new_store, &self.arena, w);
284    }
285
286    fn encode_from(
287        &self,
288        start_vv: &VersionVector,
289        start_frontiers: &Frontiers,
290        latest_vv: &VersionVector,
291        latest_frontiers: &Frontiers,
292    ) -> Bytes {
293        {
294            let mut store = self.external_kv.lock();
295            store.set(START_VV_KEY, start_vv.encode().into());
296            store.set(START_FRONTIERS_KEY, start_frontiers.encode().into());
297            let mut inner = self.inner.lock();
298            inner.start_frontiers = start_frontiers.clone();
299            inner.start_vv = ImVersionVector::from_vv(start_vv);
300        }
301        self.flush_and_compact(latest_vv, latest_frontiers);
302        self.external_kv.lock().export_all()
303    }
304
305    pub(crate) fn decode_snapshot_for_updates(
306        bytes: Bytes,
307        arena: &SharedArena,
308        self_vv: &VersionVector,
309    ) -> Result<Vec<Change>, LoroError> {
310        let change_store = ChangeStore::new_mem(arena, Arc::new(AtomicI64::new(0)));
311        let _ = change_store.import_all(bytes)?;
312        let mut changes = Vec::new();
313        change_store.visit_all_changes(&mut |c| {
314            let cnt_threshold = self_vv.get(&c.id.peer).copied().unwrap_or(0);
315            if c.id.counter >= cnt_threshold {
316                changes.push(c.clone());
317                return;
318            }
319
320            let change_end = c.ctr_end();
321            if change_end > cnt_threshold {
322                changes.push(c.slice((cnt_threshold - c.id.counter) as usize, c.atom_len()));
323            }
324        });
325
326        Ok(changes)
327    }
328
329    pub(crate) fn decode_block_bytes(
330        bytes: Bytes,
331        arena: &SharedArena,
332        self_vv: &VersionVector,
333    ) -> LoroResult<Vec<Change>> {
334        let mut ans = ChangesBlockBytes::new(bytes).parse(arena)?;
335        if ans.is_empty() {
336            return Ok(ans);
337        }
338
339        let start = self_vv.get(&ans[0].peer()).copied().unwrap_or(0);
340        ans.retain_mut(|c| {
341            if c.id.counter >= start {
342                true
343            } else if c.ctr_end() > start {
344                *c = c.slice((start - c.id.counter) as usize, c.atom_len());
345                true
346            } else {
347                false
348            }
349        });
350
351        Ok(ans)
352    }
353
354    pub(crate) fn rollback_import(&self, rollback: ChangeStoreRollback) {
355        // The name set may already include names from changes this rollback removes. That is
356        // fine: stale names only make `old_history_may_touch_root_names` conservatively true.
357        let mut inner = self.inner.lock();
358        inner.mem_parsed_kv.retain(|id, _| {
359            let old_end = rollback.old_vv.get(&id.peer).copied().unwrap_or(0);
360            id.counter < old_end
361        });
362
363        for (id, block) in rollback.blocks_before_mutation {
364            inner.mem_parsed_kv.insert(id, block);
365        }
366    }
367
368    pub fn get_dag_nodes_that_contains(&self, id: ID) -> Option<Vec<AppDagNode>> {
369        let block = self.get_block_that_contains(id)?;
370        Some(block.content.iter_dag_nodes())
371    }
372
373    pub fn get_last_dag_nodes_for_peer(&self, peer: PeerID) -> Option<Vec<AppDagNode>> {
374        let block = self.get_the_last_block_of_peer(peer)?;
375        Some(block.content.iter_dag_nodes())
376    }
377
378    pub fn visit_all_changes(&self, f: &mut dyn FnMut(&Change)) {
379        self.ensure_block_loaded_in_range(Bound::Unbounded, Bound::Unbounded);
380        let mut inner = self.inner.lock();
381        for (id, block) in inner.mem_parsed_kv.iter_mut() {
382            if let Err(err) = block.ensure_changes(&self.arena) {
383                warn!(block_id = ?id, ?err, "failed to parse change block");
384                continue;
385            }
386            for c in block.content.try_changes().unwrap() {
387                f(c);
388            }
389        }
390    }
391
392    fn build_root_history_names(&self) -> RootHistoryNamesState {
393        let mut names = FxHashSet::default();
394        let mut name_bytes = 0;
395
396        {
397            let external = self.external_kv.lock();
398            for (key, bytes) in external.scan(Bound::Unbounded, Bound::Unbounded) {
399                if key.len() != 12 {
400                    continue;
401                }
402                let header = match decode_header(&bytes)
403                    .and_then(|header| decode_cids(&bytes, Some(header)))
404                {
405                    Ok(header) => header,
406                    Err(_) => return RootHistoryNamesState::Invalid,
407                };
408                let Some(cids) = header.cids.get() else {
409                    return RootHistoryNamesState::Invalid;
410                };
411                for cid in cids.iter() {
412                    if !record_root_name(&mut names, &mut name_bytes, cid) {
413                        return RootHistoryNamesState::Invalid;
414                    }
415                }
416            }
417        }
418
419        let inner = self.inner.lock();
420        for block in inner.mem_parsed_kv.values() {
421            match &block.content {
422                ChangesBlockContent::Changes(changes) | ChangesBlockContent::Both(changes, _) => {
423                    for change in changes.iter() {
424                        for op in change.ops.iter() {
425                            let Some(cid) = self.arena.idx_to_id(op.container) else {
426                                return RootHistoryNamesState::Invalid;
427                            };
428                            if !record_root_name(&mut names, &mut name_bytes, &cid) {
429                                return RootHistoryNamesState::Invalid;
430                            }
431                        }
432                    }
433                }
434                ChangesBlockContent::Bytes(bytes) => {
435                    let header = bytes.header.get().map(|header| header.as_ref().clone());
436                    let header = match decode_cids(&bytes.bytes, header) {
437                        Ok(header) => header,
438                        Err(_) => return RootHistoryNamesState::Invalid,
439                    };
440                    let Some(cids) = header.cids.get() else {
441                        return RootHistoryNamesState::Invalid;
442                    };
443                    for cid in cids.iter() {
444                        if !record_root_name(&mut names, &mut name_bytes, cid) {
445                            return RootHistoryNamesState::Invalid;
446                        }
447                    }
448                }
449            }
450        }
451
452        RootHistoryNamesState::Valid { names, name_bytes }
453    }
454
455    fn record_change_in_root_history_names(&self, change: &Change) {
456        let mut cached = self.root_history_names.lock();
457        let RootHistoryNamesState::Valid { names, name_bytes } = &mut *cached else {
458            return;
459        };
460
461        for op in change.ops.iter() {
462            let recorded = self
463                .arena
464                .idx_to_id(op.container)
465                .is_some_and(|cid| record_root_name(names, name_bytes, &cid));
466            if !recorded {
467                *cached = RootHistoryNamesState::Invalid;
468                return;
469            }
470        }
471    }
472
473    /// Return whether the history already in this store may have touched any top-level root in
474    /// `names`. Must be called before the candidate changes are inserted.
475    ///
476    /// The first call scans encoded block container arenas without parsing operations or
477    /// populating the parsed-change cache; later inserts update the cached name set, so repeated
478    /// independent imports do not rescan the old history. Decode failures or exceeding the size
479    /// cap permanently disable this optimization for the store.
480    pub(crate) fn old_history_may_touch_root_names(
481        &self,
482        names: &FxHashSet<InternalString>,
483    ) -> bool {
484        if names.is_empty() {
485            return false;
486        }
487
488        let mut cached = self.root_history_names.lock();
489        if matches!(*cached, RootHistoryNamesState::Uninitialized) {
490            #[cfg(test)]
491            self.root_history_scan_count
492                .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
493            *cached = self.build_root_history_names();
494        }
495
496        match &*cached {
497            RootHistoryNamesState::Valid {
498                names: history_names,
499                ..
500            } => names.iter().any(|name| history_names.contains(name)),
501            RootHistoryNamesState::Invalid | RootHistoryNamesState::Uninitialized => true,
502        }
503    }
504
505    #[cfg(test)]
506    pub(crate) fn root_history_scan_count_for_test(&self) -> usize {
507        self.root_history_scan_count
508            .load(std::sync::atomic::Ordering::Relaxed)
509    }
510
511    pub(crate) fn iter_blocks(&self, id_span: IdSpan) -> Vec<(Arc<ChangesBlock>, usize, usize)> {
512        if id_span.counter.start == id_span.counter.end {
513            return vec![];
514        }
515
516        assert!(id_span.counter.start < id_span.counter.end);
517        self.ensure_block_loaded_in_range(
518            Bound::Included(id_span.id_start()),
519            Bound::Excluded(id_span.id_end()),
520        );
521        let mut inner = self.inner.lock();
522        let next_back = inner.mem_parsed_kv.range(..=id_span.id_start()).next_back();
523        match next_back {
524            None => {
525                return vec![];
526            }
527            Some(next_back) => {
528                if next_back.0.peer != id_span.peer {
529                    return vec![];
530                }
531            }
532        }
533        let start_counter = next_back.map(|(id, _)| id.counter).unwrap_or(0);
534        let ans = inner
535            .mem_parsed_kv
536            .range_mut(
537                ID::new(id_span.peer, start_counter)..ID::new(id_span.peer, id_span.counter.end),
538            )
539            .filter_map(|(_id, block)| {
540                if block.counter_range.1 < id_span.counter.start {
541                    return None;
542                }
543
544                if let Err(err) = block.ensure_changes(&self.arena) {
545                    warn!(block_id = ?_id, ?err, "failed to parse change block");
546                    return None;
547                }
548                let changes = block.content.try_changes().unwrap();
549                let start;
550                let end;
551                if id_span.counter.start <= block.counter_range.0
552                    && id_span.counter.end >= block.counter_range.1
553                {
554                    start = 0;
555                    end = changes.len();
556                } else {
557                    start = block
558                        .get_change_index_by_counter(id_span.counter.start)
559                        .unwrap_or_else(|x| x);
560
561                    match block.get_change_index_by_counter(id_span.counter.end - 1) {
562                        Ok(e) => {
563                            end = e + 1;
564                        }
565                        Err(0) => return None,
566                        Err(e) => {
567                            end = e;
568                        }
569                    }
570                }
571                if start == end {
572                    return None;
573                }
574
575                Some((block.clone(), start, end))
576            })
577            // TODO: PERF avoid alloc
578            .collect_vec();
579
580        ans
581    }
582
583    pub fn iter_changes(&self, id_span: IdSpan) -> impl Iterator<Item = BlockChangeRef> + '_ {
584        let v = self.iter_blocks(id_span);
585        #[cfg(debug_assertions)]
586        {
587            if !v.is_empty() {
588                assert_eq!(v[0].0.peer, id_span.peer);
589                assert_eq!(v.last().unwrap().0.peer, id_span.peer);
590                {
591                    // Test start
592                    let (block, start, _end) = v.first().unwrap();
593                    let changes = block.content.try_changes().unwrap();
594                    assert!(changes[*start].id.counter <= id_span.counter.start);
595                }
596                {
597                    // Test end
598                    let (block, _start, end) = v.last().unwrap();
599                    let changes = block.content.try_changes().unwrap();
600                    assert!(changes[*end - 1].ctr_end() >= id_span.counter.end);
601                    assert!(changes[*end - 1].id.counter < id_span.counter.end);
602                }
603            }
604        }
605
606        v.into_iter().flat_map(move |(block, start, end)| {
607            (start..end).map(move |i| BlockChangeRef {
608                change_index: i,
609                block: block.clone(),
610            })
611        })
612    }
613
614    #[allow(dead_code)]
615    pub(crate) fn get_blocks_in_range(&self, id_span: IdSpan) -> VecDeque<Arc<ChangesBlock>> {
616        let mut inner = self.inner.lock();
617        let start_counter = inner
618            .mem_parsed_kv
619            .range(..=id_span.id_start())
620            .next_back()
621            .map(|(id, _)| id.counter)
622            .unwrap_or(0);
623        let vec = inner
624            .mem_parsed_kv
625            .range_mut(
626                ID::new(id_span.peer, start_counter)..ID::new(id_span.peer, id_span.counter.end),
627            )
628            .filter_map(|(_id, block)| {
629                if block.counter_range.1 < id_span.counter.start {
630                    return None;
631                }
632
633                if let Err(err) = block.ensure_changes(&self.arena) {
634                    warn!(block_id = ?_id, ?err, "failed to parse change block");
635                    return None;
636                }
637                Some(block.clone())
638            })
639            // TODO: PERF avoid alloc
640            .collect();
641        vec
642    }
643
644    pub(crate) fn get_block_that_contains(&self, id: ID) -> Option<Arc<ChangesBlock>> {
645        self.ensure_block_loaded_in_range(Bound::Included(id), Bound::Included(id));
646        let inner = self.inner.lock();
647        let block = inner
648            .mem_parsed_kv
649            .range(..=id)
650            .next_back()
651            .filter(|(_, block)| {
652                block.peer == id.peer
653                    && block.counter_range.0 <= id.counter
654                    && id.counter < block.counter_range.1
655            })
656            .map(|(_, block)| block.clone());
657
658        block
659    }
660
661    pub(crate) fn get_the_last_block_of_peer(&self, peer: PeerID) -> Option<Arc<ChangesBlock>> {
662        let end_id = ID::new(peer, Counter::MAX);
663        self.ensure_id_lte(end_id);
664        let inner = self.inner.lock();
665        let block = inner
666            .mem_parsed_kv
667            .range(..=end_id)
668            .next_back()
669            .filter(|(_, block)| block.peer == peer)
670            .map(|(_, block)| block.clone());
671
672        block
673    }
674
675    pub fn change_num(&self) -> usize {
676        self.ensure_block_loaded_in_range(Bound::Unbounded, Bound::Unbounded);
677        let mut inner = self.inner.lock();
678        inner
679            .mem_parsed_kv
680            .iter_mut()
681            .map(|(_, block)| block.change_num())
682            .sum()
683    }
684
685    pub fn fork(
686        &self,
687        arena: SharedArena,
688        merge_interval: Arc<AtomicI64>,
689        vv: &VersionVector,
690        frontiers: &Frontiers,
691    ) -> Self {
692        self.flush_and_compact(vv, frontiers);
693        let inner = self.inner.lock();
694        Self {
695            inner: Arc::new(Mutex::new(ChangeStoreInner {
696                start_vv: inner.start_vv.clone(),
697                start_frontiers: inner.start_frontiers.clone(),
698                mem_parsed_kv: BTreeMap::new(),
699            })),
700            arena,
701            external_vv: Arc::new(Mutex::new(self.external_vv.lock().clone())),
702            external_kv: self.external_kv.lock().clone_store(),
703            merge_interval,
704            root_history_names: Arc::new(Mutex::new(RootHistoryNamesState::Uninitialized)),
705            #[cfg(test)]
706            root_history_scan_count: Arc::new(AtomicUsize::new(0)),
707        }
708    }
709
710    pub fn kv_size(&self) -> usize {
711        self.external_kv
712            .lock()
713            .scan(Bound::Unbounded, Bound::Unbounded)
714            .map(|(k, v)| k.len() + v.len())
715            .sum()
716    }
717
718    pub(crate) fn export_blocks_from<W: std::io::Write>(
719        &self,
720        start_vv: &VersionVector,
721        shallow_since_vv: &ImVersionVector,
722        latest_vv: &VersionVector,
723        w: &mut W,
724    ) {
725        let new_store = ChangeStore::new_mem(&self.arena, self.merge_interval.clone());
726        for mut span in latest_vv.sub_iter(start_vv) {
727            let counter_lower_bound = shallow_since_vv.get(&span.peer).copied().unwrap_or(0);
728            span.counter.start = span.counter.start.max(counter_lower_bound);
729            span.counter.end = span.counter.end.max(counter_lower_bound);
730            if span.counter.start >= span.counter.end {
731                continue;
732            }
733
734            // PERF: this can be optimized by reusing the current encoded blocks
735            // In the current method, it needs to parse and re-encode the blocks
736            for c in self.iter_changes(span) {
737                let start = ((start_vv.get(&c.id.peer).copied().unwrap_or(0) - c.id.counter).max(0)
738                    as usize)
739                    .min(c.atom_len());
740                let end = ((latest_vv.get(&c.id.peer).copied().unwrap_or(0) - c.id.counter).max(0)
741                    as usize)
742                    .min(c.atom_len());
743
744                assert_ne!(start, end);
745                let ch = c.slice(start, end);
746                new_store.insert_change(ch, false, false);
747            }
748        }
749
750        let arena = &self.arena;
751        encode_blocks_in_store(new_store, arena, w);
752    }
753
754    pub(crate) fn fork_changes_up_to(
755        &self,
756        start_vv: &ImVersionVector,
757        frontiers: &Frontiers,
758        vv: &VersionVector,
759    ) -> Bytes {
760        let new_store = ChangeStore::new_mem(&self.arena, self.merge_interval.clone());
761        for mut span in vv.sub_iter_im(start_vv) {
762            let counter_lower_bound = start_vv.get(&span.peer).copied().unwrap_or(0);
763            span.counter.start = span.counter.start.max(counter_lower_bound);
764            span.counter.end = span.counter.end.max(counter_lower_bound);
765            if span.counter.start >= span.counter.end {
766                continue;
767            }
768
769            // PERF: this can be optimized by reusing the current encoded blocks
770            // In the current method, it needs to parse and re-encode the blocks
771            for c in self.iter_changes(span) {
772                let start = ((start_vv.get(&c.id.peer).copied().unwrap_or(0) - c.id.counter).max(0)
773                    as usize)
774                    .min(c.atom_len());
775                let end = ((vv.get(&c.id.peer).copied().unwrap_or(0) - c.id.counter).max(0)
776                    as usize)
777                    .min(c.atom_len());
778
779                assert_ne!(start, end);
780                let ch = c.slice(start, end);
781                new_store.insert_change(ch, false, false);
782            }
783        }
784
785        new_store.encode_all(vv, frontiers)
786    }
787}
788
789fn encode_blocks_in_store<W: std::io::Write>(
790    new_store: ChangeStore,
791    arena: &SharedArena,
792    w: &mut W,
793) {
794    let mut inner = new_store.inner.lock();
795    for (_id, block) in inner.mem_parsed_kv.iter_mut() {
796        let bytes = block.to_bytes(arena);
797        leb128::write::unsigned(w, bytes.bytes.len() as u64).unwrap();
798        w.write_all(&bytes.bytes).unwrap();
799    }
800}
801
802mod mut_external_kv {
803    //! Only this module contains the code that mutate the external kv store
804    //! All other modules should only read from the external kv store
805    use super::*;
806
807    impl ChangeStore {
808        #[tracing::instrument(skip_all, level = "debug", name = "change_store import_all")]
809        pub(crate) fn import_all(&self, bytes: Bytes) -> Result<BatchDecodeInfo, LoroError> {
810            let mut kv_store = self.external_kv.lock();
811            assert!(
812                // 2 because there are vv and frontiers
813                kv_store.len() <= 2,
814                "kv store should be empty when using decode_all"
815            );
816            // Snapshot/update bytes are external input. Validate the checksums embedded in each
817            // SSTable block as well as the document envelope so malformed lazy blocks are rejected
818            // during import instead of surfacing from a later read.
819            kv_store
820                .import_all(bytes)
821                .map_err(|e| LoroError::DecodeError(e.into_boxed_str()))?;
822            drop(kv_store);
823            *self.root_history_names.lock() = RootHistoryNamesState::Uninitialized;
824            let vv_bytes = self.external_kv.lock().get(VV_KEY).unwrap_or_default();
825            let vv = VersionVector::decode(&vv_bytes)
826                .map_err(|_| LoroError::DecodeDataCorruptionError)?;
827            let start_vv_bytes = self
828                .external_kv
829                .lock()
830                .get(START_VV_KEY)
831                .unwrap_or_default();
832            let start_vv = if start_vv_bytes.is_empty() {
833                Default::default()
834            } else {
835                VersionVector::decode(&start_vv_bytes)
836                    .map_err(|_| LoroError::DecodeDataCorruptionError)?
837            };
838
839            #[cfg(test)]
840            {
841                // This is for tests
842                for (peer, cnt) in vv.iter() {
843                    self.get_change(ID::new(*peer, *cnt - 1))
844                        .ok_or(LoroError::DecodeDataCorruptionError)?;
845                }
846            }
847
848            *self.external_vv.lock() = vv.clone();
849            let frontiers_bytes = self
850                .external_kv
851                .lock()
852                .get(FRONTIERS_KEY)
853                .unwrap_or_default();
854            let frontiers = Frontiers::decode(&frontiers_bytes)
855                .map_err(|_| LoroError::DecodeDataCorruptionError)?;
856            let start_frontiers = self
857                .external_kv
858                .lock()
859                .get(START_FRONTIERS_KEY)
860                .unwrap_or_default();
861            let start_frontiers = if start_frontiers.is_empty() {
862                Default::default()
863            } else {
864                Frontiers::decode(&start_frontiers)
865                    .map_err(|_| LoroError::DecodeDataCorruptionError)?
866            };
867
868            let mut max_lamport = None;
869            let mut max_timestamp = 0;
870            for id in frontiers.iter() {
871                let c = self
872                    .get_change(id)
873                    .ok_or(LoroError::DecodeDataCorruptionError)?;
874                debug_assert_ne!(c.atom_len(), 0);
875                let l = c.lamport_last();
876                if let Some(x) = max_lamport {
877                    if l > x {
878                        max_lamport = Some(l);
879                    }
880                } else {
881                    max_lamport = Some(l);
882                }
883
884                let t = c.timestamp;
885                if t > max_timestamp {
886                    max_timestamp = t;
887                }
888            }
889
890            Ok(BatchDecodeInfo {
891                vv,
892                frontiers,
893                start_version: if start_vv.is_empty() {
894                    None
895                } else {
896                    let mut inner = self.inner.lock();
897                    inner.start_frontiers = start_frontiers.clone();
898                    inner.start_vv = ImVersionVector::from_vv(&start_vv);
899                    Some((start_vv, start_frontiers))
900                },
901            })
902        }
903
904        /// Flush the cached change to kv_store
905        pub(crate) fn flush_and_compact(&self, vv: &VersionVector, frontiers: &Frontiers) {
906            let mut inner = self.inner.lock();
907            let mut store = self.external_kv.lock();
908            let mut external_vv = self.external_vv.lock();
909            for (id, block) in inner.mem_parsed_kv.iter_mut() {
910                if !block.flushed {
911                    let id_bytes = id.to_bytes();
912                    let counter_start = external_vv.get(&id.peer).copied().unwrap_or(0);
913                    assert!(
914                        counter_start < block.counter_range.1,
915                        "Peer={} Block Counter Range={:?}, counter_start={}",
916                        id.peer,
917                        &block.counter_range,
918                        counter_start
919                    );
920                    if counter_start > block.counter_range.0 {
921                        assert!(store.get(&id_bytes).is_some());
922                    }
923                    external_vv.insert(id.peer, block.counter_range.1);
924                    let bytes = block.to_bytes(&self.arena);
925                    store.set(&id_bytes, bytes.bytes);
926                    Arc::make_mut(block).flushed = true;
927                }
928            }
929
930            if inner.start_vv.is_empty() {
931                assert_eq!(&*external_vv, vv);
932            } else {
933                #[cfg(debug_assertions)]
934                {
935                    // TODO: makes some assertions here?
936                }
937            }
938            let vv_bytes = vv.encode();
939            let frontiers_bytes = frontiers.encode();
940            store.set(VV_KEY, vv_bytes.into());
941            store.set(FRONTIERS_KEY, frontiers_bytes.into());
942        }
943    }
944}
945
946mod mut_inner_kv {
947    //! Only this module contains the code that mutate the internal kv store
948    //! All other modules should only read from the internal kv store
949
950    use super::*;
951    impl ChangeStore {
952        /// This method is the **only place** that push a new change into the change store
953        ///
954        /// The new change either merges with the previous block or is put into a new block.
955        /// This method only updates the internal kv store.
956        pub fn insert_change(&self, change: Change, split_when_exceeds: bool, is_local: bool) {
957            self.insert_change_inner(change, split_when_exceeds, is_local, None);
958        }
959
960        pub(crate) fn insert_change_with_rollback(
961            &self,
962            change: Change,
963            split_when_exceeds: bool,
964            is_local: bool,
965            rollback: &mut ChangeStoreRollback,
966        ) {
967            self.insert_change_inner(change, split_when_exceeds, is_local, Some(rollback));
968        }
969
970        fn insert_change_inner(
971            &self,
972            mut change: Change,
973            split_when_exceeds: bool,
974            is_local: bool,
975            mut rollback: Option<&mut ChangeStoreRollback>,
976        ) {
977            self.record_change_in_root_history_names(&change);
978
979            #[cfg(debug_assertions)]
980            {
981                let vv = self.external_vv.lock();
982                assert!(vv.get(&change.id.peer).copied().unwrap_or(0) <= change.id.counter);
983            }
984
985            let s = info_span!("change_store insert_change", id = ?change.id);
986            let _e = s.enter();
987            let estimated_size = change.estimate_storage_size();
988            if estimated_size > MAX_BLOCK_SIZE && split_when_exceeds {
989                self.split_change_then_insert(change, rollback.as_deref_mut());
990                return;
991            }
992
993            let id = change.id;
994            let mut inner = self.inner.lock();
995
996            // try to merge with previous block
997            if let Some((_id, block)) = inner.mem_parsed_kv.range_mut(..id).next_back() {
998                if block.peer == change.id.peer {
999                    if block.counter_range.1 != change.id.counter {
1000                        panic!("counter should be continuous")
1001                    }
1002
1003                    if let Some(rollback) = &mut rollback {
1004                        rollback.record_block_before_mutation(*_id, block.clone());
1005                    }
1006
1007                    match block.push_change(
1008                        change,
1009                        estimated_size,
1010                        if is_local {
1011                            // local change should try to merge with previous change when
1012                            // the timestamp interval <= the `merge_interval`
1013                            self.merge_interval
1014                                .load(std::sync::atomic::Ordering::Acquire)
1015                        } else {
1016                            0
1017                        },
1018                        &self.arena,
1019                    ) {
1020                        Ok(_) => {
1021                            drop(inner);
1022                            debug_assert!(self.get_change(id).is_some());
1023                            return;
1024                        }
1025                        Err(c) => change = c,
1026                    }
1027                }
1028            }
1029
1030            inner
1031                .mem_parsed_kv
1032                .insert(id, Arc::new(ChangesBlock::new(change, &self.arena)));
1033            drop(inner);
1034            debug_assert!(self.get_change(id).is_some());
1035        }
1036
1037        pub fn get_change(&self, id: ID) -> Option<BlockChangeRef> {
1038            let block = self.get_parsed_block(id)?;
1039            Some(BlockChangeRef {
1040                change_index: block.get_change_index_by_counter(id.counter).unwrap(),
1041                block: block.clone(),
1042            })
1043        }
1044
1045        /// Get the change with the given peer and lamport.
1046        ///
1047        /// If not found, return the change with the greatest lamport that is smaller than the given lamport.
1048        pub fn get_change_by_lamport_lte(&self, idlp: IdLp) -> Option<BlockChangeRef> {
1049            // This method is complicated because we impl binary search on top of the range api
1050            // It can be simplified
1051            let mut inner = self.inner.lock();
1052            let mut iter = inner
1053                .mem_parsed_kv
1054                .range_mut(ID::new(idlp.peer, 0)..ID::new(idlp.peer, i32::MAX));
1055
1056            // This won't change, we only adjust upper_bound
1057            let mut lower_bound = 0;
1058            let mut upper_bound = i32::MAX;
1059            let mut is_binary_searching = false;
1060            // The binary search below can stop making progress (e.g. when
1061            // `(lower_bound + upper_bound) / 2` becomes a fixed point while the
1062            // target block is missing from `mem_parsed_kv`, or when block
1063            // metadata is inconsistent). Cap its steps and fall back to the
1064            // external kv scan, which is always correct.
1065            let mut binary_search_steps = 0;
1066            loop {
1067                if is_binary_searching {
1068                    binary_search_steps += 1;
1069                    if binary_search_steps > 128 {
1070                        warn!(
1071                            "get_change_by_lamport_lte binary search did not converge; \
1072                             falling back to kv scan"
1073                        );
1074                        break;
1075                    }
1076                }
1077                match iter.next_back() {
1078                    Some((&id, block)) => {
1079                        if block.lamport_range.0 <= idlp.lamport
1080                            && (!is_binary_searching || idlp.lamport < block.lamport_range.1)
1081                        {
1082                            if !is_binary_searching
1083                                && upper_bound != i32::MAX
1084                                && upper_bound != block.counter_range.1
1085                            {
1086                                warn!(
1087                                    "There is a hole between the last block and the current block"
1088                                );
1089                                // There is hole between the last block and the current block
1090                                // We need to load it from the kv store
1091                                break;
1092                            }
1093
1094                            // Found the block
1095                            if let Err(err) = block.ensure_changes(&self.arena) {
1096                                warn!(block_id = ?id, ?err, "failed to parse change block");
1097                                return None;
1098                            }
1099                            let index = block.get_change_index_by_lamport_lte(idlp.lamport)?;
1100                            return Some(BlockChangeRef {
1101                                change_index: index,
1102                                block: block.clone(),
1103                            });
1104                        }
1105
1106                        if is_binary_searching {
1107                            let mid_bound = (lower_bound + upper_bound) / 2;
1108                            if block.lamport_range.1 <= idlp.lamport {
1109                                // Target is larger than the current block (pointed by mid_bound)
1110                                lower_bound = mid_bound;
1111                            } else {
1112                                debug_assert!(
1113                                    idlp.lamport < block.lamport_range.0,
1114                                    "{} {:?}",
1115                                    idlp,
1116                                    &block.lamport_range
1117                                );
1118                                // Target is smaller than the current block (pointed by mid_bound)
1119                                upper_bound = mid_bound;
1120                            }
1121
1122                            let mid_bound = (lower_bound + upper_bound) / 2;
1123                            iter = inner
1124                                .mem_parsed_kv
1125                                .range_mut(ID::new(idlp.peer, 0)..ID::new(idlp.peer, mid_bound));
1126                        } else {
1127                            // Test whether we need to switch to binary search by measuring the gap
1128                            if block.lamport_range.0 - idlp.lamport > MAX_BLOCK_SIZE as Lamport * 8
1129                            {
1130                                // Use binary search to find the block
1131                                upper_bound = id.counter;
1132                                let mid_bound = (lower_bound + upper_bound) / 2;
1133                                iter = inner.mem_parsed_kv.range_mut(
1134                                    ID::new(idlp.peer, 0)..ID::new(idlp.peer, mid_bound),
1135                                );
1136                                is_binary_searching = true;
1137                            }
1138
1139                            upper_bound = id.counter;
1140                        }
1141                    }
1142                    None => {
1143                        if !is_binary_searching {
1144                            break;
1145                        }
1146
1147                        let mid_bound = (lower_bound + upper_bound) / 2;
1148                        lower_bound = mid_bound;
1149                        if upper_bound - lower_bound <= MAX_BLOCK_SIZE as i32 {
1150                            // If they are too close, we can just scan the range
1151                            iter = inner.mem_parsed_kv.range_mut(
1152                                ID::new(idlp.peer, lower_bound)..ID::new(idlp.peer, upper_bound),
1153                            );
1154                            is_binary_searching = false;
1155                        } else {
1156                            let mid_bound = (lower_bound + upper_bound) / 2;
1157                            iter = inner
1158                                .mem_parsed_kv
1159                                .range_mut(ID::new(idlp.peer, 0)..ID::new(idlp.peer, mid_bound));
1160                        }
1161                    }
1162                }
1163            }
1164
1165            let counter_end = upper_bound;
1166
1167            // The answer may live only in `mem_parsed_kv` (e.g. local changes
1168            // that have not been flushed to the external kv store yet) or only
1169            // in `external_kv` (blocks that were never parsed into memory).
1170            // Check both and use the block with the greatest start counter:
1171            // within a peer, lamport grows with counter, so that block holds
1172            // the greatest matching lamport.
1173            let mem_block_id: Option<ID> = inner
1174                .mem_parsed_kv
1175                .range(ID::new(idlp.peer, 0)..ID::new(idlp.peer, counter_end))
1176                .rev()
1177                .find(|(_, block)| block.lamport_range.0 <= idlp.lamport)
1178                .map(|(id, _)| *id);
1179
1180            let external_block = 'block_scan: {
1181                let kv_store = &self.external_kv.lock();
1182                let scan_end = ID::new(idlp.peer, counter_end).to_bytes();
1183                let iter = kv_store
1184                    .scan(
1185                        Bound::Included(&ID::new(idlp.peer, 0).to_bytes()),
1186                        Bound::Excluded(&scan_end),
1187                    )
1188                    .rev();
1189
1190                for (id, bytes) in iter {
1191                    let mut block = ChangesBlockBytes::new(bytes.clone());
1192                    let (lamport_start, _lamport_end) = match block.lamport_range() {
1193                        Ok(range) => range,
1194                        Err(err) => {
1195                            let block_id = ID::from_bytes(&id);
1196                            warn!(
1197                                ?block_id,
1198                                ?err,
1199                                "failed to decode external change block range"
1200                            );
1201                            continue;
1202                        }
1203                    };
1204                    if lamport_start <= idlp.lamport {
1205                        break 'block_scan Some((ID::from_bytes(&id), bytes));
1206                    }
1207                }
1208
1209                None
1210            };
1211
1212            let use_external = match (mem_block_id, &external_block) {
1213                (Some(mem_id), Some((external_id, _))) => external_id.counter > mem_id.counter,
1214                (Some(_), None) => false,
1215                (None, Some(_)) => true,
1216                (None, None) => return None,
1217            };
1218
1219            if !use_external {
1220                let block_id = mem_block_id.unwrap();
1221                let block = inner.mem_parsed_kv.get_mut(&block_id).unwrap();
1222                if let Err(err) = block.ensure_changes(&self.arena) {
1223                    warn!(?block_id, ?err, "failed to parse change block");
1224                    return None;
1225                }
1226                let block = block.clone();
1227                let index = block.get_change_index_by_lamport_lte(idlp.lamport)?;
1228                return Some(BlockChangeRef {
1229                    change_index: index,
1230                    block,
1231                });
1232            }
1233
1234            let (block_id, bytes) = external_block.unwrap();
1235            let mut block = match ChangesBlock::from_bytes(bytes) {
1236                Ok(block) => Arc::new(block),
1237                Err(err) => {
1238                    warn!(?block_id, ?err, "failed to decode external change block");
1239                    return None;
1240                }
1241            };
1242            if let Err(err) = block.ensure_changes(&self.arena) {
1243                warn!(?block_id, ?err, "failed to parse external change block");
1244                return None;
1245            }
1246            inner.mem_parsed_kv.insert(block_id, block.clone());
1247            let index = block.get_change_index_by_lamport_lte(idlp.lamport)?;
1248            Some(BlockChangeRef {
1249                change_index: index,
1250                block,
1251            })
1252        }
1253
1254        fn split_change_then_insert(
1255            &self,
1256            change: Change,
1257            mut rollback: Option<&mut ChangeStoreRollback>,
1258        ) {
1259            let original_len = change.atom_len();
1260            let mut new_change = Change {
1261                ops: RleVec::new(),
1262                deps: change.deps,
1263                id: change.id,
1264                lamport: change.lamport,
1265                timestamp: change.timestamp,
1266                commit_msg: change.commit_msg.clone(),
1267            };
1268
1269            let mut total_len = 0;
1270            let mut estimated_size = new_change.estimate_storage_size();
1271            'outer: for mut op in change.ops.into_iter() {
1272                if op.estimate_storage_size() >= MAX_BLOCK_SIZE - estimated_size {
1273                    new_change = self._insert_splitted_change(
1274                        new_change,
1275                        &mut total_len,
1276                        &mut estimated_size,
1277                        rollback.as_deref_mut(),
1278                    );
1279                }
1280
1281                while let Some(end) =
1282                    op.check_whether_slice_content_to_fit_in_size(MAX_BLOCK_SIZE - estimated_size)
1283                {
1284                    // The new op can take the rest of the room
1285                    let new = op.slice(0, end);
1286                    new_change.ops.push(new);
1287                    new_change = self._insert_splitted_change(
1288                        new_change,
1289                        &mut total_len,
1290                        &mut estimated_size,
1291                        rollback.as_deref_mut(),
1292                    );
1293
1294                    if end < op.atom_len() {
1295                        op = op.slice(end, op.atom_len());
1296                    } else {
1297                        continue 'outer;
1298                    }
1299                }
1300
1301                estimated_size += op.estimate_storage_size();
1302                if estimated_size > MAX_BLOCK_SIZE && !new_change.ops.is_empty() {
1303                    new_change = self._insert_splitted_change(
1304                        new_change,
1305                        &mut total_len,
1306                        &mut estimated_size,
1307                        rollback.as_deref_mut(),
1308                    );
1309                    new_change.ops.push(op);
1310                } else {
1311                    new_change.ops.push(op);
1312                }
1313            }
1314
1315            if !new_change.ops.is_empty() {
1316                total_len += new_change.atom_len();
1317                self.insert_change_inner(new_change, false, false, rollback);
1318            }
1319
1320            assert_eq!(total_len, original_len);
1321        }
1322
1323        fn _insert_splitted_change(
1324            &self,
1325            new_change: Change,
1326            total_len: &mut usize,
1327            estimated_size: &mut usize,
1328            rollback: Option<&mut ChangeStoreRollback>,
1329        ) -> Change {
1330            if new_change.atom_len() == 0 {
1331                return new_change;
1332            }
1333
1334            let ctr_end = new_change.id.counter + new_change.atom_len() as Counter;
1335            let next_lamport = new_change.lamport + new_change.atom_len() as Lamport;
1336            *total_len += new_change.atom_len();
1337            let ans = Change {
1338                ops: RleVec::new(),
1339                deps: ID::new(new_change.id.peer, ctr_end - 1).into(),
1340                id: ID::new(new_change.id.peer, ctr_end),
1341                lamport: next_lamport,
1342                timestamp: new_change.timestamp,
1343                commit_msg: new_change.commit_msg.clone(),
1344            };
1345
1346            self.insert_change_inner(new_change, false, false, rollback);
1347            *estimated_size = ans.estimate_storage_size();
1348            ans
1349        }
1350
1351        fn get_parsed_block(&self, id: ID) -> Option<Arc<ChangesBlock>> {
1352            let mut inner = self.inner.lock();
1353            if let Some((_id, block)) = inner.mem_parsed_kv.range_mut(..=id).next_back() {
1354                if block.peer == id.peer && block.counter_range.1 > id.counter {
1355                    if let Err(err) = block.ensure_changes(&self.arena) {
1356                        warn!(block_id = ?_id, ?err, "failed to parse cached change block");
1357                        return None;
1358                    }
1359                    return Some(block.clone());
1360                }
1361            }
1362
1363            let store = self.external_kv.lock();
1364            let mut iter = store
1365                .scan(Bound::Unbounded, Bound::Included(&id.to_bytes()))
1366                .filter(|(id, _)| id.len() == 12);
1367
1368            // println!(
1369            //     "\nkeys {:?}",
1370            //     store
1371            //         .scan(Bound::Unbounded, Bound::Included(&id.to_bytes()))
1372            //         .filter(|(id, _)| id.len() == 12)
1373            //         .map(|(k, _v)| ID::from_bytes(&k))
1374            //         .count()
1375            // );
1376            // println!("id {:?}", id);
1377
1378            let (b_id, b_bytes) = iter.next_back()?;
1379            let block_id: ID = ID::from_bytes(&b_id[..]);
1380            let block = match ChangesBlock::from_bytes(b_bytes) {
1381                Ok(block) => block,
1382                Err(err) => {
1383                    warn!(?block_id, ?err, "failed to decode external change block");
1384                    return None;
1385                }
1386            };
1387            if block_id.peer == id.peer
1388                && block_id.counter <= id.counter
1389                && block.counter_range.1 > id.counter
1390            {
1391                let mut arc_block = Arc::new(block);
1392                if let Err(err) = arc_block.ensure_changes(&self.arena) {
1393                    warn!(?block_id, ?err, "failed to parse external change block");
1394                    return None;
1395                }
1396                inner.mem_parsed_kv.insert(block_id, arc_block.clone());
1397                return Some(arc_block);
1398            }
1399
1400            None
1401        }
1402
1403        /// Load all the blocks that have overlapped with the given ID range into `inner_mem_parsed_kv`
1404        ///
1405        /// This is fast because we don't actually parse the content.
1406        // TODO: PERF: This method feels slow.
1407        pub(super) fn ensure_block_loaded_in_range(&self, start: Bound<ID>, end: Bound<ID>) {
1408            let mut whether_need_scan_backward = match start {
1409                Bound::Included(id) => Some(id),
1410                Bound::Excluded(id) => Some(id.inc(1)),
1411                Bound::Unbounded => None,
1412            };
1413
1414            {
1415                let start = start.map(|id| id.to_bytes());
1416                let end = end.map(|id| id.to_bytes());
1417                let kv = self.external_kv.lock();
1418                let mut inner = self.inner.lock();
1419                for (id, bytes) in kv
1420                    .scan(
1421                        start.as_ref().map(|x| x.as_slice()),
1422                        end.as_ref().map(|x| x.as_slice()),
1423                    )
1424                    .filter(|(id, _)| id.len() == 12)
1425                {
1426                    let id = ID::from_bytes(&id);
1427                    if let Some(expected_start_id) = whether_need_scan_backward {
1428                        if id == expected_start_id {
1429                            whether_need_scan_backward = None;
1430                        }
1431                    }
1432
1433                    if inner.mem_parsed_kv.contains_key(&id) {
1434                        continue;
1435                    }
1436
1437                    let block = match ChangesBlock::from_bytes(bytes.clone()) {
1438                        Ok(block) => block,
1439                        Err(err) => {
1440                            warn!(?id, ?err, "failed to decode external change block");
1441                            continue;
1442                        }
1443                    };
1444                    inner.mem_parsed_kv.insert(id, Arc::new(block));
1445                }
1446            }
1447
1448            if let Some(start_id) = whether_need_scan_backward {
1449                self.ensure_id_lte(start_id);
1450            }
1451        }
1452
1453        pub(super) fn ensure_id_lte(&self, id: ID) {
1454            let kv = self.external_kv.lock();
1455            let mut inner = self.inner.lock();
1456            let Some((next_back_id, next_back_bytes)) = kv
1457                .scan(Bound::Unbounded, Bound::Included(&id.to_bytes()))
1458                .rfind(|(id, _)| id.len() == 12)
1459            else {
1460                return;
1461            };
1462
1463            let next_back_id = ID::from_bytes(&next_back_id);
1464            if next_back_id.peer == id.peer {
1465                if inner.mem_parsed_kv.contains_key(&next_back_id) {
1466                    return;
1467                }
1468
1469                let block = match ChangesBlock::from_bytes(next_back_bytes) {
1470                    Ok(block) => block,
1471                    Err(err) => {
1472                        warn!(
1473                            ?next_back_id,
1474                            ?err,
1475                            "failed to decode external change block"
1476                        );
1477                        return;
1478                    }
1479                };
1480                inner.mem_parsed_kv.insert(next_back_id, Arc::new(block));
1481            }
1482        }
1483    }
1484}
1485
1486#[must_use]
1487#[derive(Clone, Debug)]
1488pub(crate) struct BatchDecodeInfo {
1489    pub vv: VersionVector,
1490    pub frontiers: Frontiers,
1491    pub start_version: Option<(VersionVector, Frontiers)>,
1492}
1493
1494#[derive(Clone, Debug)]
1495pub struct BlockChangeRef {
1496    block: Arc<ChangesBlock>,
1497    change_index: usize,
1498}
1499
1500impl Deref for BlockChangeRef {
1501    type Target = Change;
1502    fn deref(&self) -> &Change {
1503        &self.block.content.try_changes().unwrap()[self.change_index]
1504    }
1505}
1506
1507impl BlockChangeRef {
1508    pub(crate) fn get_op_with_counter(&self, counter: Counter) -> Option<BlockOpRef> {
1509        if counter >= self.ctr_end() {
1510            return None;
1511        }
1512
1513        let index = self.ops.search_atom_index(counter);
1514        Some(BlockOpRef {
1515            block: self.block.clone(),
1516            change_index: self.change_index,
1517            op_index: index,
1518        })
1519    }
1520}
1521
1522#[derive(Clone, Debug)]
1523pub(crate) struct BlockOpRef {
1524    pub block: Arc<ChangesBlock>,
1525    pub change_index: usize,
1526    pub op_index: usize,
1527}
1528
1529impl Deref for BlockOpRef {
1530    type Target = Op;
1531
1532    fn deref(&self) -> &Op {
1533        &self.block.content.try_changes().unwrap()[self.change_index].ops[self.op_index]
1534    }
1535}
1536
1537impl BlockOpRef {
1538    pub fn lamport(&self) -> Lamport {
1539        let change = &self.block.content.try_changes().unwrap()[self.change_index];
1540        let op = &change.ops[self.op_index];
1541        (op.counter - change.id.counter) as Lamport + change.lamport
1542    }
1543}
1544
1545impl ChangesBlock {
1546    fn from_bytes(bytes: Bytes) -> LoroResult<Self> {
1547        let len = bytes.len();
1548        let bytes = ChangesBlockBytes::new(bytes);
1549        bytes.ensure_header()?;
1550        let header = bytes
1551            .header
1552            .get()
1553            .expect("header should be initialized after ensure_header");
1554        let peer = header.peer;
1555        let counter_range = (
1556            header.counter,
1557            *header.counters.last().ok_or_else(|| {
1558                LoroError::DecodeError("Decode block error: missing counters".into())
1559            })?,
1560        );
1561        // `header.lamports` only stores the start lamport of each change (n entries),
1562        // while `header.counters` has n + 1 entries ending with the block's end counter.
1563        // The block's exclusive end lamport is the last change's start lamport plus its len.
1564        let last_change_start = *header
1565            .counters
1566            .len()
1567            .checked_sub(2)
1568            .and_then(|i| header.counters.get(i))
1569            .ok_or_else(|| LoroError::DecodeError("Decode block error: missing counters".into()))?;
1570        let last_change_len = counter_range.1 - last_change_start;
1571        let lamport_range = (
1572            *header.lamports.first().ok_or_else(|| {
1573                LoroError::DecodeError("Decode block error: missing lamports".into())
1574            })?,
1575            header
1576                .lamports
1577                .last()
1578                .ok_or_else(|| {
1579                    LoroError::DecodeError("Decode block error: missing lamports".into())
1580                })?
1581                .checked_add(last_change_len as Lamport)
1582                .ok_or_else(|| {
1583                    LoroError::DecodeError("Decode block error: lamport overflow".into())
1584                })?,
1585        );
1586        let content = ChangesBlockContent::Bytes(bytes);
1587        Ok(Self {
1588            peer,
1589            estimated_size: len,
1590            counter_range,
1591            lamport_range,
1592            flushed: true,
1593            content,
1594        })
1595    }
1596
1597    #[allow(dead_code)]
1598    pub(crate) fn content(&self) -> &ChangesBlockContent {
1599        &self.content
1600    }
1601
1602    fn new(change: Change, _a: &SharedArena) -> Self {
1603        let atom_len = change.atom_len();
1604        let counter_range = (change.id.counter, change.id.counter + atom_len as Counter);
1605        let lamport_range = (change.lamport, change.lamport + atom_len as Lamport);
1606        let estimated_size = change.estimate_storage_size();
1607        let peer = change.id.peer;
1608        let content = ChangesBlockContent::Changes(Arc::new(vec![change]));
1609        Self {
1610            peer,
1611            counter_range,
1612            lamport_range,
1613            estimated_size,
1614            content,
1615            flushed: false,
1616        }
1617    }
1618
1619    #[allow(unused)]
1620    fn cmp_id(&self, id: ID) -> Ordering {
1621        self.peer.cmp(&id.peer).then_with(|| {
1622            if self.counter_range.0 > id.counter {
1623                Ordering::Greater
1624            } else if self.counter_range.1 <= id.counter {
1625                Ordering::Less
1626            } else {
1627                Ordering::Equal
1628            }
1629        })
1630    }
1631
1632    #[allow(unused)]
1633    fn cmp_idlp(&self, idlp: (PeerID, Lamport)) -> Ordering {
1634        self.peer.cmp(&idlp.0).then_with(|| {
1635            if self.lamport_range.0 > idlp.1 {
1636                Ordering::Greater
1637            } else if self.lamport_range.1 <= idlp.1 {
1638                Ordering::Less
1639            } else {
1640                Ordering::Equal
1641            }
1642        })
1643    }
1644
1645    #[allow(unused)]
1646    fn is_full(&self) -> bool {
1647        self.estimated_size > MAX_BLOCK_SIZE
1648    }
1649
1650    #[allow(clippy::result_large_err)]
1651    fn push_change(
1652        self: &mut Arc<Self>,
1653        change: Change,
1654        new_change_size: usize,
1655        merge_interval: i64,
1656        a: &SharedArena,
1657    ) -> Result<(), Change> {
1658        if self.counter_range.1 != change.id.counter {
1659            return Err(change);
1660        }
1661
1662        let atom_len = change.atom_len();
1663        let next_lamport = change.lamport + atom_len as Lamport;
1664        let next_counter = change.id.counter + atom_len as Counter;
1665
1666        let is_full = new_change_size + self.estimated_size > MAX_BLOCK_SIZE;
1667        let this = Arc::make_mut(self);
1668        let changes = this.content.changes_mut(a).unwrap();
1669        let changes = Arc::make_mut(changes);
1670        match changes.last_mut() {
1671            Some(last)
1672                if last.can_merge_right(&change, merge_interval)
1673                    && (!is_full
1674                        || (change.ops.len() == 1
1675                            && last.ops.last().unwrap().is_mergable(&change.ops[0], &()))) =>
1676            {
1677                for op in change.ops.into_iter() {
1678                    let size = op.estimate_storage_size();
1679                    if !last.ops.push(op) {
1680                        this.estimated_size += size;
1681                    }
1682                }
1683            }
1684            _ => {
1685                if is_full {
1686                    return Err(change);
1687                } else {
1688                    this.estimated_size += new_change_size;
1689                    changes.push(change);
1690                }
1691            }
1692        }
1693
1694        this.flushed = false;
1695        this.counter_range.1 = next_counter;
1696        this.lamport_range.1 = next_lamport;
1697        Ok(())
1698    }
1699
1700    fn to_bytes(self: &mut Arc<Self>, a: &SharedArena) -> ChangesBlockBytes {
1701        match &self.content {
1702            ChangesBlockContent::Bytes(bytes) => bytes.clone(),
1703            ChangesBlockContent::Both(_, bytes) => {
1704                let bytes = bytes.clone();
1705                let this = Arc::make_mut(self);
1706                this.content = ChangesBlockContent::Bytes(bytes.clone());
1707                bytes
1708            }
1709            ChangesBlockContent::Changes(changes) => {
1710                let bytes = ChangesBlockBytes::serialize(changes, a);
1711                let this = Arc::make_mut(self);
1712                this.content = ChangesBlockContent::Bytes(bytes.clone());
1713                bytes
1714            }
1715        }
1716    }
1717
1718    fn ensure_changes(self: &mut Arc<Self>, a: &SharedArena) -> LoroResult<()> {
1719        match &self.content {
1720            ChangesBlockContent::Changes(_) => Ok(()),
1721            ChangesBlockContent::Both(_, _) => Ok(()),
1722            ChangesBlockContent::Bytes(bytes) => {
1723                let changes = bytes.parse(a)?;
1724                let b = bytes.clone();
1725                let this = Arc::make_mut(self);
1726                this.content = ChangesBlockContent::Both(Arc::new(changes), b);
1727                Ok(())
1728            }
1729        }
1730    }
1731
1732    fn get_change_index_by_counter(&self, counter: Counter) -> Result<usize, usize> {
1733        let changes = self.content.try_changes().unwrap();
1734        changes.binary_search_by(|c| {
1735            if c.id.counter > counter {
1736                Ordering::Greater
1737            } else if (c.id.counter + c.content_len() as Counter) <= counter {
1738                Ordering::Less
1739            } else {
1740                Ordering::Equal
1741            }
1742        })
1743    }
1744
1745    fn get_change_index_by_lamport_lte(&self, lamport: Lamport) -> Option<usize> {
1746        let changes = self.content.try_changes().unwrap();
1747        let r = changes.binary_search_by(|c| {
1748            if c.lamport > lamport {
1749                Ordering::Greater
1750            } else if (c.lamport + c.content_len() as Lamport) <= lamport {
1751                Ordering::Less
1752            } else {
1753                Ordering::Equal
1754            }
1755        });
1756
1757        match r {
1758            Ok(found) => Some(found),
1759            Err(idx) => {
1760                if idx == 0 {
1761                    None
1762                } else {
1763                    Some(idx - 1)
1764                }
1765            }
1766        }
1767    }
1768
1769    #[allow(unused)]
1770    fn get_changes(&mut self, a: &SharedArena) -> LoroResult<&Vec<Change>> {
1771        self.content.changes(a)
1772    }
1773
1774    #[allow(unused)]
1775    fn id(&self) -> ID {
1776        ID::new(self.peer, self.counter_range.0)
1777    }
1778
1779    pub fn change_num(&self) -> usize {
1780        match &self.content {
1781            ChangesBlockContent::Changes(c) => c.len(),
1782            ChangesBlockContent::Bytes(b) => b.len_changes(),
1783            ChangesBlockContent::Both(c, _) => c.len(),
1784        }
1785    }
1786}
1787
1788impl ChangesBlockContent {
1789    // TODO: PERF: We can use Iter to replace Vec
1790    pub fn iter_dag_nodes(&self) -> Vec<AppDagNode> {
1791        let mut dag_nodes = Vec::new();
1792        match self {
1793            ChangesBlockContent::Changes(c) | ChangesBlockContent::Both(c, _) => {
1794                for change in c.iter() {
1795                    let new_node = AppDagNodeInner {
1796                        peer: change.id.peer,
1797                        cnt: change.id.counter,
1798                        lamport: change.lamport,
1799                        deps: change.deps.clone(),
1800                        vv: OnceCell::new(),
1801                        has_succ: false,
1802                        len: change.atom_len(),
1803                    }
1804                    .into();
1805
1806                    dag_nodes.push_rle_element(new_node);
1807                }
1808            }
1809            ChangesBlockContent::Bytes(b) => {
1810                b.ensure_header().unwrap();
1811                let header = b.header.get().unwrap();
1812                let n = header.n_changes;
1813                for i in 0..n {
1814                    let new_node = AppDagNodeInner {
1815                        peer: header.peer,
1816                        cnt: header.counters[i],
1817                        lamport: header.lamports[i],
1818                        deps: header.deps_groups[i].clone(),
1819                        vv: OnceCell::new(),
1820                        has_succ: false,
1821                        len: (header.counters[i + 1] - header.counters[i]) as usize,
1822                    }
1823                    .into();
1824
1825                    dag_nodes.push_rle_element(new_node);
1826                }
1827            }
1828        }
1829
1830        dag_nodes
1831    }
1832
1833    #[allow(unused)]
1834    pub fn changes(&mut self, a: &SharedArena) -> LoroResult<&Vec<Change>> {
1835        match self {
1836            ChangesBlockContent::Changes(changes) => Ok(changes),
1837            ChangesBlockContent::Both(changes, _) => Ok(changes),
1838            ChangesBlockContent::Bytes(bytes) => {
1839                let changes = bytes.parse(a)?;
1840                *self = ChangesBlockContent::Both(Arc::new(changes), bytes.clone());
1841                self.changes(a)
1842            }
1843        }
1844    }
1845
1846    /// Note that this method will invalidate the stored bytes
1847    fn changes_mut(&mut self, a: &SharedArena) -> LoroResult<&mut Arc<Vec<Change>>> {
1848        match self {
1849            ChangesBlockContent::Changes(changes) => Ok(changes),
1850            ChangesBlockContent::Both(changes, _) => {
1851                *self = ChangesBlockContent::Changes(std::mem::take(changes));
1852                self.changes_mut(a)
1853            }
1854            ChangesBlockContent::Bytes(bytes) => {
1855                let changes = bytes.parse(a)?;
1856                *self = ChangesBlockContent::Changes(Arc::new(changes));
1857                self.changes_mut(a)
1858            }
1859        }
1860    }
1861
1862    pub(crate) fn try_changes(&self) -> Option<&Vec<Change>> {
1863        match self {
1864            ChangesBlockContent::Changes(changes) => Some(changes),
1865            ChangesBlockContent::Both(changes, _) => Some(changes),
1866            ChangesBlockContent::Bytes(_) => None,
1867        }
1868    }
1869
1870    #[allow(dead_code)]
1871    pub(crate) fn len_changes(&self) -> usize {
1872        match self {
1873            ChangesBlockContent::Changes(changes) => changes.len(),
1874            ChangesBlockContent::Both(changes, _) => changes.len(),
1875            ChangesBlockContent::Bytes(bytes) => bytes.len_changes(),
1876        }
1877    }
1878}
1879
1880impl std::fmt::Debug for ChangesBlockContent {
1881    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1882        match self {
1883            ChangesBlockContent::Changes(changes) => f
1884                .debug_tuple("ChangesBlockContent::Changes")
1885                .field(changes)
1886                .finish(),
1887            ChangesBlockContent::Bytes(_bytes) => {
1888                f.debug_tuple("ChangesBlockContent::Bytes").finish()
1889            }
1890            ChangesBlockContent::Both(changes, _bytes) => f
1891                .debug_tuple("ChangesBlockContent::Both")
1892                .field(changes)
1893                .finish(),
1894        }
1895    }
1896}
1897
1898impl ChangesBlockBytes {
1899    fn new(bytes: Bytes) -> Self {
1900        Self {
1901            header: OnceCell::new(),
1902            bytes,
1903        }
1904    }
1905
1906    fn ensure_header(&self) -> LoroResult<()> {
1907        self.header
1908            .get_or_try_init(|| decode_header(&self.bytes).map(Arc::new))?;
1909        Ok(())
1910    }
1911
1912    fn parse(&self, a: &SharedArena) -> LoroResult<Vec<Change>> {
1913        self.ensure_header()?;
1914        let ans: Vec<Change> = decode_block(&self.bytes, a, self.header.get().map(|h| h.as_ref()))?;
1915        for c in ans.iter() {
1916            // PERF: This can be made faster (low priority)
1917            register_container_and_parent_link(a, c)
1918        }
1919
1920        Ok(ans)
1921    }
1922
1923    fn serialize(changes: &[Change], a: &SharedArena) -> Self {
1924        let bytes = encode_block(changes, a);
1925        // TODO: Perf we can calculate header directly without parsing the bytes
1926        let bytes = ChangesBlockBytes::new(Bytes::from(bytes));
1927        bytes.ensure_header().unwrap();
1928        bytes
1929    }
1930
1931    fn lamport_range(&mut self) -> LoroResult<(Lamport, Lamport)> {
1932        if let Some(header) = self.header.get() {
1933            Ok((header.lamports[0], *header.lamports.last().unwrap()))
1934        } else {
1935            decode_block_range(&self.bytes).map(|(_, lamport_range)| lamport_range)
1936        }
1937    }
1938
1939    /// Length of the changes
1940    fn len_changes(&self) -> usize {
1941        self.ensure_header().unwrap();
1942        self.header.get().unwrap().n_changes
1943    }
1944}
1945
1946#[cfg(test)]
1947mod test {
1948    use crate::cursor::PosType;
1949    use crate::{
1950        loro::ExportMode, oplog::convert_change_to_remote, state::TreeParentId, ListHandler,
1951        LoroDoc, MovableListHandler, TextHandler, TreeHandler,
1952    };
1953
1954    use super::*;
1955
1956    fn test_encode_decode(doc: LoroDoc) {
1957        doc.commit_then_renew();
1958        let oplog = doc.oplog().lock();
1959        let bytes = oplog
1960            .change_store
1961            .encode_all(oplog.vv(), oplog.dag.frontiers());
1962        let store = ChangeStore::new_for_test();
1963        let _ = store.import_all(bytes.clone()).unwrap();
1964        assert_eq!(store.external_kv.lock().export_all(), bytes);
1965        let mut changes_parsed = Vec::new();
1966        let a = store.arena.clone();
1967        store.visit_all_changes(&mut |c| {
1968            changes_parsed.push(convert_change_to_remote(&a, c));
1969        });
1970        let mut changes = Vec::new();
1971        oplog.change_store.visit_all_changes(&mut |c| {
1972            changes.push(convert_change_to_remote(&oplog.arena, c));
1973        });
1974        assert_eq!(changes_parsed, changes);
1975    }
1976
1977    #[test]
1978    fn decoded_block_lamport_range_matches_counter_range() {
1979        // Regression test for a checkout hang after snapshot import.
1980        // `ChangesBlock::from_bytes` used the start lamport of the block's last
1981        // change as the block's end lamport, producing degenerate lamport
1982        // ranges (empty for single-change blocks). The binary search in
1983        // `get_change_by_lamport_lte` then misclassified the block containing
1984        // the target lamport and looped forever.
1985        let doc = LoroDoc::new_auto_commit();
1986        doc.set_peer_id(1).unwrap();
1987        // One big commit that splits into many blocks, with enough ops that
1988        // lamport lookups engage the binary search path
1989        // (lamport gap > MAX_BLOCK_SIZE * 8).
1990        for i in 0..100 {
1991            let text = doc.get_text(format!("t{i}").as_str());
1992            text.insert(0, &"x".repeat(30), PosType::Unicode).unwrap();
1993        }
1994        doc.commit_then_renew();
1995
1996        let (bytes, end_counter) = {
1997            let oplog = doc.oplog().lock();
1998            let end = oplog.vv().get(&1).copied().unwrap();
1999            let bytes = oplog
2000                .change_store
2001                .encode_all(oplog.vv(), oplog.dag.frontiers());
2002            (bytes, end)
2003        };
2004
2005        let store = ChangeStore::new_for_test();
2006        let _ = store.import_all(bytes).unwrap();
2007        // Parse every block out of the external kv store
2008        let mut c = 0;
2009        while c < end_counter {
2010            let change = store.get_change(ID::new(1, c)).unwrap();
2011            c = change.id.counter + change.atom_len() as Counter;
2012        }
2013
2014        {
2015            let inner = store.inner.lock();
2016            assert!(
2017                inner.mem_parsed_kv.len() > 1,
2018                "the change should be split into multiple blocks"
2019            );
2020            for (id, block) in inner.mem_parsed_kv.iter() {
2021                // Single-peer linear history: lamport == counter for every op
2022                assert_eq!(id.counter, block.counter_range.0);
2023                assert_eq!(block.lamport_range.0 as Counter, block.counter_range.0);
2024                assert_eq!(block.lamport_range.1 as Counter, block.counter_range.1);
2025            }
2026        }
2027
2028        for l in (0..end_counter as Lamport).step_by(7) {
2029            let change = store.get_change_by_lamport_lte(IdLp::new(1, l)).unwrap();
2030            assert!(change.lamport <= l);
2031            assert!(l < change.lamport + change.atom_len() as Lamport);
2032        }
2033    }
2034
2035    #[test]
2036    fn lamport_lookup_finds_unflushed_mem_blocks() {
2037        // Regression: when the lamport binary search bails out, the fallback
2038        // used to scan only the external kv store. Local changes may exist
2039        // solely in `mem_parsed_kv` before any flush, so a lookup targeting a
2040        // lamport gap incorrectly returned `None`.
2041        let doc = LoroDoc::new_auto_commit();
2042        doc.set_peer_id(1).unwrap();
2043        let text = doc.get_text("t");
2044        text.insert(0, &"x".repeat(500), PosType::Unicode).unwrap();
2045        doc.commit_then_renew();
2046
2047        // An independent large change from peer 2 pushes peer 1's next
2048        // lamport far above its first change (gap > MAX_BLOCK_SIZE * 8, so
2049        // lookups below engage the binary search path).
2050        let doc2 = LoroDoc::new_auto_commit();
2051        doc2.set_peer_id(2).unwrap();
2052        let text2 = doc2.get_text("t2");
2053        text2
2054            .insert(0, &"y".repeat(3000), PosType::Unicode)
2055            .unwrap();
2056        doc2.commit_then_renew();
2057        doc.import(&doc2.export(ExportMode::all_updates()).unwrap())
2058            .unwrap();
2059
2060        let text = doc.get_text("t");
2061        text.insert(0, &"z".repeat(500), PosType::Unicode).unwrap();
2062        doc.commit_then_renew();
2063
2064        // Query a lamport inside the gap between peer 1's two changes,
2065        // without flushing the change store. The answer (the tail of peer 1's
2066        // first commit) lives only in `mem_parsed_kv`.
2067        let oplog = doc.oplog().lock();
2068        let change = oplog
2069            .change_store
2070            .get_change_by_lamport_lte(IdLp::new(1, 700))
2071            .expect("the change should be found in the unflushed mem blocks");
2072        assert_eq!(change.id.peer, 1);
2073        assert!(change.lamport <= 700);
2074        assert_eq!(change.id.counter + change.atom_len() as Counter, 500);
2075    }
2076
2077    #[test]
2078    fn root_history_names_reject_oversized_name_without_retaining_it() {
2079        let mut names = FxHashSet::default();
2080        let mut name_bytes = 0;
2081        let oversized_name = "x".repeat(MAX_ROOT_HISTORY_NAME_BYTES + 1);
2082        let oversized = ContainerID::new_root(&oversized_name, crate::ContainerType::Map);
2083
2084        assert!(!record_root_name(&mut names, &mut name_bytes, &oversized));
2085        assert!(names.is_empty());
2086        assert_eq!(name_bytes, 0);
2087    }
2088
2089    #[test]
2090    fn test_change_store() {
2091        let doc = LoroDoc::new_auto_commit();
2092        doc.set_record_timestamp(true);
2093        let t = doc.get_text("t");
2094        t.insert(0, "hello", PosType::Unicode).unwrap();
2095        doc.commit_then_renew();
2096        let t = doc.get_list("t");
2097        t.insert(0, "hello").unwrap();
2098        test_encode_decode(doc);
2099    }
2100
2101    #[test]
2102    fn test_synced_doc() -> LoroResult<()> {
2103        let doc_a = LoroDoc::new_auto_commit();
2104        let doc_b = LoroDoc::new_auto_commit();
2105        let doc_c = LoroDoc::new_auto_commit();
2106
2107        {
2108            // A: Create initial structure
2109            let map = doc_a.get_map("root");
2110            map.insert_container("text", TextHandler::new_detached())?;
2111            map.insert_container("list", ListHandler::new_detached())?;
2112            map.insert_container("tree", TreeHandler::new_detached())?;
2113        }
2114
2115        {
2116            // Sync initial state to B and C
2117            let initial_state = doc_a.export(ExportMode::all_updates()).unwrap();
2118            doc_b.import(&initial_state)?;
2119            doc_c.import(&initial_state)?;
2120        }
2121
2122        {
2123            // B: Edit text and list
2124            let map = doc_b.get_map("root");
2125            let text = map
2126                .insert_container("text", TextHandler::new_detached())
2127                .unwrap();
2128            text.insert(0, "Hello, ", PosType::Unicode)?;
2129
2130            let list = map
2131                .insert_container("list", ListHandler::new_detached())
2132                .unwrap();
2133            list.push("world")?;
2134        }
2135
2136        {
2137            // C: Edit tree and movable list
2138            let map = doc_c.get_map("root");
2139            let tree = map
2140                .insert_container("tree", TreeHandler::new_detached())
2141                .unwrap();
2142            let node_id = tree.create(TreeParentId::Root)?;
2143            tree.get_meta(node_id)?.insert("key", "value")?;
2144            let node_b = tree.create(TreeParentId::Root)?;
2145            tree.move_to(node_b, TreeParentId::Root, 0).unwrap();
2146
2147            let movable_list = map
2148                .insert_container("movable", MovableListHandler::new_detached())
2149                .unwrap();
2150            movable_list.push("item1".into())?;
2151            movable_list.push("item2".into())?;
2152            movable_list.mov(0, 1)?;
2153        }
2154
2155        // Sync B's changes to A
2156        let b_changes = doc_b
2157            .export(ExportMode::updates(&doc_a.oplog_vv()))
2158            .unwrap();
2159        doc_a.import(&b_changes)?;
2160
2161        // Sync C's changes to A
2162        let c_changes = doc_c
2163            .export(ExportMode::updates(&doc_a.oplog_vv()))
2164            .unwrap();
2165        doc_a.import(&c_changes)?;
2166
2167        test_encode_decode(doc_a);
2168        Ok(())
2169    }
2170}