n0-computer / n0-computer/iroh-docs

Docs gets corrupted state when app terminated after doc.set_bytes() success

Open
#78 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
74
Forks
16
PR merge metrics
No merged PRs in 30d

Description

Problem:

Doc state is not fully finalized after doc.set_bytes(<..>) completes with success. Seems that data to FsStore is not written completely.

Example case:

  1. Call doc.set_bytes(<..>) on Docs with FsStore.
  2. await for completion success
  3. Terminate app immediately without waiting for clean shutdown.
  4. Start application again, and try to get the content for all Entries doc.get_many(query) and store.get_bytes(hash)
  5. Observe that the entry in Docs exists, but the data in the store not.

This is problematic because docs then try to sync with other endpoints, but noone has that data in a store.

Expectation:

  • doc.set_bytes(<..>) should complete only when everything is written.

I think this test showcases the issue, where state is not finalized after doc.set_bytes(<..>) return.

Click to expand full code block

On Linux the store file is locked for the second FsStore::load(store_path).await.unwrap(); indefinitely if the original Docs dereferenced and the test never finishes.

    #[tokio::test]
    async fn test_doc_blob() {
        let store_path = "asdf.blobs";
        {
            let secret_key = { SecretKey::generate(&mut rand::rng()) };
            let endpoint = Endpoint::empty_builder(RelayMode::Disabled)
                .secret_key(secret_key)
                .bind()
                .await
                .unwrap();
            let mdns = MdnsDiscovery::builder().build(endpoint.id()).unwrap();
            endpoint.discovery().add(mdns.clone());

            let pinger = iroh_ping::Ping::new();
            // let blobs_store = MemStore::new();
            let blobs_store = FsStore::load(store_path).await.unwrap();
            let gossip = Gossip::builder().spawn(endpoint.clone());
            let docs = Docs::memory()
                .spawn(endpoint.clone(), (*blobs_store).clone(), gossip.clone())
                .await
                .unwrap();

            let _router = Router::builder(endpoint.clone())
                .accept(iroh_ping::ALPN, pinger.clone())
                .accept(iroh_blobs::ALPN, BlobsProtocol::new(&blobs_store, None))
                .accept(iroh_gossip::ALPN, gossip.clone())
                .accept(iroh_docs::ALPN, docs.clone())
                .spawn();

            let doc = docs.create().await.unwrap();
            let _hash = doc
                .set_bytes(docs.author_default().await.unwrap(), "my_key", "my_value")
                .await
                .unwrap();
        }

        let _blobs_store = FsStore::load(store_path).await.unwrap();  // Does not have data
    }

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the provided test with Docs, FsStore::load, and doc.set_bytes, then trace when the store data is persisted and when Docs is shut down. Done means set_bytes returns only after the blob is durable, so reloading FsStore after immediate termination can retrieve the data.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.