Concurrent writes to remote replica within a transaction fail with: `Transaction timed-out`
sveltespot opened this issue · comments
sveltespot commented
Executing INSERT(s)
in multiple concurrent transactions on remote replica connections fail, with only one of them succeeding and the rest failing with error: Err(RemoteSqliteFailure(2, 0, "Transaction timed-out"))
. One of the transactions go through and the rest seem to simply hang around until the default transaction timeout (5s) and then are rightfully are timed out to prevent writer starvation.
Below is a minimal reproducer for the same:
use libsql::{Builder, Connection, Result};
#[tokio::main]
async fn main() {
let db_url = "http://localhost:8080";
let replica = Builder::new_remote_replica(
"/tmp/embedded_transaction.db",
db_url.to_string(),
String::new(),
)
.build()
.await
.unwrap();
let replica_conn_1 = replica.connect().unwrap();
let replica_conn_2 = replica.connect().unwrap();
let replica_conn_3 = replica.connect().unwrap();
setup_db(replica_conn_1.clone()).await.unwrap();
// This works as expected.
let replica_task_1 = db_work(replica_conn_1).await;
assert!(replica_task_1.is_ok());
// If we execute concurrently, two tasks
let replica_task_2 = tokio::task::spawn(async move { db_work(replica_conn_2).await });
let replica_task_3 = tokio::task::spawn(async move { db_work(replica_conn_3).await });
let (task_2_res, task_3_res) = tokio::join!(replica_task_2, replica_task_3);
let replica_task_2_res = task_2_res.unwrap();
let replica_task_3_res = task_3_res.unwrap();
if replica_task_2_res.is_err() {
eprintln!("Task 2 failed: {:?}", replica_task_2_res);
}
if replica_task_3_res.is_err() {
eprintln!("Task 3 failed: {:?}", replica_task_3_res);
}
// One of these concurrent tasks fail currently. Both tasks should succeed.
assert!(replica_task_2_res.is_ok());
assert!(replica_task_3_res.is_ok());
}
async fn db_work(conn: Connection) -> Result<()> {
let tx = conn.transaction().await?;
// Some business logic here...
tx.execute("INSERT INTO test (name) VALUES (?1)", ["somename"])
.await?;
tx.commit().await?;
Ok(())
}
async fn setup_db(conn: Connection) -> Result<()> {
conn.execute(
"CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.await?;
Ok(())
}
sveltespot commented
duplicate of #1290