PyCRDT Store
pycrdt-store provides persistent storage solutions for `pycrdt`, specifically offering a `SQLiteYStore` implementation. It enables `pycrdt` documents to persist their state beyond application lifetime, supporting features like history squashing, cleanup based on database size, and performance optimizations. The current version is 0.1.3, and the library is under active development with frequent minor releases addressing performance, features, and bug fixes.
Common errors
-
sqlite3.OperationalError: database is locked
cause SQLite databases are designed for single-writer access. This error occurs when multiple processes or threads attempt to write to the same database file concurrently without proper locking mechanisms or when a previous connection was not properly closed.fixEnsure only one `SQLiteYStore` instance or application process is actively writing to a specific SQLite database file at a time. If concurrent writes are necessary, consider using an in-memory database (`:memory:`) for transient data or explore alternative CRDT storage backends that support higher concurrency. -
AttributeError: 'SQLiteYStore' object has no attribute 'setup'
cause This error likely occurs if you are attempting to call `store.setup()` synchronously or if you are on a very old version of `pycrdt-store` where `setup` was not an awaitable method or didn't exist.fixEnsure `pycrdt-store` is updated to a recent version (0.1.0+). Always call `setup()` and `close()` asynchronously: `await store.setup()` and `await store.close()`. -
TypeError: object NoneType can't be used in 'await' expression
cause This often happens if the `store` object itself is `None` (e.g., due to an error in its creation) when you try to `await store.setup()` or `await store.close()`.fixVerify that your `SQLiteYStore` instance is correctly created before attempting to `await` its methods. Check for exceptions during the `SQLiteYStore(...)` constructor call.
Warnings
- gotcha Version 0.1.3 introduced automatic database cleanup and history squashing features (`cleanup_when_db_size_above`, `squash_history_older_than`). While these improve performance and manageability, they are active by default or through configuration. Users upgrading may want to review and explicitly configure these options to match their desired data retention and database size policies.
- gotcha Asynchronous setup and teardown of `SQLiteYStore` are crucial. Forgetting to `await store.setup()` or `await store.close()` can lead to uninitialized stores, resource leaks, or unsaved data, especially in file-based persistence scenarios.
- gotcha While `SQLiteYStore` can manage multiple `YDoc` instances, each `YDoc` must have a unique `doc_name` when associated with the same store. Reusing a `doc_name` for different `YDoc` objects can lead to data corruption or unexpected behavior.
Install
-
pip install pycrdt-store
Imports
- SQLiteYStore
from pycrdt_store import SQLiteYStore
- YDoc
from pycrdt import YDoc
Quickstart
import asyncio
from pycrdt import YDoc
from pycrdt_store import SQLiteYStore
async def main():
# Use an in-memory database for quick testing, or provide a file path
# e.g., SQLiteYStore(path="./my_document.db")
store = SQLiteYStore(path=":memory:")
await store.setup() # Initialize the store connection
doc_name = "my_persistent_doc"
ydoc = YDoc(doc_name, store) # Associate YDoc with the store
# Perform CRDT operations
text = ydoc.get_text("my_text")
text.insert(0, "Hello, world!")
# Changes are automatically synchronized with the store by YDoc
print(f"Document content: {text.to_py()}")
await store.close() # Close the store connection
if __name__ == "__main__":
asyncio.run(main())