Why we're not using yrs
One of the biggest reason why we are writing our own yjs compatible Rust implementation is yrs is not safe for multi-threading.
You can run the following code to see the problem:
cargo run --bin yrs-is-unsafe
The source codes is under the yrs-is-unsafe:
main.rs
use std::thread::spawn;
use yrs::Doc;
fn main() {
let doc = Doc::new();
let t1 = {
let doc = doc.clone();
spawn(move || {
let _ = doc.get_or_insert_map("text");
})
};
let t2 = {
let doc = doc.clone();
spawn(move || {
let _ = doc.get_or_insert_map("text");
})
};
t1.join().unwrap();
t2.join().unwrap();
}
We are facing this problem in our Rust server, and the iOS/Android clients which are running with the multi-threading runtime.
Adding a global lock is not a good solution in this case. First the performance will be bad, and deadlocks will come after that. In general the deadlocks in application level are more difficult to debug than in the library level. Second, we assume the Rust compiler can guarantee the multi-threading safety, and with yrs we must to guarantee the safety by ourselves, and it often leads to bugs.
In the previous versions of Mysc mobile apps, there are always oom issues happened. You can run the following command to see the problem:
cargo build --release --bin yrs-mem
/usr/bin/time -l ./target/release/yrs-mem
mem_usage.rs
use yrs::{updates::decoder::Decode, Update};
fn main() {
if let Ok(_) = Update::decode_v1(&[255, 255, 255, 122]) {};
}
On my MacBook pro, the results is like that:
.05 real 0.01 user 0.04 sys
538050560 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
32959 page reclaims
1 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
0 voluntary context switches
5 involuntary context switches
298179580 instructions retired
166031219 cycles elapsed
538003008 peak memory footprint
There is 538050560
bytes memory used, which is about 538MB. It's too bad for mobile apps or the similar low memory devices.
Unlike most of the Rust libraries, yrs panics everywhere, rather than returning the Result
type. It causes the application crash easily without the guarantee of the compiler's safety checks. We must add catch_unwind
everywhere in our application to avoid the crash, that is bad.