pub struct YDoc { /* private fields */ }Expand description
A Y-CRDT based Store implementation with efficient differential saving.
YDoc provides a CRDT-based storage abstraction using the yrs library,
which is a Rust port of Yjs. This allows for real-time collaborative editing
and automatic conflict resolution through the Y-CRDT algorithms.
§Architecture
The YDoc integrates with Eidetica’s transaction system to provide:
- Differential Updates: Only saves incremental changes, not full document state
- Efficient Caching: Caches expensive backend data retrieval operations
- Transaction/Viewer Model: Compatible with Eidetica’s transaction patterns
- Full Y-CRDT API: Direct access to the complete yrs library functionality
§Caching Strategy
To optimize performance, YDoc caches the expensive get_full_state() operation
from the backend and constructs documents and state vectors on-demand from this
cached data. This approach minimizes I/O operations while keeping memory usage low.
§Differential Saving
When saving documents, YDoc calculates diffs relative to the current backend
state rather than saving full document snapshots. This significantly reduces storage
overhead for large documents with incremental changes.
§Usage
The YDoc exposes the underlying Y-CRDT document directly, allowing users
to work with the full yrs API. Changes are automatically captured and stored
when the transaction is committed.
use eidetica::store::YDoc;
use yrs::{Map, Text, Transact};
// Work directly with the yrs document
store.with_doc_mut(|doc| {
let map = doc.get_or_insert_map("root");
let text = doc.get_or_insert_text("document");
let mut txn = doc.transact_mut();
map.insert(&mut txn, "key", "value");
text.insert(&mut txn, 0, "Hello, World!");
Ok(())
}).await?;Implementations§
Source§impl YDoc
impl YDoc
Sourcepub async fn doc(&self) -> Result<Doc>
pub async fn doc(&self) -> Result<Doc>
Gets the current Y-CRDT document, merging all historical state.
This method reconstructs the current state of the Y-CRDT document by:
- Loading the full historical state from the backend (cached)
- Applying any local changes from the current transaction
- Returning a Y-Doc that can be used for reading and further modifications
§Performance
The expensive backend data retrieval is cached, so subsequent calls are fast. Documents are constructed fresh each time to ensure isolation between operations.
§Returns
A Result containing the merged Doc (Y-CRDT document).
§Errors
Returns an error if there are issues deserializing the Y-CRDT updates.
Sourcepub async fn with_doc<F, R>(&self, f: F) -> Result<R>
pub async fn with_doc<F, R>(&self, f: F) -> Result<R>
Executes a function with read-only access to the Y-Doc.
This method provides access to the current state of the document for read-only operations. No changes are persisted.
§Arguments
f- A function that receives the Y-Doc for reading
§Returns
A Result containing the return value of the function.
§Example
let content = store.with_doc(|doc| {
let text = doc.get_or_insert_text("document");
let txn = doc.transact();
Ok(text.get_string(&txn))
}).await?;Sourcepub async fn with_doc_mut<F, R>(&self, f: F) -> Result<R>
pub async fn with_doc_mut<F, R>(&self, f: F) -> Result<R>
Executes a function with access to the Y-Doc and automatically saves changes.
This is the preferred way to make changes to the document as it ensures all changes are captured using differential saving and staged in the transaction for later commit.
§Differential Saving
Changes are saved as diffs relative to the current backend state, which significantly reduces storage overhead compared to saving full document snapshots.
§Arguments
f- A function that receives the Y-Doc and can make modifications
§Returns
A Result containing the return value of the function.
§Example
store.with_doc_mut(|doc| {
let text = doc.get_or_insert_text("document");
let mut txn = doc.transact_mut();
text.insert(&mut txn, 0, "Hello, World!");
Ok(())
}).await?;Sourcepub async fn apply_update(&self, update_data: &[u8]) -> Result<()>
pub async fn apply_update(&self, update_data: &[u8]) -> Result<()>
Applies a Y-CRDT update to the document.
This method is useful for receiving updates from other collaborators or applying updates received through a network provider. The update is applied to the current document state and saved using differential saving.
§Use Cases
- Applying updates from remote collaborators
- Synchronizing with external Y-CRDT instances
- Replaying historical updates
§Arguments
update_data- The binary Y-CRDT update data
§Returns
A Result<()> indicating success or failure.
§Errors
Returns an error if the update data is malformed or cannot be applied.
Sourcepub async fn get_update(&self) -> Result<Vec<u8>>
pub async fn get_update(&self) -> Result<Vec<u8>>
Gets the current state of the document as a binary update.
This method encodes the complete current document state as a Y-CRDT binary update that can be used to synchronize with other instances or persist the current state.
§Use Cases
- Synchronizing document state with other instances
- Creating snapshots of the current state
- Sharing the complete document with new collaborators
§Returns
A Result containing the binary update data representing the full document state.
§Performance
This method constructs the full document state, so it may be expensive for
large documents. For incremental synchronization, consider using the
differential updates automatically saved by with_doc_mut().
Sourcepub async fn save_doc_full(&self, doc: &Doc) -> Result<()>
pub async fn save_doc_full(&self, doc: &Doc) -> Result<()>
Saves the complete document state to the transaction.
This method captures the entire current state of the document and stages it
in the transaction. Unlike save_doc(), this saves the full document
state rather than just the incremental changes.
§When to Use
- When you need to ensure the complete state is captured
- For creating clean snapshots without incremental history
- When differential saving is not suitable for your use case
§Performance Impact
This method is less storage-efficient than save_doc() as it saves the
complete document state regardless of what changes were made.
§Arguments
doc- The Y-CRDT document to save
§Returns
A Result<()> indicating success or failure.
Sourcepub async fn save_doc(&self, doc: &Doc) -> Result<()>
pub async fn save_doc(&self, doc: &Doc) -> Result<()>
Saves the document state using efficient differential encoding.
This method captures only the changes since the current backend state and stages them in the transaction. This is the preferred saving method as it significantly reduces storage overhead for incremental changes.
§Differential Encoding
The method works by:
- Getting the current backend state vector (cached for efficiency)
- Encoding only the changes since that state
- Saving only the incremental diff, not the full document
§Storage Efficiency
For a document with small incremental changes, this can reduce storage requirements by orders of magnitude compared to saving full snapshots.
§Arguments
doc- The Y-CRDT document to save differentially
§Returns
A Result<()> indicating success or failure.
§Performance
This method is optimized for performance - the expensive backend state retrieval is cached, and only minimal diff calculation is performed.
Trait Implementations§
Source§impl Registered for YDoc
impl Registered for YDoc
Source§impl Store for YDoc
impl Store for YDoc
Source§type Data = YrsBinary
type Data = YrsBinary
Source§fn new<'life0, 'async_trait>(
txn: &'life0 Transaction,
subtree_name: String,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn new<'life0, 'async_trait>(
txn: &'life0 Transaction,
subtree_name: String,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Store handle associated with a specific transaction. Read moreSource§fn transaction(&self) -> &Transaction
fn transaction(&self) -> &Transaction
Source§fn default_config() -> Doc
fn default_config() -> Doc
Source§fn init<'life0, 'async_trait>(
txn: &'life0 Transaction,
subtree_name: String,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn init<'life0, 'async_trait>(
txn: &'life0 Transaction,
subtree_name: String,
) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
_index. Read moreSource§fn get_config<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Doc>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_config<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Doc>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
_index subtree. Read moreSource§fn set_config<'life0, 'async_trait>(
&'life0 self,
config: Doc,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn set_config<'life0, 'async_trait>(
&'life0 self,
config: Doc,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
_index subtree. Read moreSource§fn get_height_strategy<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<HeightStrategy>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_height_strategy<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<HeightStrategy>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
_index subtree. Read moreAuto Trait Implementations§
impl !Freeze for YDoc
impl !RefUnwindSafe for YDoc
impl Send for YDoc
impl Sync for YDoc
impl Unpin for YDoc
impl !UnwindSafe for YDoc
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> CompatExt for T
impl<T> CompatExt for T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more