diff --git a/crates/fuzz/fuzz/Cargo.lock b/crates/fuzz/fuzz/Cargo.lock index 86356b3d..655bc3a2 100644 --- a/crates/fuzz/fuzz/Cargo.lock +++ b/crates/fuzz/fuzz/Cargo.lock @@ -536,6 +536,7 @@ dependencies = [ "either", "enum-as-inner 0.6.0", "generic-btree", + "loro-common 0.16.12", "loro-delta 0.16.12", "loro-internal 0.16.12", "loro-kv-store", @@ -586,6 +587,7 @@ dependencies = [ "nonmax", "serde", "serde_columnar", + "serde_json", "string_cache", "thiserror", ] diff --git a/crates/fuzz/src/actor.rs b/crates/fuzz/src/actor.rs index 780d21de..305ce5c1 100644 --- a/crates/fuzz/src/actor.rs +++ b/crates/fuzz/src/actor.rs @@ -227,6 +227,7 @@ impl Actor { match self.loro.checkout(&f) { Ok(_) => { // check snapshot correctness after checkout + self.loro.check_state_correctness_slow(); self.loro.checkout_to_latest(); let new_doc = LoroDoc::new(); new_doc diff --git a/crates/fuzz/tests/test.rs b/crates/fuzz/tests/test.rs index 56181911..b3609366 100644 --- a/crates/fuzz/tests/test.rs +++ b/crates/fuzz/tests/test.rs @@ -10784,6 +10784,170 @@ fn gc_fuzz_20() { ) } +#[test] +fn gc_fuzz_21() { + test_multi_sites_with_gc( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 11, + target: 11, + container: 11, + action: Generic(GenericAction { + value: I32(185335563), + bool: true, + key: 3385444809, + pos: 5714873654208057167, + length: 5714873654208057137, + prop: 5714873654208057167, + }), + }, + Checkout { + site: 79, + to: 189747023, + }, + Handle { + site: 11, + target: 38, + container: 11, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 185274159, + pos: 795741901218843403, + length: 795741901218843403, + prop: 795741901218843403, + }), + }, + Handle { + site: 11, + target: 11, + container: 11, + action: Generic(GenericAction { + value: Container(List), + bool: true, + key: 189747023, + pos: 2748896764614085387, + length: 3400217718631893771, + prop: 795741901219114799, + }), + }, + Handle { + site: 11, + target: 11, + container: 11, + action: Generic(GenericAction { + value: I32(185273099), + bool: true, + key: 185273099, + pos: 795741901218843549, + length: 795741901218843403, + prop: 5714873654208102155, + }), + }, + Handle { + site: 11, + target: 11, + container: 11, + action: Generic(GenericAction { + value: I32(805178123), + bool: true, + key: 791613439, + pos: 795741901218843599, + length: 795741901218843403, + prop: 795741901218843403, + }), + }, + Handle { + site: 11, + target: 11, + container: 11, + action: Generic(GenericAction { + value: I32(185273099), + bool: true, + key: 185273099, + pos: 795741901218843403, + length: 18446743021627837195, + prop: 795741909623504895, + }), + }, + Handle { + site: 11, + target: 15, + container: 11, + action: Generic(GenericAction { + value: I32(185273099), + bool: true, + key: 1862994699, + pos: 795741901224567158, + length: 2741296940242897675, + prop: 3458764505415879435, + }), + }, + Handle { + site: 11, + target: 11, + container: 11, + action: Generic(GenericAction { + value: I32(185273099), + bool: true, + key: 185273099, + pos: 795741901218843403, + length: 795741901218843403, + prop: 8001501305011637003, + }), + }, + Undo { + site: 111, + op_len: 1869573999, + }, + Undo { + site: 111, + op_len: 1869573999, + }, + Undo { + site: 79, + op_len: 1330597711, + }, + Checkout { + site: 79, + to: 1330597711, + }, + Checkout { + site: 11, + to: 185273099, + }, + Handle { + site: 11, + target: 11, + container: 254, + action: Generic(GenericAction { + value: I32(185544495), + bool: true, + key: 185273099, + pos: 795741901218843403, + length: 795741901218843403, + prop: 4123339075892218635, + }), + }, + Handle { + site: 11, + target: 11, + container: 11, + action: Generic(GenericAction { + value: I32(1330597887), + bool: true, + key: 1330597711, + pos: 2741296940242897675, + length: 3458764505415879462, + prop: 1302123111085387567, + }), + }, + ], + ) +} + #[test] fn minify() { minify_error( diff --git a/crates/loro-internal/src/encoding/fast_snapshot.rs b/crates/loro-internal/src/encoding/fast_snapshot.rs index 046b531a..e97fb289 100644 --- a/crates/loro-internal/src/encoding/fast_snapshot.rs +++ b/crates/loro-internal/src/encoding/fast_snapshot.rs @@ -15,7 +15,7 @@ //! use std::io::{Read, Write}; -use crate::{oplog::ChangeStore, LoroDoc, OpLog, VersionVector}; +use crate::{encoding::gc, oplog::ChangeStore, LoroDoc, OpLog, VersionVector}; use bytes::{Buf, Bytes}; use loro_common::{IdSpan, LoroError, LoroResult}; use tracing::trace; @@ -161,12 +161,19 @@ impl OpLog { pub(crate) fn encode_snapshot(doc: &LoroDoc, w: &mut W) { let mut state = doc.app_state().try_lock().unwrap(); let oplog = doc.oplog().try_lock().unwrap(); + let is_gc = state.store.gc_store().is_some(); + if is_gc { + let f = oplog.trimmed_frontiers().clone(); + drop(oplog); + drop(state); + gc::export_gc_snapshot(doc, &f, w).unwrap(); + return; + } assert!(!state.is_in_txn()); assert_eq!(oplog.frontiers(), &state.frontiers); let oplog_bytes = oplog.encode_change_store(); state.ensure_all_alive_containers(); - let state_bytes = state.store.encode(); if oplog.is_trimmed() { assert_eq!( @@ -175,11 +182,12 @@ pub(crate) fn encode_snapshot(doc: &LoroDoc, w: &mut W) { ); } + let state_bytes = state.store.encode(); _encode_snapshot( Snapshot { oplog_bytes, state_bytes: Some(state_bytes), - gc_bytes: state.store.encode_gc(), + gc_bytes: Bytes::new(), }, w, ); diff --git a/crates/loro-internal/src/state/container_store.rs b/crates/loro-internal/src/state/container_store.rs index b8e8dbfd..96e5819b 100644 --- a/crates/loro-internal/src/state/container_store.rs +++ b/crates/loro-internal/src/state/container_store.rs @@ -128,14 +128,6 @@ impl ContainerStore { self.store.flush() } - pub fn encode_gc(&mut self) -> Bytes { - if let Some(gc) = self.gc_store.as_mut() { - gc.store.try_lock().unwrap().get_kv().export() - } else { - Bytes::new() - } - } - pub fn trimmed_frontiers(&self) -> Option<&Frontiers> { self.gc_store.as_ref().map(|x| &x.trimmed_frontiers) } diff --git a/crates/loro-internal/src/utils/kv_wrapper.rs b/crates/loro-internal/src/utils/kv_wrapper.rs index 166f963d..e346a1b8 100644 --- a/crates/loro-internal/src/utils/kv_wrapper.rs +++ b/crates/loro-internal/src/utils/kv_wrapper.rs @@ -9,12 +9,6 @@ use loro_kv_store::{mem_store::MemKvConfig, MemKvStore}; use crate::kv_store::KvStore; -pub(crate) enum Status { - BytesOnly, - ImmBoth, - MutState, -} - /// This thin wrapper aims to limit the ability to modify the kv store and make /// it easy to find all the modifications. pub(crate) struct KvWrapper { diff --git a/loro-js/src/index.ts b/loro-js/src/index.ts index 7b7c90a6..6ac23df6 100644 --- a/loro-js/src/index.ts +++ b/loro-js/src/index.ts @@ -6,7 +6,7 @@ export { LoroMovableList, LoroText, LoroTree, LoroTreeNode, MapOp, MovableListOp, OpId, PeerID, Side, TextOp, TreeID, TreeNodeValue, TreeOp, UndoConfig, UndoManager, UnknownOp, Value, VersionVector, decodeImportBlobMeta, setDebug, - newContainerID, newRootContainerID + newContainerID, newRootContainerID, LoroDoc } from "loro-wasm"; import { Container, diff --git a/package.json b/package.json index 23b10ebf..492f2b66 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "check-all": "cargo hack check --each-feature", "build": "cargo build", - "test": "cargo nextest run --features=test_utils && cargo test --doc", + "test": "cargo nextest run --features=test_utils --no-fail-fast && cargo test --doc", "test-all": "nr test && nr test-wasm", "test-wasm": "cd crates/loro-wasm && deno task dev && cd ../../loro-js && pnpm i && pnpm run test", "coverage": "mkdir -p coverage && cargo llvm-cov nextest --features test_utils --lcov > coverage/lcov-nextest.info && cargo llvm-cov report",