Skip to main content

loro_internal/oplog/
change_store.rs

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