Skip to content

Commit

Permalink
fix: don't hang when sharepolicies return immediately
Browse files Browse the repository at this point in the history
Problem: when using a SharePolicy (rather than doing some asynchronous
work) it was possible for the repo to hang. The symptom of this would be
that a repo would never respond to sync messages for a document when a
SharePolicy returned a positive response for the document.

Why was this happening? When the repo first receives a sync message for
a (document, peer) combination it checks whether the SharePolicy allows
synchronization with the peer in question. To do this the repo creates a
new future (correspoinding to SharePolicy::should_sync) and then puts it
in a list of share decisions to poll. The share decisions are then
polled on the next run through the event loop. If the repo is in a very
quiet loop then no new event occurs to trigger the first poll of the
future.

Solution: at the end of the repo event loop check if there are any share
decisions to poll. If there are, then run the loop again.
  • Loading branch information
alexjg committed Nov 28, 2024
1 parent 469a355 commit a5b19f7
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,9 @@ impl Repo {
self.remove_unused_sync_states();
self.remove_unused_pending_messages();
self.gc_docs();
if !self.pending_events.is_empty() || !self.share_decisions_to_poll.is_empty() {
continue;
}
select! {
recv(self.repo_receiver) -> repo_event => {
if let Ok(event) = repo_event {
Expand Down
52 changes: 52 additions & 0 deletions tests/network/document_create_then_change.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
extern crate test_utils;

use std::time::Duration;

use automerge::transaction::Transactable;
use automerge_repo::Repo;
use test_log::test;
use test_utils::storage_utils::{InMemoryStorage, SimpleStorage};

use super::tincans::connect_repos;

#[test(tokio::test)]
async fn test_create_document_then_change_is_synced() {
let repo2_storage = InMemoryStorage::default();
// Create two repos.
let repo_1 = Repo::new(Some("repo1".to_string()), Box::new(SimpleStorage));
let repo_2 = Repo::new(Some("repo2".to_string()), Box::new(repo2_storage.clone()));

// Run the repos in the background.
let repo_handle_1 = repo_1.run();
let repo_handle_2 = repo_2.run();
connect_repos(&repo_handle_1, &repo_handle_2);

// Spawn a task that creates a document and makes a change to id
let doc_handle = tokio::spawn({
let repo_handle_1 = repo_handle_1.clone();
async move {
let document_handle_1 = repo_handle_1.new_document();

let repo_id = repo_handle_1.get_repo_id().clone();
let document_handle_1 = document_handle_1.clone();
// Edit the document.
document_handle_1.with_doc_mut(|doc| {
let mut tx = doc.transaction();
tx.put(automerge::ROOT, "repo_id", format!("{}", repo_id))
.expect("Failed to change the document.");
tx.commit();
});
document_handle_1
}
})
.await
.unwrap();

tokio::time::sleep(Duration::from_millis(1000)).await;

// Now stop both repos
repo_handle_1.stop().unwrap();
repo_handle_2.stop().unwrap();

assert!(repo2_storage.contains_document(doc_handle.document_id()));
}
1 change: 1 addition & 0 deletions tests/network/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use test_utils::storage_utils::SimpleStorage;
mod tincans;
use tincans::{tincan_to_nowhere, tincans, TinCan, TinCans};
mod document_changed;
mod document_create_then_change;
mod document_list;
mod document_load;
mod document_request;
Expand Down

0 comments on commit a5b19f7

Please sign in to comment.