From 8f6df203d7c05f6a41addf9b18c70a55100efef5 Mon Sep 17 00:00:00 2001 From: Lucio Franco Date: Thu, 19 Sep 2024 11:26:10 -0400 Subject: [PATCH 1/2] sqld: prevent embedded replica to sync schema db --- .../src/rpc/replication/replication_log.rs | 8 +++ libsql-server/tests/cluster/mod.rs | 29 +++++++++ libsql-server/tests/cluster/schema_dbs.rs | 61 +++++++++++++++++++ libsql-server/tests/embedded_replica/mod.rs | 59 ++++++++++++++++++ 4 files changed, 157 insertions(+) diff --git a/libsql-server/src/rpc/replication/replication_log.rs b/libsql-server/src/rpc/replication/replication_log.rs index 3abf06b283..1f7585f34f 100644 --- a/libsql-server/src/rpc/replication/replication_log.rs +++ b/libsql-server/src/rpc/replication/replication_log.rs @@ -351,6 +351,14 @@ impl ReplicationLog for ReplicationLogService { let (logger, config, version, _, _) = self.logger_from_namespace(namespace, &req, false).await?; + // If we are a shared schema and serving externally (aka to embedded replica's) then + // return an error. + if config.is_shared_schema && !self.service_internal { + return Err(tonic::Status::failed_precondition( + "cannot replicate a shared schema db", + )); + } + let session_hash = self.encode_session_token(version); let response = HelloResponse { diff --git a/libsql-server/tests/cluster/mod.rs b/libsql-server/tests/cluster/mod.rs index a8ae930613..4cfb20dccf 100644 --- a/libsql-server/tests/cluster/mod.rs +++ b/libsql-server/tests/cluster/mod.rs @@ -311,3 +311,32 @@ fn large_proxy_query() { sim.run().unwrap(); } + +#[test] +fn replicate_from_shared_schema() { + let mut sim = Builder::new() + .simulation_duration(Duration::from_secs(10000)) + .tcp_capacity(100000) + .build(); + make_cluster(&mut sim, 1, true); + + sim.client("client", async { + let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector) + .unwrap(); + let conn = db.connect().unwrap(); + + conn.execute("create table test (x)", ()).await.unwrap(); + + let db = Database::open_remote_with_connector("http://replica0:8080", "", TurmoilConnector) + .unwrap(); + let conn = db.connect().unwrap(); + + conn.execute_batch("select * from sqlite_master;") + .await + .unwrap(); + + Ok(()) + }); + + sim.run().unwrap(); +} diff --git a/libsql-server/tests/cluster/schema_dbs.rs b/libsql-server/tests/cluster/schema_dbs.rs index a5318a5af2..37dc0473d1 100644 --- a/libsql-server/tests/cluster/schema_dbs.rs +++ b/libsql-server/tests/cluster/schema_dbs.rs @@ -129,3 +129,64 @@ fn schema_migration_basics() { sim.run().unwrap(); } + +#[test] +fn error_on_replicate() { + let mut sim = Builder::new() + .simulation_duration(Duration::from_secs(1000)) + .build(); + make_cluster(&mut sim, 1, true); + + sim.client("client", async { + let http = Client::new(); + + assert!(http + .post( + "http://primary:9090/v1/namespaces/schema/create", + json!({ "shared_schema": true }) + ) + .await + .unwrap() + .status() + .is_success()); + assert!(http + .post( + "http://primary:9090/v1/namespaces/foo/create", + json!({ "shared_schema_name": "schema" }) + ) + .await + .unwrap() + .status() + .is_success()); + + { + let db = Database::open_remote_with_connector( + "http://schema.primary:8080", + "", + TurmoilConnector, + ) + .unwrap(); + let conn = db.connect().unwrap(); + conn.execute("create table test (x)", ()).await.unwrap(); + } + + { + let db = Database::open_remote_with_connector( + "http://schema.replica0:8080", + "", + TurmoilConnector, + ) + .unwrap(); + let conn = db.connect().unwrap(); + conn.execute("select * from sqlite_master;", ()) + .await + .unwrap(); + + conn.execute("create table foo (x)", ()).await.unwrap(); + } + + Ok(()) + }); + + sim.run().unwrap(); +} diff --git a/libsql-server/tests/embedded_replica/mod.rs b/libsql-server/tests/embedded_replica/mod.rs index 46fd659980..e7b4b9f7f0 100644 --- a/libsql-server/tests/embedded_replica/mod.rs +++ b/libsql-server/tests/embedded_replica/mod.rs @@ -1637,3 +1637,62 @@ fn replicated_synced_frames_zero_when_no_data_synced() { sim.run().unwrap(); } + +#[test] +fn schema_db() { + let tmp_embedded = tempdir().unwrap(); + let tmp_host = tempdir().unwrap(); + let tmp_embedded_path = tmp_embedded.path().to_owned(); + let tmp_host_path = tmp_host.path().to_owned(); + + let mut sim = Builder::new() + .simulation_duration(Duration::from_secs(1000)) + .build(); + + make_primary(&mut sim, tmp_host_path.clone()); + + sim.client("client", async move { + let http = Client::new(); + + assert!(http + .post( + "http://primary:9090/v1/namespaces/schema/create", + json!({ "shared_schema": true }) + ) + .await + .unwrap() + .status() + .is_success()); + assert!(http + .post( + "http://primary:9090/v1/namespaces/foo/create", + json!({ "shared_schema_name": "schema" }) + ) + .await + .unwrap() + .status() + .is_success()); + + let path = tmp_embedded_path.join("embedded"); + let db = Database::open_with_remote_sync_connector( + path.to_str().unwrap(), + "http://schema.primary:8080", + "", + TurmoilConnector, + false, + None, + ) + .await + .unwrap(); + + db.sync().await.unwrap_err(); + + let conn = db.connect().unwrap(); + + conn.execute("create table test (x)", ()).await.unwrap_err(); + + Ok(()) + }); + + sim.run().unwrap(); +} From 1f0c9df0cb746a019572cf7ff9cd203dc24fc133 Mon Sep 17 00:00:00 2001 From: Lucio Franco Date: Thu, 19 Sep 2024 13:58:18 -0400 Subject: [PATCH 2/2] sqld: improve conn.is_primary error message --- libsql-server/src/rpc/proxy.rs | 4 ++-- libsql-server/tests/cluster/schema_dbs.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libsql-server/src/rpc/proxy.rs b/libsql-server/src/rpc/proxy.rs index ff9d5bac0a..1ab5d1d19a 100644 --- a/libsql-server/src/rpc/proxy.rs +++ b/libsql-server/src/rpc/proxy.rs @@ -635,7 +635,7 @@ impl Proxy for ProxyService { Ok(conn) => { if !conn.is_primary() { return Err(tonic::Status::failed_precondition( - "cannot run schema migration against a replica", + "cannot run schema migration against a replica from a replica", )); } @@ -710,7 +710,7 @@ impl Proxy for ProxyService { Ok(conn) => { if !conn.is_primary() { return Err(tonic::Status::failed_precondition( - "cannot run schema migration against a replica", + "cannot run schema migration against a replica from a replica", )); } diff --git a/libsql-server/tests/cluster/schema_dbs.rs b/libsql-server/tests/cluster/schema_dbs.rs index 37dc0473d1..e9ab5036cc 100644 --- a/libsql-server/tests/cluster/schema_dbs.rs +++ b/libsql-server/tests/cluster/schema_dbs.rs @@ -131,7 +131,7 @@ fn schema_migration_basics() { } #[test] -fn error_on_replicate() { +fn schema_migration_via_replica() { let mut sim = Builder::new() .simulation_duration(Duration::from_secs(1000)) .build();