Skip to content

My repository only accepts 10 changes #70

Open
@pierreprinetti

Description

I am trying to build a Taskwarrior clone with Automerge, Automerge-repo and Autosurgeon.

However, I can only do so many changes to my repository, until it starts appearing frozen. I don't know whether the problem is the insertion, or the listing, and I get no errors in either operation.

The reproducer

I have tried to remove as much as possible of my own logic, and only leave in the glue code and a minimal reproducer.

This program opens (or creates) an automerge repository in ./data, adds one item, and prints a list of all items.

The first 10 executions work as intended. From the 11th on, the list stays at 10 items.

rm -rf ./data
cargo build
for i in {1..11}; do
    ./target/debug/tsk
done

The full code is here: https://codeberg.org/pierreprinetti/tsk/src/branch/debug

and here below I paste the contents of main.src for completeness.

use anyhow::Result;
use automerge_repo::{DocumentId, StorageError};
use autosurgeon::{hydrate, reconcile};
use futures::future::BoxFuture;
use std::path::PathBuf;
use task::{Task, Tasks};

mod task {
    use autosurgeon::{Hydrate, Reconcile};
    use std::fmt;

    #[derive(Default, Debug, Clone, Reconcile, Hydrate, PartialEq)]
    pub struct Annotation {
        created: String,
        text: String,
    }

    impl fmt::Display for Annotation {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{}", self.text)
        }
    }

    #[derive(Default, Debug, Clone, Reconcile, Hydrate, PartialEq)]
    pub struct Task {
        pub description: String,
    }

    impl Task {
        pub fn new(name: &str) -> Self {
            return Task {
                description: name.to_string(),
            };
        }
    }

    #[derive(Default, Debug, Clone, Reconcile, Hydrate, PartialEq)]
    pub struct Tasks {
        pub tasks: Vec<Task>,
    }
}

struct Fs {
    fs_storage: automerge_repo::fs_store::FsStore,
}

impl Fs {
    pub fn new(p: std::path::PathBuf) -> Self {
        let fs_storage =
            automerge_repo::fs_store::FsStore::open(p).expect("could not open the storage");
        Fs { fs_storage }
    }
}

impl automerge_repo::Storage for Fs {
    fn get(&self, id: DocumentId) -> BoxFuture<'static, Result<Option<Vec<u8>>, StorageError>> {
        Box::pin(futures::future::ready(
            self.fs_storage
                .get(&id)
                .map_err(move |_| StorageError::Error),
        ))
    }

    fn list_all(&self) -> BoxFuture<'static, Result<Vec<DocumentId>, StorageError>> {
        Box::pin(futures::future::ready(
            self.fs_storage.list().map_err(move |_| StorageError::Error),
        ))
    }

    fn append(
        &self,
        id: DocumentId,
        changes: Vec<u8>,
    ) -> BoxFuture<'static, Result<(), StorageError>> {
        Box::pin(futures::future::ready(
            self.fs_storage
                .append(&id, &changes)
                .map_err(move |_| StorageError::Error),
        ))
    }

    fn compact(&self, _: DocumentId, _: Vec<u8>) -> BoxFuture<'static, Result<(), StorageError>> {
        Box::pin(futures::future::ready(Ok(())))
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    println!("--- start ---");
    tracing_subscriber::fmt::init();
    let repo_handle = {
        let data_dir = Fs::new(PathBuf::from("./data"));
        let repo = automerge_repo::Repo::new(None, Box::new(data_dir));
        repo.run()
    };
    let doc_handle = {
        let docs = repo_handle
            .list_all()
            .await
            .expect("failed to list documents");
        if docs.is_empty() {
            let d = repo_handle.new_document();
            let tasks = Tasks { tasks: Vec::new() };
            d.with_doc_mut(|doc| {
                let mut tx = doc.transaction();
                reconcile(&mut tx, &tasks).unwrap();
                tx.commit();
            });
            d
        } else {
            let doc_id = &docs[0];
            repo_handle
                .load(doc_id.clone())
                .await
                .expect("failed to open the document")
                .unwrap()
        }
    };

    let t = Task::new(&format!("New item"));
    doc_handle.with_doc_mut(|doc| {
        let mut tasks: Tasks = hydrate(doc).unwrap();
        tasks.tasks.push(t);
        let mut tx = doc.transaction();
        reconcile(&mut tx, &tasks).unwrap();
        tx.commit();
    });

    doc_handle.with_doc(|doc| {
        let tasks: Tasks = hydrate(doc).unwrap();
        for (i, task) in tasks.tasks.iter().enumerate() {
            println!("{} {}", i + 1, task.description);
        }
    });

    repo_handle
        .stop()
        .expect("failed to cleanly close the repository");

    Ok(())
}

I hope you can make sense of this...

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions