jupyter-server/pycrdt

Allow nested transactions

davidbrochart opened this issue · 1 comments

Currently, nested transactions are not allowed because that would lead to multiple TransactionMut on a document:

def foo(doc):
    with doc.transaction():
        text = doc.get_text("text")
        text += ", World!"

with doc.transaction():
    text = doc.get_text("text")
    text += "Hello"
    foo(doc)  # will fail

This hurts modularity, for instance if we wanted foo to be used independently. Now foo has to check if there is already a transaction on the document:

def foo(doc, txn=None):
    if txn is None:
        with doc.transaction():
            text = doc.get_text("text")
            text += ", World!"
    else:
        text = doc.get_text("text")
        text += ", World!"

with doc.transaction() as txn:
    text = doc.get_text("text")
    text += "Hello"
    foo(doc, txn)

See an example of such a workaround in jupyter-ydoc. This is not only more complicated, but this doesn't even do what is expected: the changes in foo are "merged" into the parent transaction, which might not be desirable because we wanted them to be grouped into their own transaction.
I think that for nested transactions to work, the context manager should only create the transaction at exit, and make the changes then. This means that every change made in the context manager should be registered first.

Actually since registering changes would not affect the data structures, querying some information about their state would not return the right result, for instance:

with doc.transaction():
    text += "Hello"
    if len(text) > 10:  # the previous operation was actually not done yet
        ...

A potential solution would be to mirror every change in a "parallel" data structure (not a Y type), but I don't want to get into that.
Meanwhile, #8 allows nested transactions.