1mod change_store;
2pub(crate) mod loro_dag;
3mod pending_changes;
4
5use crate::sync::{AtomicUsize, Mutex};
6use bytes::Bytes;
7use std::borrow::Cow;
8use std::cell::RefCell;
9use std::cmp::Ordering;
10use std::rc::Rc;
11use std::sync::Arc;
12use tracing::trace_span;
13
14use self::change_store::iter::MergedChangeIter;
15use self::pending_changes::{PendingChanges, PendingChangesRollback};
16use super::arena::{SharedArena, SharedArenaRollback};
17use crate::change::{get_sys_timestamp, Change, Lamport, Timestamp};
18use crate::configure::Configure;
19use crate::container::idx::ContainerIdx;
20use crate::container::list::list_op;
21use crate::dag::{Dag, DagUtils, MeetAsBase};
22use crate::diff_calc::DiffMode;
23use crate::encoding::decode_oplog;
24use crate::encoding::{ImportStatus, ParsedHeaderAndBody};
25use crate::history_cache::ContainerHistoryCache;
26use crate::id::{Counter, PeerID, ID};
27use crate::op::{FutureInnerContent, ListSlice, RawOpContent, RemoteOp, RichOp};
28use crate::span::{HasCounterSpan, HasLamportSpan};
29use crate::version::{Frontiers, ImVersionVector, VersionVector};
30use crate::LoroError;
31use change_store::{BlockOpRef, ChangeStoreRollback};
32use loro_common::{ContainerType, HasIdSpan, IdLp, IdSpan};
33use rle::{HasLength, RleVec, Sliceable};
34use rustc_hash::FxHashSet;
35use smallvec::SmallVec;
36
37pub use self::loro_dag::{AppDag, AppDagNode, FrontiersNotIncluded};
38pub use change_store::{BlockChangeRef, ChangeStore};
39
40pub struct OpLog {
48 pub(crate) dag: AppDag,
49 pub(crate) arena: SharedArena,
50 visible_op_count: Arc<AtomicUsize>,
51 change_store: ChangeStore,
52 history_cache: Mutex<ContainerHistoryCache>,
53 pub(crate) pending_changes: PendingChanges,
57 pub(crate) batch_importing: bool,
60 pub(crate) configure: Configure,
61 pub(crate) uncommitted_change: Option<Change>,
64 pub(crate) import_rollback: Option<ImportRollback>,
65}
66
67pub(crate) struct ImportRollback {
68 old_vv: VersionVector,
69 arena: SharedArenaRollback,
70 change_store: ChangeStoreRollback,
71 pending: PendingChangesRollback,
72}
73
74#[derive(Debug, Default, Clone, Copy)]
75pub(crate) struct ImportChangesPreflight {
76 pub applies_to_dag: bool,
77 pub has_deps_before_shallow_root: bool,
78 pub needs_state_apply_rollback: bool,
79}
80
81impl std::fmt::Debug for OpLog {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 f.debug_struct("OpLog")
84 .field("dag", &self.dag)
85 .field("pending_changes", &self.pending_changes)
86 .finish()
87 }
88}
89
90#[cfg(test)]
91thread_local! {
92 pub(crate) static CRITICAL_BASE_FALLBACK_COUNT: std::cell::Cell<u64> =
97 const { std::cell::Cell::new(0) };
98}
99
100#[derive(Debug, PartialEq, Eq)]
102pub(crate) enum CriticalVersionSearch {
103 Found(VersionVector),
105 NoneBelowMeet,
110 Unknown,
113}
114
115#[derive(Debug)]
117pub(crate) struct ReplayBase {
118 pub vv: VersionVector,
119 pub diff_mode: DiffMode,
120 pub is_critical: bool,
126 pub concurrent_containers: Option<FxHashSet<ContainerIdx>>,
130}
131
132impl OpLog {
133 #[inline]
134 pub(crate) fn new(visible_op_count: Arc<AtomicUsize>) -> Self {
135 let arena = SharedArena::new();
136 let cfg = Configure::default();
137 let change_store = ChangeStore::new_mem(&arena, cfg.merge_interval_in_s.clone());
138 Self {
139 visible_op_count,
140 history_cache: Mutex::new(ContainerHistoryCache::new(change_store.clone(), None)),
141 dag: AppDag::new(change_store.clone()),
142 change_store,
143 arena,
144 pending_changes: Default::default(),
145 batch_importing: false,
146 configure: cfg,
147 uncommitted_change: None,
148 import_rollback: None,
149 }
150 }
151
152 #[inline]
153 fn calc_visible_op_count(&self) -> usize {
154 let total = self.dag.vv().values().sum::<i32>() as usize;
155 let shallow = self
156 .dag
157 .shallow_since_vv()
158 .iter()
159 .map(|(_, ops)| *ops)
160 .sum::<i32>() as usize;
161 total - shallow
162 }
163
164 #[inline]
165 pub(crate) fn visible_op_count_exact(&self) -> usize {
166 self.calc_visible_op_count()
167 }
168
169 #[inline]
170 pub(crate) fn refresh_visible_op_count(&self) -> usize {
171 let count = self.calc_visible_op_count();
172 self.visible_op_count
173 .store(count, std::sync::atomic::Ordering::Release);
174 count
175 }
176
177 #[inline]
183 pub(crate) fn inc_visible_op_count(&self, delta: usize) {
184 self.visible_op_count
185 .fetch_add(delta, std::sync::atomic::Ordering::Release);
186 }
187
188 #[cfg(test)]
189 pub(crate) fn cached_visible_op_count(&self) -> usize {
190 self.visible_op_count
191 .load(std::sync::atomic::Ordering::Acquire)
192 }
193
194 #[inline]
195 pub fn dag(&self) -> &AppDag {
196 &self.dag
197 }
198
199 pub fn change_store(&self) -> &ChangeStore {
200 &self.change_store
201 }
202
203 pub fn get_change_with_lamport_lte(
207 &self,
208 peer: PeerID,
209 lamport: Lamport,
210 ) -> Option<BlockChangeRef> {
211 let ans = self
212 .change_store
213 .get_change_by_lamport_lte(IdLp::new(peer, lamport))?;
214 debug_assert!(ans.lamport <= lamport);
215 Some(ans)
216 }
217
218 pub fn get_timestamp_of_version(&self, f: &Frontiers) -> Timestamp {
219 let mut timestamp = Timestamp::default();
220 for id in f.iter() {
221 if let Some(change) = self.lookup_change(id) {
222 timestamp = timestamp.max(change.timestamp);
223 }
224 }
225
226 timestamp
227 }
228
229 #[inline]
230 pub fn is_empty(&self) -> bool {
231 self.dag.is_empty() && self.arena.can_import_snapshot()
232 }
233
234 pub(crate) fn insert_new_change(&mut self, change: Change, from_local: bool) {
236 let s = trace_span!(
237 "insert_new_change",
238 id = ?change.id,
239 lamport = change.lamport,
240 deps = ?change.deps
241 );
242 let _enter = s.enter();
243 let rollback_old_vv = self
244 .import_rollback
245 .as_ref()
246 .and_then(|x| (!x.old_vv.is_empty()).then_some(&x.old_vv));
247 self.dag
248 .handle_new_change(&change, from_local, rollback_old_vv);
249 self.history_cache
250 .lock()
251 .insert_by_new_change(&change, true, true);
252 self.register_container_and_parent_link(&change);
253 if let Some(rollback) = self.import_rollback.as_mut() {
254 self.change_store.insert_change_with_rollback(
255 change,
256 true,
257 from_local,
258 &mut rollback.change_store,
259 );
260 } else {
261 self.change_store.insert_change(change, true, from_local);
262 }
263 self.refresh_visible_op_count();
264 }
265
266 pub(crate) fn begin_import_rollback(&mut self) {
267 let arena = self.arena.checkpoint_for_rollback();
268 self.begin_import_rollback_with_arena(arena);
269 }
270
271 pub(crate) fn begin_import_rollback_with_arena(&mut self, arena: SharedArenaRollback) {
272 debug_assert!(self.import_rollback.is_none());
273 let old_vv = self.vv().clone();
274 self.dag.begin_import_rollback();
275 self.import_rollback = Some(ImportRollback {
276 old_vv: old_vv.clone(),
277 arena,
278 change_store: ChangeStoreRollback::new(old_vv),
279 pending: Default::default(),
280 });
281 }
282
283 pub(crate) fn has_import_rollback(&self) -> bool {
290 self.import_rollback.is_some()
291 }
292
293 pub(crate) fn commit_import_rollback(&mut self) {
294 self.dag.commit_import_rollback();
295 self.import_rollback = None;
296 }
297
298 pub(crate) fn end_import_rollback(&mut self, owns: bool, keep: bool) {
302 if !owns {
303 return;
304 }
305
306 if keep {
307 self.commit_import_rollback();
308 } else {
309 self.rollback_import();
310 }
311 }
312
313 pub(crate) fn preflight_import_changes(&self, changes: &[Change]) -> ImportChangesPreflight {
314 let mut ans = ImportChangesPreflight::default();
315 for change in changes {
316 if change.ctr_end() <= self.vv().get(&change.id.peer).copied().unwrap_or(0) {
317 continue;
318 }
319
320 if self.dag.import_deps_before_shallow_root(&change.deps) {
321 ans.has_deps_before_shallow_root = true;
322 continue;
323 }
324
325 if self
326 .dag
327 .get_change_lamport_from_deps(&change.deps)
328 .is_none()
329 {
330 continue;
331 }
332
333 ans.applies_to_dag = true;
334 if change.ops.iter().any(|op| {
335 matches!(
336 op.container.get_type(),
337 ContainerType::List | ContainerType::Tree
338 )
339 }) {
340 ans.needs_state_apply_rollback = true;
341 }
342 }
343
344 if ans.applies_to_dag
357 && !ans.needs_state_apply_rollback
358 && self.pending_changes.has_state_apply_rollback_ops()
359 {
360 ans.needs_state_apply_rollback = true;
361 }
362
363 #[cfg(test)]
364 if ans.applies_to_dag {
365 ans.needs_state_apply_rollback = true;
366 }
367
368 ans
369 }
370
371 pub(crate) fn rollback_import(&mut self) {
372 let Some(rollback) = self.import_rollback.take() else {
373 return;
374 };
375
376 self.change_store.rollback_import(rollback.change_store);
377 self.dag.rollback_import();
378 rollback.pending.rollback(&mut self.pending_changes);
379 self.history_cache.lock().free_all();
380 self.arena.rollback(rollback.arena);
381 self.refresh_visible_op_count();
382 }
383
384 pub(crate) fn reset_to_empty_for_failed_snapshot_import(
385 &mut self,
386 arena_checkpoint: SharedArenaRollback,
387 ) {
388 let arena = self.arena.clone();
389 let configure = self.configure.clone();
390 arena.rollback(arena_checkpoint);
391 let change_store = ChangeStore::new_mem(&arena, configure.merge_interval_in_s.clone());
392 self.history_cache = Mutex::new(ContainerHistoryCache::new(change_store.clone(), None));
393 self.dag = AppDag::new(change_store.clone());
394 self.change_store = change_store;
395 self.pending_changes = Default::default();
396 self.batch_importing = false;
397 self.configure = configure;
398 self.uncommitted_change = None;
399 self.import_rollback = None;
400 self.visible_op_count
401 .store(0, std::sync::atomic::Ordering::Release);
402 }
403
404 #[inline(always)]
405 pub(crate) fn with_history_cache<F, R>(&self, f: F) -> R
406 where
407 F: FnOnce(&mut ContainerHistoryCache) -> R,
408 {
409 let mut history_cache = self.history_cache.lock();
410 f(&mut history_cache)
411 }
412
413 pub fn has_history_cache(&self) -> bool {
414 self.history_cache.lock().has_cache()
415 }
416
417 pub fn free_history_cache(&self) {
418 let mut history_cache = self.history_cache.lock();
419 history_cache.free();
420 }
421
422 #[cfg(test)]
423 #[allow(dead_code)]
424 pub(crate) fn pending_changes_len(&self) -> usize {
425 self.pending_changes.len()
426 }
427
428 pub(crate) fn import_local_change(&mut self, change: Change) -> Result<(), LoroError> {
439 self.insert_new_change(change, true);
440 Ok(())
441 }
442
443 pub(crate) fn trim_the_known_part_of_change(&self, change: Change) -> Option<Change> {
445 let Some(&end) = self.dag.vv().get(&change.id.peer) else {
446 return Some(change);
447 };
448
449 if change.id.counter >= end {
450 return Some(change);
451 }
452
453 if change.ctr_end() <= end {
454 return None;
455 }
456
457 let offset = (end - change.id.counter) as usize;
458 Some(change.slice(offset, change.atom_len()))
459 }
460
461 #[allow(unused)]
462 fn check_id_is_not_duplicated(&self, id: ID) -> Result<(), LoroError> {
463 let cur_end = self.dag.vv().get(&id.peer).cloned().unwrap_or(0);
464 if cur_end > id.counter {
465 return Err(LoroError::UsedOpID { id });
466 }
467
468 Ok(())
469 }
470
471 pub(crate) fn check_change_greater_than_last_peer_id(
476 &self,
477 peer: PeerID,
478 counter: Counter,
479 deps: &Frontiers,
480 ) -> Result<(), LoroError> {
481 if counter == 0 {
482 return Ok(());
483 }
484
485 if !self.configure.detached_editing() {
486 return Ok(());
487 }
488
489 let mut max_last_counter = -1;
490 for dep in deps.iter() {
491 let dep_vv = self
492 .dag
493 .get_vv(dep)
494 .ok_or(LoroError::FrontiersNotFound(dep))?;
495 max_last_counter = max_last_counter.max(dep_vv.get(&peer).cloned().unwrap_or(0) - 1);
496 }
497
498 if counter != max_last_counter + 1 {
499 return Err(LoroError::ConcurrentOpsWithSamePeerID {
500 peer,
501 last_counter: max_last_counter,
502 current: counter,
503 });
504 }
505
506 Ok(())
507 }
508
509 pub(crate) fn next_id(&self, peer: PeerID) -> ID {
510 let cnt = self.dag.vv().get(&peer).copied().unwrap_or(0);
511 ID::new(peer, cnt)
512 }
513
514 pub(crate) fn vv(&self) -> &VersionVector {
515 self.dag.vv()
516 }
517
518 pub(crate) fn frontiers(&self) -> &Frontiers {
519 self.dag.frontiers()
520 }
521
522 pub fn cmp_with_frontiers(&self, other: &Frontiers) -> Ordering {
526 self.dag.cmp_with_frontiers(other)
527 }
528
529 #[inline]
533 pub fn cmp_frontiers(
534 &self,
535 a: &Frontiers,
536 b: &Frontiers,
537 ) -> Result<Option<Ordering>, FrontiersNotIncluded> {
538 self.dag.cmp_frontiers(a, b)
539 }
540
541 pub(crate) fn get_min_lamport_at(&self, id: ID) -> Lamport {
542 self.get_change_at(id).map(|c| c.lamport).unwrap_or(0)
543 }
544
545 pub(crate) fn get_lamport_at(&self, id: ID) -> Option<Lamport> {
546 self.get_change_at(id)
547 .map(|c| c.lamport + (id.counter - c.id.counter) as Lamport)
548 }
549
550 pub(crate) fn iter_ops(&self, id_span: IdSpan) -> impl Iterator<Item = RichOp<'static>> + '_ {
551 let change_iter = self.change_store.iter_changes(id_span);
552 change_iter.flat_map(move |c| RichOp::new_iter_by_cnt_range(c, id_span.counter))
553 }
554
555 pub(crate) fn iter_changes(
556 &self,
557 id_span: IdSpan,
558 ) -> impl Iterator<Item = BlockChangeRef> + '_ {
559 self.change_store.iter_changes(id_span)
560 }
561
562 pub(crate) fn get_max_lamport_at(&self, id: ID) -> Lamport {
563 self.get_change_at(id)
564 .map(|c| {
565 let change_counter = c.id.counter as u32;
566 c.lamport + c.ops().last().map(|op| op.counter).unwrap_or(0) as u32 - change_counter
567 })
568 .unwrap_or(Lamport::MAX)
569 }
570
571 pub fn get_change_at(&self, id: ID) -> Option<BlockChangeRef> {
572 self.change_store.get_change(id)
573 }
574
575 pub(crate) fn set_uncommitted_change(&mut self, change: Change) {
576 self.uncommitted_change = Some(change);
577 }
578
579 pub(crate) fn get_uncommitted_change_in_span(
580 &self,
581 id_span: IdSpan,
582 ) -> Option<Cow<'_, Change>> {
583 self.uncommitted_change.as_ref().and_then(|c| {
584 if c.id_span() == id_span {
585 Some(Cow::Borrowed(c))
586 } else if let Some((start, end)) = id_span.get_slice_range_on(&c.id_span()) {
587 Some(Cow::Owned(c.slice(start, end)))
588 } else {
589 None
590 }
591 })
592 }
593
594 pub fn get_deps_of(&self, id: ID) -> Option<Frontiers> {
595 self.get_change_at(id).map(|c| {
596 if c.id.counter == id.counter {
597 c.deps.clone()
598 } else {
599 Frontiers::from_id(id.inc(-1))
600 }
601 })
602 }
603
604 pub fn get_remote_change_at(&self, id: ID) -> Option<Change<RemoteOp<'static>>> {
605 let change = self.get_change_at(id)?;
606 Some(convert_change_to_remote(&self.arena, &change))
607 }
608
609 pub(crate) fn import_unknown_lamport_pending_changes(
610 &mut self,
611 remote_changes: Vec<Change>,
612 would_affect: Option<&mut crate::version::VersionRange>,
613 ) -> crate::version::VersionRange {
614 self.extend_pending_changes_with_unknown_lamport(remote_changes, would_affect)
615 }
616
617 pub(crate) fn lookup_change(&self, id: ID) -> Option<BlockChangeRef> {
621 self.change_store.get_change(id)
622 }
623
624 #[inline(always)]
625 pub(crate) fn export_change_store_from(&self, vv: &VersionVector, f: &Frontiers) -> Bytes {
626 self.change_store
627 .export_from(vv, f, self.vv(), self.frontiers())
628 }
629
630 #[inline(always)]
631 pub(crate) fn export_change_store_in_range(
632 &self,
633 vv: &VersionVector,
634 f: &Frontiers,
635 to_vv: &VersionVector,
636 to_frontiers: &Frontiers,
637 ) -> Bytes {
638 self.change_store.export_from(vv, f, to_vv, to_frontiers)
639 }
640
641 #[inline(always)]
642 pub(crate) fn export_blocks_from<W: std::io::Write>(&self, vv: &VersionVector, w: &mut W) {
643 self.change_store
644 .export_blocks_from(vv, self.shallow_since_vv(), self.vv(), w)
645 }
646
647 #[inline(always)]
648 pub(crate) fn export_blocks_in_range<W: std::io::Write>(&self, spans: &[IdSpan], w: &mut W) {
649 self.change_store.export_blocks_in_range(spans, w)
650 }
651
652 pub(crate) fn fork_changes_up_to(&self, frontiers: &Frontiers) -> Option<Bytes> {
653 let vv = self.dag.frontiers_to_vv(frontiers)?;
654 Some(
655 self.change_store
656 .fork_changes_up_to(self.dag.shallow_since_vv(), frontiers, &vv),
657 )
658 }
659
660 #[inline(always)]
661 pub(crate) fn decode(&mut self, data: ParsedHeaderAndBody) -> Result<ImportStatus, LoroError> {
662 decode_oplog(self, data)
663 }
664
665 pub(crate) fn containers_in_spans(
670 &self,
671 spans: impl Iterator<Item = IdSpan>,
672 ) -> FxHashSet<ContainerIdx> {
673 let mut containers = FxHashSet::default();
674 let mut last_container = None;
677 for span in spans {
678 for change in self.change_store.iter_changes(span) {
679 let start_counter = span.counter.min().max(change.id.counter);
680 let end_counter = span.counter.norm_end();
681 let start = change
682 .ops
683 .binary_search_by(|op| op.ctr_last().cmp(&start_counter))
684 .unwrap_or_else(|e| e);
685 for op in &change.ops.vec()[start..] {
686 if op.counter >= end_counter {
687 break;
688 }
689
690 if last_container != Some(op.container) {
691 containers.insert(op.container);
692 last_container = Some(op.container);
693 }
694 }
695 }
696 }
697
698 containers
699 }
700
701 fn uncovered_entry_parents(
719 &self,
720 from: &VersionVector,
721 from_frontiers: &Frontiers,
722 to: &VersionVector,
723 ) -> Option<Vec<Frontiers>> {
724 let mut uncovered = Vec::new();
725 for (peer, span) in from.diff(to).forward.iter() {
726 let id_span = IdSpan::new(*peer, span.start, span.end);
727 for change in self.change_store.iter_changes(id_span) {
728 let start = change.id.counter.max(span.start);
729 let parents = if start > change.id.counter {
730 Frontiers::from_id(ID::new(*peer, start - 1))
733 } else {
734 let mut parents = change.deps().clone();
735 if change.id.counter > 0 {
736 let prev = ID::new(*peer, change.id.counter - 1);
737 if !parents.contains(&prev) {
738 parents.push(prev);
739 }
740 }
741 parents
742 };
743
744 if &parents == from_frontiers {
747 continue;
748 }
749
750 if !parents.iter().all(|id| from.includes_id(id)) {
751 continue;
754 }
755
756 let parents_vv = self.dag.frontiers_to_vv(&parents)?;
757 if !parents_vv.includes_vv(from) {
758 uncovered.push(parents);
759 }
760 }
761 }
762
763 Some(uncovered)
764 }
765
766 fn register_only_concurrency(
787 &self,
788 from: &VersionVector,
789 to: &VersionVector,
790 entry_parents: &[Frontiers],
791 ) -> Option<FxHashSet<ContainerIdx>> {
792 let mut causal_past = from.clone();
794 for parents in entry_parents {
795 let parents_vv = self.dag.frontiers_to_vv(parents)?;
796 causal_past.retain(|peer, end| {
797 let bound = parents_vv.get(peer).copied().unwrap_or(0);
798 *end = (*end).min(bound);
799 *end > 0
800 });
801 }
802
803 if !causal_past.includes_vv(&self.dag.shallow_since_vv().to_vv()) {
809 return None;
810 }
811
812 let concurrent_old = causal_past.diff(from).forward;
813 let old_containers = self.containers_in_spans(
814 concurrent_old
815 .iter()
816 .map(|(peer, span)| IdSpan::new(*peer, span.start, span.end)),
817 );
818 if old_containers.is_empty() {
819 return Some(old_containers);
820 }
821
822 let new_region = from.diff(to).forward;
823 let new_containers = self.containers_in_spans(
824 new_region
825 .iter()
826 .map(|(peer, span)| IdSpan::new(*peer, span.start, span.end)),
827 );
828 let harmless = old_containers
829 .iter()
830 .filter(|idx| new_containers.contains(idx))
831 .all(|idx| match idx.get_type() {
832 ContainerType::Map => true,
833 #[cfg(feature = "counter")]
834 ContainerType::Counter => true,
835 _ => false,
836 });
837 harmless.then_some(old_containers)
838 }
839
840 pub(crate) fn latest_critical_version_below_meet(
866 &self,
867 from: &VersionVector,
868 to: &VersionVector,
869 merged: &VersionVector,
870 ) -> CriticalVersionSearch {
871 use CriticalVersionSearch::*;
872
873 let mut v = from.intersection(to);
876 if v.is_empty() {
877 return NoneBelowMeet;
878 }
879
880 let mut pending: Vec<IdSpan> = v.diff_iter(merged).1.collect();
881 while let Some(span) = pending.pop() {
882 for change in self.change_store.iter_changes(span) {
883 let Some(mut ctx) = self.dag.frontiers_to_vv(&change.deps) else {
887 return Unknown;
890 };
891 let cut = v.get(&change.id.peer).copied().unwrap_or(0);
892 let first_above = change.id.counter.max(cut);
893 ctx.extend_to_include_end_id(ID::new(change.id.peer, first_above));
894
895 if !ctx.includes_vv(&v) {
896 let above = v.clone();
897 v.intersect_with(&ctx);
898 if v.is_empty() {
899 return NoneBelowMeet;
900 }
901 pending.extend(v.diff_iter(&above).1);
902 }
903 }
904 }
905
906 Found(v)
907 }
908
909 #[allow(clippy::type_complexity)]
931 pub(crate) fn iter_from_replay_base_causally(
932 &self,
933 from: &VersionVector,
934 from_frontiers: &Frontiers,
935 to: &VersionVector,
936 to_frontiers: &Frontiers,
937 ) -> (
938 ReplayBase,
939 impl Iterator<
940 Item = (
941 BlockChangeRef,
942 (Counter, Counter),
943 Rc<RefCell<VersionVector>>,
944 ),
945 > + '_,
946 ) {
947 let mut merged_vv = from.clone();
948 merged_vv.merge(to);
949 loro_common::debug!("to_frontiers={:?} vv={:?}", &to_frontiers, to);
950 let mut concurrent_containers = None;
951 let mut register_only_base = None;
952 if to > from {
953 if let Some(entry_parents) = self.uncovered_entry_parents(from, from_frontiers, to) {
959 if !entry_parents.is_empty() {
960 if let Some(containers) =
961 self.register_only_concurrency(from, to, &entry_parents)
962 {
963 concurrent_containers = Some(containers);
964 register_only_base = Some(from_frontiers.clone());
965 }
966 }
967 }
968 }
969
970 let (meet, mut diff_mode) = if let Some(base) = register_only_base {
971 (MeetAsBase::Valid(base), DiffMode::ImportGreaterUpdates)
972 } else {
973 self.dag.find_meet_and_mode(from_frontiers, to_frontiers)
974 };
975 if diff_mode == DiffMode::Checkout && to > from {
976 diff_mode = DiffMode::Import;
977 }
978
979 let shallow_since_vv = self.dag.shallow_since_vv().to_vv();
980 let mut replay_base_is_critical = false;
981 let mut replay_base_frontiers = match meet {
982 MeetAsBase::Valid(meet) => meet,
983 MeetAsBase::NeedsCriticalRetreat => {
984 let descend = || {
990 #[cfg(test)]
991 CRITICAL_BASE_FALLBACK_COUNT.with(|c| c.set(c.get() + 1));
992 self.dag
993 .latest_single_head_critical_version(from_frontiers, to_frontiers)
994 };
995 match self.latest_critical_version_below_meet(from, to, &merged_vv) {
996 CriticalVersionSearch::Found(v) => {
997 let f = self.dag.vv_to_frontiers(&v);
1007 let seed = self
1008 .dag
1009 .frontiers_to_vv(self.dag.shallow_since_frontiers())
1010 .unwrap();
1011 if v.includes_vv(&seed) && self.dag.frontiers_to_vv(&f).as_ref() == Some(&v)
1012 {
1013 replay_base_is_critical = true;
1014 f
1015 } else {
1016 descend()
1017 }
1018 }
1019 CriticalVersionSearch::NoneBelowMeet => {
1020 Frontiers::default()
1026 }
1027 CriticalVersionSearch::Unknown => descend(),
1028 }
1029 }
1030 };
1031
1032 let mut replay_base_vv = self.dag.frontiers_to_vv(&replay_base_frontiers).unwrap();
1033 replay_base_is_critical |= replay_base_vv.is_empty();
1034 if !replay_base_vv.includes_vv(&shallow_since_vv) {
1035 replay_base_frontiers = self.dag.shallow_since_frontiers().clone();
1038 replay_base_vv = self
1039 .dag
1040 .frontiers_to_vv(&replay_base_frontiers)
1041 .unwrap_or(shallow_since_vv);
1042 replay_base_is_critical = false;
1043 }
1044
1045 let diff = replay_base_vv.diff(&merged_vv).forward;
1047 let mut iter = self.dag.iter_causal(replay_base_frontiers, diff);
1048 let mut node = iter.next();
1049 let mut cur_cnt = 0;
1050 let vv = Rc::new(RefCell::new(VersionVector::default()));
1051 (
1052 ReplayBase {
1053 vv: replay_base_vv.clone(),
1054 diff_mode,
1055 is_critical: replay_base_is_critical,
1056 concurrent_containers,
1057 },
1058 std::iter::from_fn(move || {
1059 if let Some(inner) = &node {
1060 let mut inner_vv = vv.borrow_mut();
1061 inner_vv.clear();
1063 self.dag.ensure_vv_for(&inner.data);
1064 inner_vv.extend_to_include_vv(inner.data.vv.get().unwrap().iter());
1065 let peer = inner.data.peer;
1066 let cnt = inner
1067 .data
1068 .cnt
1069 .max(cur_cnt)
1070 .max(replay_base_vv.get(&peer).copied().unwrap_or(0));
1071 let dag_node_end = (inner.data.cnt + inner.data.len as Counter)
1072 .min(merged_vv.get(&peer).copied().unwrap_or(0));
1073 let change = self.change_store.get_change(ID::new(peer, cnt)).unwrap();
1074
1075 if change.ctr_end() < dag_node_end {
1076 cur_cnt = change.ctr_end();
1077 } else {
1078 node = iter.next();
1079 cur_cnt = 0;
1080 }
1081
1082 inner_vv.extend_to_include_end_id(change.id);
1083
1084 Some((change, (cnt, dag_node_end), vv.clone()))
1085 } else {
1086 None
1087 }
1088 }),
1089 )
1090 }
1091
1092 pub fn len_changes(&self) -> usize {
1093 self.change_store.change_num()
1094 }
1095
1096 pub fn diagnose_size(&self) -> SizeInfo {
1097 let mut total_changes = 0;
1098 let mut total_ops = 0;
1099 let mut total_atom_ops = 0;
1100 let total_dag_node = self.dag.total_parsed_dag_node();
1101 self.change_store.visit_all_changes(&mut |change| {
1102 total_changes += 1;
1103 total_ops += change.ops.len();
1104 total_atom_ops += change.atom_len();
1105 });
1106
1107 println!("total changes: {}", total_changes);
1108 println!("total ops: {}", total_ops);
1109 println!("total atom ops: {}", total_atom_ops);
1110 println!("total dag node: {}", total_dag_node);
1111 SizeInfo {
1112 total_changes,
1113 total_ops,
1114 total_atom_ops,
1115 total_dag_node,
1116 }
1117 }
1118
1119 pub(crate) fn iter_changes_peer_by_peer<'a>(
1120 &'a self,
1121 from: &VersionVector,
1122 to: &VersionVector,
1123 ) -> impl Iterator<Item = BlockChangeRef> + 'a {
1124 let spans: Vec<_> = from.diff_iter(to).1.collect();
1125 spans
1126 .into_iter()
1127 .flat_map(move |span| self.change_store.iter_changes(span))
1128 }
1129
1130 #[allow(dead_code)]
1131 pub(crate) fn iter_changes_causally_rev<'a>(
1132 &'a self,
1133 from: &VersionVector,
1134 to: &VersionVector,
1135 ) -> impl Iterator<Item = BlockChangeRef> + 'a {
1136 MergedChangeIter::new_change_iter_rev(self, from, to)
1137 }
1138
1139 pub fn get_timestamp_for_next_txn(&self) -> Timestamp {
1140 if self.configure.record_timestamp() {
1141 get_timestamp_now_txn()
1142 } else {
1143 0
1144 }
1145 }
1146
1147 #[inline(never)]
1148 pub(crate) fn idlp_to_id(&self, id: loro_common::IdLp) -> Option<ID> {
1149 let change = self.change_store.get_change_by_lamport_lte(id)?;
1150
1151 if change.lamport > id.lamport || change.lamport_end() <= id.lamport {
1152 return None;
1153 }
1154
1155 Some(ID::new(
1156 change.id.peer,
1157 (id.lamport - change.lamport) as Counter + change.id.counter,
1158 ))
1159 }
1160
1161 #[allow(unused)]
1162 pub(crate) fn id_to_idlp(&self, id_start: ID) -> IdLp {
1163 let change = self.get_change_at(id_start).unwrap();
1164 let lamport = change.lamport + (id_start.counter - change.id.counter) as Lamport;
1165 let peer = id_start.peer;
1166 loro_common::IdLp { peer, lamport }
1167 }
1168
1169 pub(crate) fn get_op_that_includes(&self, id: ID) -> Option<BlockOpRef> {
1171 let change = self.get_change_at(id)?;
1172 change.get_op_with_counter(id.counter)
1173 }
1174
1175 pub(crate) fn split_span_based_on_deps(&self, id_span: IdSpan) -> Vec<(IdSpan, Frontiers)> {
1176 let peer = id_span.peer;
1177 let mut counter = id_span.counter.min();
1178 let span_end = id_span.counter.norm_end();
1179 let mut ans = Vec::new();
1180
1181 while counter < span_end {
1182 let id = ID::new(peer, counter);
1183 let node = self.dag.get(id).unwrap();
1184
1185 let f = if node.cnt == counter {
1186 node.deps.clone()
1187 } else if counter > 0 {
1188 id.inc(-1).into()
1189 } else {
1190 unreachable!()
1191 };
1192
1193 let cur_end = node.cnt + node.len as Counter;
1194 let len = cur_end.min(span_end) - counter;
1195 ans.push((id.to_span(len as usize), f));
1196 counter += len;
1197 }
1198
1199 ans
1200 }
1201
1202 #[inline]
1203 pub fn compact_change_store(&mut self) {
1204 self.change_store
1205 .flush_and_compact(self.dag.vv(), self.dag.frontiers());
1206 }
1207
1208 #[inline]
1209 pub fn change_store_kv_size(&self) -> usize {
1210 self.change_store.kv_size()
1211 }
1212
1213 pub fn encode_change_store(&self) -> bytes::Bytes {
1214 self.change_store
1215 .encode_all(self.dag.vv(), self.dag.frontiers())
1216 }
1217
1218 pub fn check_dag_correctness(&self) {
1219 self.dag.check_dag_correctness();
1220 }
1221
1222 pub fn shallow_since_vv(&self) -> &ImVersionVector {
1223 self.dag.shallow_since_vv()
1224 }
1225
1226 pub fn shallow_since_frontiers(&self) -> &Frontiers {
1227 self.dag.shallow_since_frontiers()
1228 }
1229
1230 pub fn is_shallow(&self) -> bool {
1231 !self.dag.shallow_since_vv().is_empty()
1232 }
1233
1234 pub fn get_greatest_timestamp(&self, frontiers: &Frontiers) -> Timestamp {
1235 let mut max_timestamp = Timestamp::default();
1236 for id in frontiers.iter() {
1237 let change = self.get_change_at(id).unwrap();
1238 if change.timestamp > max_timestamp {
1239 max_timestamp = change.timestamp;
1240 }
1241 }
1242
1243 max_timestamp
1244 }
1245}
1246
1247#[derive(Debug)]
1248pub struct SizeInfo {
1249 pub total_changes: usize,
1250 pub total_ops: usize,
1251 pub total_atom_ops: usize,
1252 pub total_dag_node: usize,
1253}
1254
1255pub(crate) fn convert_change_to_remote(
1256 arena: &SharedArena,
1257 change: &Change,
1258) -> Change<RemoteOp<'static>> {
1259 let mut ops = RleVec::new();
1260 for op in change.ops.iter() {
1261 for op in local_op_to_remote(arena, op) {
1262 ops.push(op);
1263 }
1264 }
1265
1266 Change {
1267 ops,
1268 id: change.id,
1269 deps: change.deps.clone(),
1270 lamport: change.lamport,
1271 timestamp: change.timestamp,
1272 commit_msg: change.commit_msg.clone(),
1273 }
1274}
1275
1276pub(crate) fn local_op_to_remote(
1277 arena: &SharedArena,
1278 op: &crate::op::Op,
1279) -> SmallVec<[RemoteOp<'static>; 1]> {
1280 let container = arena.get_container_id(op.container).unwrap();
1281 let mut contents: SmallVec<[_; 1]> = SmallVec::new();
1282 match &op.content {
1283 crate::op::InnerContent::List(list) => match list {
1284 list_op::InnerListOp::Insert { slice, pos } => match container.container_type() {
1285 loro_common::ContainerType::Text => {
1286 let str = arena
1287 .slice_str_by_unicode_range(slice.0.start as usize..slice.0.end as usize);
1288 contents.push(RawOpContent::List(list_op::ListOp::Insert {
1289 slice: ListSlice::RawStr {
1290 unicode_len: str.chars().count(),
1291 str: Cow::Owned(str),
1292 },
1293 pos: *pos,
1294 }));
1295 }
1296 loro_common::ContainerType::List | loro_common::ContainerType::MovableList => {
1297 contents.push(RawOpContent::List(list_op::ListOp::Insert {
1298 slice: ListSlice::RawData(Cow::Owned(
1299 arena.get_values(slice.0.start as usize..slice.0.end as usize),
1300 )),
1301 pos: *pos,
1302 }))
1303 }
1304 _ => unreachable!(),
1305 },
1306 list_op::InnerListOp::InsertText {
1307 slice,
1308 unicode_len: len,
1309 unicode_start: _,
1310 pos,
1311 } => match container.container_type() {
1312 loro_common::ContainerType::Text => {
1313 contents.push(RawOpContent::List(list_op::ListOp::Insert {
1314 slice: ListSlice::RawStr {
1315 unicode_len: *len as usize,
1316 str: Cow::Owned(std::str::from_utf8(slice).unwrap().to_owned()),
1317 },
1318 pos: *pos as usize,
1319 }));
1320 }
1321 _ => unreachable!(),
1322 },
1323 list_op::InnerListOp::Delete(del) => {
1324 contents.push(RawOpContent::List(list_op::ListOp::Delete(*del)))
1325 }
1326 list_op::InnerListOp::StyleStart {
1327 start,
1328 end,
1329 key,
1330 value,
1331 info,
1332 } => contents.push(RawOpContent::List(list_op::ListOp::StyleStart {
1333 start: *start,
1334 end: *end,
1335 key: key.clone(),
1336 value: value.clone(),
1337 info: *info,
1338 })),
1339 list_op::InnerListOp::StyleEnd => {
1340 contents.push(RawOpContent::List(list_op::ListOp::StyleEnd))
1341 }
1342 list_op::InnerListOp::Move {
1343 from,
1344 elem_id: from_id,
1345 to,
1346 } => contents.push(RawOpContent::List(list_op::ListOp::Move {
1347 from: *from,
1348 elem_id: *from_id,
1349 to: *to,
1350 })),
1351 list_op::InnerListOp::Set { elem_id, value } => {
1352 contents.push(RawOpContent::List(list_op::ListOp::Set {
1353 elem_id: *elem_id,
1354 value: value.clone(),
1355 }))
1356 }
1357 },
1358 crate::op::InnerContent::Map(map) => {
1359 let value = map.value.clone();
1360 contents.push(RawOpContent::Map(crate::container::map::MapSet {
1361 key: map.key.clone(),
1362 value,
1363 }))
1364 }
1365 crate::op::InnerContent::Tree(tree) => contents.push(RawOpContent::Tree(tree.clone())),
1366 crate::op::InnerContent::Future(f) => match f {
1367 #[cfg(feature = "counter")]
1368 crate::op::FutureInnerContent::Counter(c) => contents.push(RawOpContent::Counter(*c)),
1369 FutureInnerContent::Unknown { prop, value } => {
1370 contents.push(crate::op::RawOpContent::Unknown {
1371 prop: *prop,
1372 value: (**value).clone(),
1373 })
1374 }
1375 },
1376 };
1377
1378 let mut ans = SmallVec::with_capacity(contents.len());
1379 for content in contents {
1380 ans.push(RemoteOp {
1381 container: container.clone(),
1382 content,
1383 counter: op.counter,
1384 })
1385 }
1386 ans
1387}
1388
1389pub(crate) fn get_timestamp_now_txn() -> Timestamp {
1390 (get_sys_timestamp() as Timestamp + 500) / 1000
1391}
1392
1393#[cfg(test)]
1394mod visible_op_count_tests {
1395 use crate::{cursor::PosType, loro::ExportMode, LoroDoc};
1396
1397 #[test]
1401 fn cached_visible_op_count_matches_exact() {
1402 let doc = LoroDoc::new();
1403 let text = doc.get_text("text");
1404 let mut txn = doc.txn().unwrap();
1405 for i in 0..50 {
1406 text.insert_with_txn(&mut txn, i, "a", PosType::Unicode)
1407 .unwrap();
1408 }
1409 txn.commit().unwrap();
1410 {
1411 let oplog = doc.oplog().lock();
1412 assert_eq!(
1413 oplog.cached_visible_op_count(),
1414 oplog.visible_op_count_exact(),
1415 "after local edits"
1416 );
1417 }
1418
1419 let doc2 = LoroDoc::new();
1422 doc2.import(&doc.export(ExportMode::all_updates()).unwrap())
1423 .unwrap();
1424 let text2 = doc2.get_text("text");
1425 let mut txn2 = doc2.txn().unwrap();
1426 text2
1427 .insert_with_txn(&mut txn2, 0, "bbb", PosType::Unicode)
1428 .unwrap();
1429 txn2.commit().unwrap();
1430 {
1431 let oplog = doc2.oplog().lock();
1432 assert_eq!(
1433 oplog.cached_visible_op_count(),
1434 oplog.visible_op_count_exact(),
1435 "after import + local edits"
1436 );
1437 }
1438 }
1439}