Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve design for apis with transaction #235

Open
darkskygit opened this issue Mar 2, 2023 · 4 comments
Open

improve design for apis with transaction #235

darkskygit opened this issue Mar 2, 2023 · 4 comments
Assignees
Labels
feat New feature or request mod:libs Related to the libs

Comments

@darkskygit
Copy link
Member

Currently all workspace/block apis require transaction, we need call the api with transaction like block.xxx(&trx, ..)

Consider providing the Workspace/Block type encapsulated by with_trx, which includes trx:

struct WrappedBlock {
  block: Block, // original block type
  trx: Transact,// transaction
}

struct WrappedWorkspace {
  workspace: Workspace, // original workspace type
  trx: Transact,// transaction
}

workspace.with_trx(|ws: WrappedWorkspace| {
  let block: WrappedBlock = ws.get(&block_id);
  block.set("key", value);
});
@darkskygit darkskygit added roadmap feat New feature or request mod:libs Related to the libs labels Mar 2, 2023
@darkskygit darkskygit self-assigned this Mar 2, 2023
@fundon
Copy link
Contributor

fundon commented Mar 3, 2023

We can add two structs: Transaction<'doc, T> and TransactionMut<'doc, T>

pub struct Transaction<'doc, T> {
    inner: &'doc T,
    txn: yrs::Transaction<'doc>,
}

impl<'doc, T> Transaction<'doc, T> {
    pub fn new(inner: &'doc T, txn: yrs::Transaction<'doc>) -> Self {
        Self { inner, txn }
    }
}

pub struct TransactionMut<'doc, T> {
    inner: &'doc T,
    txn: yrs::TransactionMut<'doc>,
}

impl<'doc, T> TransactionMut<'doc, T> {
    pub fn new(inner: &'doc T, txn: yrs::TransactionMut<'doc>) -> Self {
        Self { inner, txn }
    }

    pub fn encode_update_v1(&self) -> Vec<u8> {
        self.txn.encode_update_v1()
    }
}

impl<'doc> TransactionMut<'doc, Workspace> {
    pub fn create<B, F>(&mut self, block_id: B, flavor: F) -> Block
    where
        B: AsRef<str>,
        F: AsRef<str>,
    {
        info!("create block: {}", block_id.as_ref());
        Block::new(
            &mut self.txn,
            self.inner,
            block_id,
            flavor,
            self.inner.client_id(),
        )
    }
}

impl<'doc> TransactionMut<'doc, Block> {
    pub fn set<K, T>(&mut self, key: K, value: T)
    where
        K: AsRef<str>,
        T: Into<Any>,
    {
        self.inner.set(&mut self.txn, key.as_ref(), value)
    }
}

And add tow methods to Workspace and Block

    pub fn with_transact<T>(&self, f: impl FnOnce(crate::Transaction<'_, Self>) -> T) -> T {
        f(crate::Transaction::new(self, self.doc.transact()))
    }

    pub fn with_transact_mut<T>(&self, f: impl FnOnce(crate::TransactionMut<'_, Self>) -> T) -> T {
        f(crate::TransactionMut::new(self, self.doc.transact_mut()))
    }

If we want to add Block to Workspace, we can do this:

                workspace.with_transact_mut(|mut t| {
                    let block = t.create(&block_id, "text");

                    let count = block.with_transact_mut(|mut t| {
                        payload
                            .into_iter()
                            .filter_map(|(key, value)| {
                                serde_json::from_value::<Any>(value)
                                    .map(|value| t.set(&key, value))
                                    .ok()
                            })
                            .count()
                    });

                    Some(((count > 0).then(|| t.encode_update_v1()), block))
                })

@fundon
Copy link
Contributor

fundon commented Mar 3, 2023

And add tow methods to Workspace and Block

    pub fn with_transact<T>(&self, f: impl FnOnce(crate::Transaction<'_, Self>) -> T) -> T {
        f(crate::Transaction::new(self, self.doc.transact()))
    }

    pub fn with_transact_mut<T>(&self, f: impl FnOnce(crate::TransactionMut<'_, Self>) -> T) -> T {
        f(crate::TransactionMut::new(self, self.doc.transact_mut()))
    }

It can be abstracted into a Trait.

If we want to add Block to Workspace, we can do this:

                workspace.with_transact_mut(|mut t| {
                    let block = t.create(&block_id, "text");

                    let count = block.with_transact_mut(|mut t| {
                        payload
                            .into_iter()
                            .filter_map(|(key, value)| {
                                serde_json::from_value::<Any>(value)
                                    .map(|value| t.set(&key, value))
                                    .ok()
                            })
                            .count()
                    });

                    Some(((count > 0).then(|| t.encode_update_v1()), block))
                })

Or

block.transaction(|mut t| {}, &mut txn);

block.transaction(|mut t| {});

let transaction = block.transaction();

@darkskygit
Copy link
Member Author

darkskygit commented Mar 4, 2023

i prefer to abstract into traits

Another thing that can be improved is that we need to do some error handling for transform/transact_mut -- i means switch to try_transact/try_transact_mut

The transact function call try_transact in internal and unwrap it directly, this will cause thread panic in a multi-threaded environment.

It may be a good choice to rewrite with_trx as an async function:

// try to get transaction internally with try_transact and automatically retry
workspace.with_transact(|t| {
    // do something with transaction
}).await;

@fundon
Copy link
Contributor

fundon commented Mar 5, 2023

pub fn doc(&self) -> Doc {
self.awareness.read().unwrap().doc().clone()
}

Direct unwrap is definitely wrong.

Using try_read/try_write for lock. This also corresponds to the try_transact/try_transact_mut.

For error handling, Result<Response> is a bit better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat New feature or request mod:libs Related to the libs
Projects
None yet
Development

No branches or pull requests

3 participants