This blog post is a short summary of ecosystem and capabilities of Yrs (read: wires): a fully-compatible Rust port of Yjs library used to build collaborative peer-to-peer applications thanks to the power of Conflict-free Replicated Data Types. They are being used and adopted in many different products such as Jupyter Notebooks, AFFiNE and Evernote. Basics of its architecture, conflict resolution algorithm and ideas behind move semantics have been already covered here on several occasions.
While both Yjs and Yrs can be used for pretty much any P2P collaborative apps scenario, it's easy to tell that the most popular use cases are oriented around support for rich text editors:
- Text operations, including formatting and inserting embedded elements i.e. images or tables.
- Sharing information about cursor position while other peers are making their changes.
- Undo/redo individual peer edits without affecting concurrent modifications coming from other peers.
- Snapshot and preview of past revisions of the document with metadata for diff highlighting between them.
Besides text, it also supports collection types for map, array and XML node representation. Additionally it also supports observers for various lifecycle events used to react on changes occurring in a collaborative document tree structure.
Even though Yjs/Yrs synchronization protocol is very simple and easy to adapt to ones own needs, we didn't stop at just providing a collections library. We've worked on crates supporting synchronization over WebSockets and WebRTC, which are compatible with their equivalents in Yjs.
If you want to persist your document in some sort of database in an efficient manner, you can use LMDB or RockDB (or any sort of persistent key-value store tbh.). If you want to store it on your server, there's also a native extension for PostgreSQL, which provides core synchronization primitives without a need to move data back and forth over the network all the time.
One of the reasons we decided to build a core library in Rust is its potential to bring a shared implementation that can be used across many different languages. This portfolio already includes:
- WebAssembly which hopefully will let us eventually replace Yjs itself with a single core implementation.
- C Foreign Function Interface which makes it possible to use anywhere.
- Python including websockets support.
- Ruby with support for Rails ActionCable channels and Redis for persistence.
- Swift (there's also an independent implementation in pure Swift)
... with JVM and .NET (partially already possible by using a no longer supported ycs project) already in plans. These are the ones we know about and had contact with their contributors. There still might be other ports done by independent developers.
Plans for the future
What new can we expect from Yrs (and Yjs) in the months to come? While ideas come back and forth on Yjs meetings practically every month, here are a few we're determined to achieve:
- Weak reference links between nodes living within document tree.
- Cursors which could give faster and nicer touch to series of operations executed over the consecutive index position.
- Bluetooth LE network synchronization provider.
While we also have plans on continuing improving the performance, currently it's not high on the priority list. The reason is simple: in its current state Yrs and Yjs are already among the fastest libraries in their domain. This is what we have from the get go without putting months or years worth of effort to profile and optimize the code. Compatibility and feature parity between Yjs/Yrs were more important at that moment. That being said I believe that Rust gives us a huge field for improvement in that space and we'll happily work on making Yrs even faster in the future.