From f6f4c6fbf788a18d9d1235a418d597a25f722e5e Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:17:53 +0100 Subject: [PATCH 01/57] Open cloud connection with address translation, treat advertised addresses as names --- c/src/database.rs | 2 +- rust/src/common/error.rs | 6 +- rust/src/common/info.rs | 6 +- rust/src/connection/connection.rs | 136 +++++++++++++----- rust/src/connection/network/proto/database.rs | 2 +- rust/src/database/database.rs | 26 ++-- rust/src/database/database_manager.rs | 4 +- rust/src/database/session.rs | 7 +- rust/src/user/user.rs | 2 +- 9 files changed, 132 insertions(+), 59 deletions(-) diff --git a/c/src/database.rs b/c/src/database.rs index aa65b984fb..259c46a6ba 100644 --- a/c/src/database.rs +++ b/c/src/database.rs @@ -110,7 +110,7 @@ pub extern "C" fn replica_info_drop(replica_info: *mut ReplicaInfo) { /// Retrieves the address of the server hosting this replica #[no_mangle] pub extern "C" fn replica_info_get_address(replica_info: *const ReplicaInfo) -> *mut c_char { - release_string(borrow(replica_info).address.to_string()) + release_string(borrow(replica_info).server_name.to_string()) } /// Checks whether this is the primary replica of the raft cluster. diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index d141e3d23a..d1a3776260 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -22,7 +22,7 @@ use std::{error::Error as StdError, fmt}; use tonic::{Code, Status}; use typeql::error_messages; -use super::{address::Address, RequestID}; +use super::RequestID; error_messages! { ConnectionError code: "CXN", type: "Connection Error", @@ -84,8 +84,8 @@ error_messages! { InternalError 3: "Unexpected request type for remote procedure call: {request_type}.", UnexpectedResponseType { response_type: String } = 4: "Unexpected response type for remote procedure call: {response_type}.", - UnknownConnectionAddress { address: Address } = - 5: "Received unrecognized address from the server: {address}.", + UnknownConnection { name: String } = + 5: "Received unrecognized node ID from the server: {name}.", EnumOutOfBounds { value: i32, enum_name: &'static str } = 6: "Value '{value}' is out of bounds for enum '{enum_name}'.", } diff --git a/rust/src/common/info.rs b/rust/src/common/info.rs index 2ce6b74f33..5fffad5b1a 100644 --- a/rust/src/common/info.rs +++ b/rust/src/common/info.rs @@ -21,12 +21,12 @@ use std::time::Duration; use tokio::sync::mpsc::UnboundedSender; -use super::{address::Address, SessionID}; +use super::SessionID; use crate::common::Callback; #[derive(Clone, Debug)] pub(crate) struct SessionInfo { - pub(crate) address: Address, + pub(crate) server_name: String, pub(crate) session_id: SessionID, pub(crate) network_latency: Duration, pub(crate) on_close_register_sink: UnboundedSender, @@ -42,7 +42,7 @@ pub(crate) struct DatabaseInfo { #[derive(Debug)] pub struct ReplicaInfo { /// The address of the server hosting this replica - pub address: Address, + pub server_name: String, /// Whether this is the primary replica of the raft cluster. pub is_primary: bool, /// Whether this is the preferred replica of the raft cluster. diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 29f636d1db..1ed9f133e8 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -57,7 +57,8 @@ use crate::{ /// A connection to a TypeDB server which serves as the starting point for all interaction. #[derive(Clone)] pub struct Connection { - server_connections: HashMap, + server_addresses: HashMap, + server_connections: HashMap, background_runtime: Arc, username: Option, is_cloud: bool, @@ -76,18 +77,23 @@ impl Connection { /// Connection::new_core("127.0.0.1:1729") /// ``` pub fn new_core(address: impl AsRef) -> Result { + let name = address.as_ref().to_owned(); let address: Address = address.as_ref().parse()?; let background_runtime = Arc::new(BackgroundRuntime::new()?); - let mut server_connection = ServerConnection::new_core(background_runtime.clone(), address)?; - let address = server_connection + let mut server_connection = ServerConnection::new_core(background_runtime.clone(), name, address.clone())?; + + let advertised_name = server_connection .servers_all()? .into_iter() .exactly_one() - .map_err(|e| ConnectionError::ServerConnectionFailedStatusError { error: e.to_string() })?; - server_connection.set_address(address.clone()); + .map_err(|e| ConnectionError::ServerConnectionFailedStatusError { error: e.to_string() })? + .to_string(); + server_connection.set_name(advertised_name.clone()); + match server_connection.validate() { Ok(()) => Ok(Self { - server_connections: [(address, server_connection)].into(), + server_connections: [(advertised_name.clone(), server_connection)].into(), + server_addresses: [(advertised_name, address)].into(), background_runtime, username: None, is_cloud: false, @@ -124,11 +130,68 @@ impl Connection { let init_addresses = init_addresses.iter().map(|addr| addr.as_ref().parse()).try_collect()?; let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; - let server_connections: HashMap = addresses + Self::new_cloud_impl( + background_runtime, + addresses.into_iter().map(|addr| (addr.to_string(), addr)).collect(), + credential, + ) + } + + /// Creates a new TypeDB Cloud connection. + /// + /// # Arguments + /// + /// * `addresses` -- Translation map from addresses received from the TypeDB server(s) to + /// addresses to be used by the driver for connection + /// * `credential` -- User credential and TLS encryption setting + /// + /// # Examples + /// + /// ```rust + /// Connection::new_cloud( + /// &["localhost:11729", "localhost:21729", "localhost:31729"], + /// Credential::with_tls( + /// "admin", + /// "password", + /// Some(&PathBuf::from( + /// std::env::var("ROOT_CA") + /// .expect("ROOT_CA environment variable needs to be set for cloud tests to run"), + /// )), + /// )?, + /// ) + /// ``` + pub fn new_cloud_address_map + Sync, U: AsRef + Sync>( + addresses: HashMap, + credential: Credential, + ) -> Result { + let background_runtime = Arc::new(BackgroundRuntime::new()?); + + let server_addresses: HashMap<_, _> = addresses .into_iter() - .map(|address| { - ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()) - .map(|server_connection| (address, server_connection)) + .map(|(name, address)| { + let name = name.as_ref().to_owned(); + address.as_ref().parse::
().map(|address| (name, address)) + }) + .try_collect()?; + + Self::new_cloud_impl(background_runtime, server_addresses, credential) + } + + fn new_cloud_impl( + background_runtime: Arc, + server_addresses: HashMap, + credential: Credential, + ) -> Result { + let server_connections: HashMap = server_addresses + .iter() + .map(|(name, address)| { + ServerConnection::new_cloud( + background_runtime.clone(), + name.clone(), + address.clone(), + credential.clone(), + ) + .map(|server_connection| (name.clone(), server_connection)) }) .try_collect()?; @@ -140,6 +203,7 @@ impl Connection { })? } else { Ok(Self { + server_addresses, server_connections, background_runtime, username: Some(credential.username().to_string()), @@ -154,8 +218,12 @@ impl Connection { credential: Credential, ) -> Result> { for address in &addresses { - let server_connection = - ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()); + let server_connection = ServerConnection::new_cloud( + background_runtime.clone(), + address.to_string(), + address.clone(), + credential.clone(), + ); match server_connection { Ok(server_connection) => match server_connection.servers_all() { Ok(servers) => return Ok(servers.into_iter().collect()), @@ -215,14 +283,14 @@ impl Connection { self.server_connections.len() } - pub(crate) fn addresses(&self) -> impl Iterator { - self.server_connections.keys() + pub(crate) fn server_names(&self) -> impl Iterator { + self.server_connections.keys().map(|name| name.as_str()) } - pub(crate) fn connection(&self, address: &Address) -> Result<&ServerConnection> { + pub(crate) fn connection(&self, address: &str) -> Result<&ServerConnection> { self.server_connections .get(address) - .ok_or_else(|| InternalError::UnknownConnectionAddress { address: address.clone() }.into()) + .ok_or_else(|| InternalError::UnknownConnection { name: address.to_owned() }.into()) } pub(crate) fn connections(&self) -> impl Iterator + '_ { @@ -234,9 +302,7 @@ impl Connection { } pub(crate) fn unable_to_connect_error(&self) -> Error { - Error::Connection(ConnectionError::ServerConnectionFailedStatusError { - error: self.addresses().map(Address::to_string).collect::>().join(", "), - }) + Error::Connection(ConnectionError::ServerConnectionFailedStatusError { error: self.server_names().join(", ") }) } } @@ -248,22 +314,26 @@ impl fmt::Debug for Connection { #[derive(Clone)] pub(crate) struct ServerConnection { - address: Address, + name: String, background_runtime: Arc, open_sessions: Arc>>>, request_transmitter: Arc, } impl ServerConnection { - fn new_core(background_runtime: Arc, address: Address) -> Result { - let request_transmitter = Arc::new(RPCTransmitter::start_core(address.clone(), &background_runtime)?); - Ok(Self { address, background_runtime, open_sessions: Default::default(), request_transmitter }) + fn new_core(background_runtime: Arc, name: String, address: Address) -> Result { + let request_transmitter = Arc::new(RPCTransmitter::start_core(address, &background_runtime)?); + Ok(Self { name, background_runtime, open_sessions: Default::default(), request_transmitter }) } - fn new_cloud(background_runtime: Arc, address: Address, credential: Credential) -> Result { - let request_transmitter = - Arc::new(RPCTransmitter::start_cloud(address.clone(), credential, &background_runtime)?); - Ok(Self { address, background_runtime, open_sessions: Default::default(), request_transmitter }) + fn new_cloud( + background_runtime: Arc, + name: String, + address: Address, + credential: Credential, + ) -> Result { + let request_transmitter = Arc::new(RPCTransmitter::start_cloud(address, credential, &background_runtime)?); + Ok(Self { name, background_runtime, open_sessions: Default::default(), request_transmitter }) } pub(crate) fn validate(&self) -> Result { @@ -273,12 +343,12 @@ impl ServerConnection { } } - fn set_address(&mut self, address: Address) { - self.address = address; + fn set_name(&mut self, name: String) { + self.name = name; } - pub(crate) fn address(&self) -> &Address { - &self.address + pub(crate) fn name(&self) -> &str { + &self.name } #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] @@ -392,7 +462,7 @@ impl ServerConnection { pulse_shutdown_source, )); Ok(SessionInfo { - address: self.address.clone(), + server_name: self.name.clone(), session_id, network_latency: start.elapsed().saturating_sub(server_duration), on_close_register_sink, @@ -507,7 +577,7 @@ impl ServerConnection { impl fmt::Debug for ServerConnection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ServerConnection") - .field("address", &self.address) + .field("name", &self.name) .field("open_sessions", &self.open_sessions) .finish() } diff --git a/rust/src/connection/network/proto/database.rs b/rust/src/connection/network/proto/database.rs index 471d116619..c2b3310109 100644 --- a/rust/src/connection/network/proto/database.rs +++ b/rust/src/connection/network/proto/database.rs @@ -38,7 +38,7 @@ impl TryFromProto for DatabaseInfo { impl TryFromProto for ReplicaInfo { fn try_from_proto(proto: ReplicaProto) -> Result { Ok(Self { - address: proto.address.parse()?, + server_name: proto.address, is_primary: proto.primary, is_preferred: proto.preferred, term: proto.term, diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index a8d8e8a178..075d3f26df 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -26,7 +26,6 @@ use log::{debug, error}; use crate::{ common::{ - address::Address, error::ConnectionError, info::{DatabaseInfo, ReplicaInfo}, Error, Result, @@ -183,11 +182,11 @@ impl Database { { let replicas = self.replicas.read().unwrap().clone(); for replica in replicas { - match task(replica.database.clone(), self.connection.connection(&replica.address)?.clone()).await { + match task(replica.database.clone(), self.connection.connection(&replica.server_name)?.clone()).await { Err(Error::Connection( ConnectionError::ServerConnectionFailedStatusError { .. } | ConnectionError::ConnectionFailed, )) => { - debug!("Unable to connect to {}. Attempting next server.", replica.address); + debug!("Unable to connect to {}. Attempting next server.", replica.server_name); } res => return res, } @@ -205,8 +204,11 @@ impl Database { if let Some(replica) = self.primary_replica() { replica } else { self.seek_primary_replica().await? }; for _ in 0..Self::PRIMARY_REPLICA_TASK_MAX_RETRIES { - match task(primary_replica.database.clone(), self.connection.connection(&primary_replica.address)?.clone()) - .await + match task( + primary_replica.database.clone(), + self.connection.connection(&primary_replica.server_name)?.clone(), + ) + .await { Err(Error::Connection( ConnectionError::CloudReplicaNotPrimary @@ -260,8 +262,8 @@ impl fmt::Debug for Database { /// The metadata and state of an individual raft replica of a database. #[derive(Clone)] pub(super) struct Replica { - /// Retrieves address of the server hosting this replica - address: Address, + /// Retrieves the name of the server hosting this replica + server_name: String, /// Retrieves the database name for which this is a replica database_name: String, /// Checks whether this is the primary replica of the raft cluster. @@ -277,7 +279,7 @@ pub(super) struct Replica { impl Replica { fn new(name: String, metadata: ReplicaInfo, server_connection: ServerConnection) -> Self { Self { - address: metadata.address, + server_name: metadata.server_name, database_name: name.clone(), is_primary: metadata.is_primary, term: metadata.term, @@ -291,7 +293,7 @@ impl Replica { .replicas .into_iter() .map(|replica| { - let server_connection = connection.connection(&replica.address)?.clone(); + let server_connection = connection.connection(&replica.server_name)?.clone(); Ok(Self::new(database_info.name.clone(), replica, server_connection)) }) .try_collect() @@ -299,7 +301,7 @@ impl Replica { fn to_info(&self) -> ReplicaInfo { ReplicaInfo { - address: self.address.clone(), + server_name: self.server_name.clone(), is_primary: self.is_primary, is_preferred: self.is_preferred, term: self.term, @@ -322,7 +324,7 @@ impl Replica { error!( "Failed to fetch replica info for database '{}' from {}. Attempting next server.", name, - server_connection.address() + server_connection.name() ); } Err(err) => return Err(err), @@ -335,7 +337,7 @@ impl Replica { impl fmt::Debug for Replica { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Replica") - .field("address", &self.address) + .field("address", &self.server_name) .field("database_name", &self.database_name) .field("is_primary", &self.is_primary) .field("term", &self.term) diff --git a/rust/src/database/database_manager.rs b/rust/src/database/database_manager.rs index c6676f6abe..55191e36b5 100644 --- a/rust/src/database/database_manager.rs +++ b/rust/src/database/database_manager.rs @@ -114,7 +114,7 @@ impl DatabaseManager { Ok(list) => { return list.into_iter().map(|db_info| Database::new(db_info, self.connection.clone())).collect() } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.name(), err)), } } Err(ConnectionError::ServerConnectionFailedWithError { error: error_buffer.join("\n") })? @@ -140,7 +140,7 @@ impl DatabaseManager { .await } err @ Err(Error::Connection(ConnectionError::ConnectionIsClosed)) => return err, - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.name(), err)), } } Err(ConnectionError::ServerConnectionFailedWithError { error: error_buffer.join("\n") })? diff --git a/rust/src/database/session.rs b/rust/src/database/session.rs index 4d772f1e40..ed6722467e 100644 --- a/rust/src/database/session.rs +++ b/rust/src/database/session.rs @@ -143,7 +143,7 @@ impl Session { pub fn force_close(&self) -> Result { if self.is_open.compare_exchange(true, false).is_ok() { let session_info = self.server_session_info.write().unwrap(); - let connection = self.database.connection().connection(&session_info.address).unwrap(); + let connection = self.database.connection().connection(&session_info.server_name).unwrap(); connection.close_session(session_info.session_id.clone())?; } Ok(()) @@ -222,8 +222,9 @@ impl Session { return Err(ConnectionError::SessionIsClosed.into()); } - let SessionInfo { address, session_id, network_latency, .. } = self.server_session_info.read().unwrap().clone(); - let server_connection = &self.database.connection().connection(&address)?; + let SessionInfo { server_name, session_id, network_latency, .. } = + self.server_session_info.read().unwrap().clone(); + let server_connection = &self.database.connection().connection(&server_name)?; let (transaction_stream, transaction_shutdown_sink) = match server_connection .open_transaction(session_id.clone(), transaction_type, options, network_latency) diff --git a/rust/src/user/user.rs b/rust/src/user/user.rs index a3f9582dbd..11f99dc4d7 100644 --- a/rust/src/user/user.rs +++ b/rust/src/user/user.rs @@ -58,7 +58,7 @@ impl User { .await { Ok(()) => return Ok(()), - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.name(), err)), } } Err(ConnectionError::CloudAllNodesFailed { errors: error_buffer.join("\n") })? From 8cefac4abe3cb31f8172cb48871e69b7ef193ed5 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:24:26 +0100 Subject: [PATCH 02/57] Cloud address translation basic test --- rust/tests/integration/mod.rs | 1 + rust/tests/integration/network.rs | 45 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 rust/tests/integration/network.rs diff --git a/rust/tests/integration/mod.rs b/rust/tests/integration/mod.rs index 31cac660b8..fd618d7845 100644 --- a/rust/tests/integration/mod.rs +++ b/rust/tests/integration/mod.rs @@ -19,5 +19,6 @@ mod common; mod logic; +mod network; mod queries; mod runtimes; diff --git a/rust/tests/integration/network.rs b/rust/tests/integration/network.rs new file mode 100644 index 0000000000..75e018c21c --- /dev/null +++ b/rust/tests/integration/network.rs @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +use std::path::PathBuf; + +use serial_test::serial; +use typedb_driver::{Connection, Credential}; + +#[test] +#[serial] +fn address_translation() { + Connection::new_cloud_address_map( + [ + ("localhost:11729", "localhost:11729"), + ("localhost:21729", "localhost:21729"), + ("localhost:31729", "localhost:31729"), + ] + .into(), + Credential::with_tls( + "admin", + "password", + Some(&PathBuf::from( + std::env::var("ROOT_CA").expect("ROOT_CA environment variable needs to be set for cloud tests to run"), + )), + ) + .unwrap(), + ) + .unwrap(); +} From bb98855ba6c314633416048bad54d1c87427ddf0 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:24:39 +0100 Subject: [PATCH 03/57] Cloud address translation FFI --- c/src/connection.rs | 21 ++++++++++++++++++++- c/typedb_driver.i | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/c/src/connection.rs b/c/src/connection.rs index 6ccdfcfcef..33be4ce662 100644 --- a/c/src/connection.rs +++ b/c/src/connection.rs @@ -17,7 +17,7 @@ * under the License. */ -use std::{ffi::c_char, path::Path}; +use std::{collections::HashMap, ffi::c_char, path::Path}; use typedb_driver::{Connection, Credential}; @@ -48,6 +48,25 @@ pub extern "C" fn connection_open_cloud( try_release(Connection::new_cloud(&addresses, borrow(credential).clone())) } +/// Open a TypeDB Driver to TypeDB Cloud server(s) available at the provided addresses, using +/// the provided credential. +/// +/// @param advertised_addresses a null-terminated array holding the address(es) the TypeDB server(s) +/// are configured to advertise +/// @param translated_addresses a null-terminated array holding the address(es) of the TypeDB server(s) +/// the driver will connect to +/// @param credential The Credential to connect with +#[no_mangle] +pub extern "C" fn connection_open_cloud_translated( + advertised_addresses: *const *const c_char, + translated_addresses: *const *const c_char, + credential: *const Credential, +) -> *mut Connection { + let addresses: HashMap<&str, &str> = + string_array_view(advertised_addresses).zip(string_array_view(translated_addresses)).collect(); + try_release(Connection::new_cloud_address_map(addresses, borrow(credential).clone())) +} + /// Closes the driver. Before instantiating a new driver, the driver that’s currently open should first be closed. /// Closing a connction frees the underlying rust object. #[no_mangle] diff --git a/c/typedb_driver.i b/c/typedb_driver.i index cf4b8ebfdb..79223eb7c1 100644 --- a/c/typedb_driver.i +++ b/c/typedb_driver.i @@ -343,6 +343,7 @@ void transaction_on_close_register(const Transaction* transaction, TransactionCa %newobject connection_open_core; %newobject connection_open_cloud; +%newobject connection_open_cloud_translated; %newobject credential_new; From ce3e4861a4232627be84224c30e3bba21abd1b35 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:41:19 +0100 Subject: [PATCH 04/57] Cloud address translation in Java --- java/connection/TypeDBDriverImpl.java | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/java/connection/TypeDBDriverImpl.java b/java/connection/TypeDBDriverImpl.java index 0defaa58d1..682ad62295 100644 --- a/java/connection/TypeDBDriverImpl.java +++ b/java/connection/TypeDBDriverImpl.java @@ -30,11 +30,15 @@ import com.vaticle.typedb.driver.common.exception.TypeDBDriverException; import com.vaticle.typedb.driver.user.UserManagerImpl; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Set; import static com.vaticle.typedb.driver.jni.typedb_driver.connection_force_close; import static com.vaticle.typedb.driver.jni.typedb_driver.connection_is_open; import static com.vaticle.typedb.driver.jni.typedb_driver.connection_open_cloud; +import static com.vaticle.typedb.driver.jni.typedb_driver.connection_open_cloud_translated; import static com.vaticle.typedb.driver.jni.typedb_driver.connection_open_core; public class TypeDBDriverImpl extends NativeObject implements TypeDBDriver { @@ -49,6 +53,10 @@ public TypeDBDriverImpl(Set initAddresses, TypeDBCredential credential) this(openCloud(initAddresses, credential)); } + public TypeDBDriverImpl(Map addresses, TypeDBCredential credential) throws TypeDBDriverException { + this(openCloud(addresses, credential)); + } + private TypeDBDriverImpl(com.vaticle.typedb.driver.jni.Connection connection) { super(connection); databaseMgr = new TypeDBDatabaseManagerImpl(this.nativeObject); @@ -71,6 +79,24 @@ private static com.vaticle.typedb.driver.jni.Connection openCloud(Set in } } + private static com.vaticle.typedb.driver.jni.Connection openCloud(Map addresses, TypeDBCredential credential) { + try { + List advertised = new ArrayList(); + List translated = new ArrayList(); + for (Map.Entry entry: addresses.entrySet()) { + advertised.add(entry.getKey()); + translated.add(entry.getValue()); + } + return connection_open_cloud_translated( + advertised.toArray(new String[0]), + translated.toArray(new String[0]), + credential.nativeObject + ); + } catch (com.vaticle.typedb.driver.jni.Error e) { + throw new TypeDBDriverException(e); + } + } + @Override public boolean isOpen() { return connection_is_open(nativeObject); From d43c77501d6e1cd6f3190f1d99ad3cb3f16ad6e5 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:56:42 +0100 Subject: [PATCH 05/57] disambiguate arraySize in cscode --- c/swig/typedb_driver_csharp.swg | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/c/swig/typedb_driver_csharp.swg b/c/swig/typedb_driver_csharp.swg index 9fc124eed8..5957f1b90c 100644 --- a/c/swig/typedb_driver_csharp.swg +++ b/c/swig/typedb_driver_csharp.swg @@ -620,27 +620,27 @@ %typemap( csin, pre=" - int arraySize = $csinput.Length; + int arraySize$csinput = $csinput.Length; global::System.IntPtr unmanaged$csinput = System.Runtime.InteropServices.Marshal.AllocHGlobal( - (arraySize + 1) * System.Runtime.InteropServices.Marshal.SizeOf()); + (arraySize$csinput + 1) * System.Runtime.InteropServices.Marshal.SizeOf()); unsafe { global::System.IntPtr* arrayPtr = (global::System.IntPtr*)unmanaged$csinput.ToPointer(); - for (int i = 0; i < arraySize; i++) + for (int i = 0; i < arraySize$csinput; i++) { arrayPtr[i] = global::System.Runtime.InteropServices.Marshal.StringToCoTaskMemAnsi($csinput[i]); } - arrayPtr[arraySize] = global::System.IntPtr.Zero; + arrayPtr[arraySize$csinput] = global::System.IntPtr.Zero; }", post=" unsafe { global::System.IntPtr* arrayPtr = (global::System.IntPtr*)unmanaged$csinput.ToPointer(); - for (int i = 0; i < arraySize; i++) + for (int i = 0; i < arraySize$csinput; i++) { global::System.Runtime.InteropServices.Marshal.FreeHGlobal(arrayPtr[i]); } From e55be7d683fa3226391b2d5742b4f6ca91d40453 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:02:48 +0100 Subject: [PATCH 06/57] Cloud address translaton in Java cont'd --- java/TypeDB.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/java/TypeDB.java b/java/TypeDB.java index f391e92576..364a51545f 100644 --- a/java/TypeDB.java +++ b/java/TypeDB.java @@ -23,6 +23,7 @@ import com.vaticle.typedb.driver.api.TypeDBCredential; import com.vaticle.typedb.driver.connection.TypeDBDriverImpl; +import java.util.Map; import java.util.Set; import static com.vaticle.typedb.common.collection.Collections.set; @@ -75,4 +76,21 @@ public static TypeDBDriver cloudDriver(String address, TypeDBCredential credenti public static TypeDBDriver cloudDriver(Set addresses, TypeDBCredential credential) { return new TypeDBDriverImpl(addresses, credential); } + + /** + * Open a TypeDB Driver to TypeDB Cloud server(s) available at the provided addresses, using + * the provided credential. + * + *

Examples

+ *
+     * TypeDB.cloudDriver(addresses, credential);
+     * 
+ * + * @param addresses Translation map from addresses received from the TypeDB server(s) + * to addresses to be used by the driver for connection + * @param credential The credential to connect with + */ + public static TypeDBDriver cloudDriver(Map addressesTranslation, TypeDBCredential credential) { + return new TypeDBDriverImpl(addressesTranslation, credential); + } } From f58d529c7361c9c1a81eb838b33e4f929b93ef55 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:47:41 +0100 Subject: [PATCH 07/57] test rust network integration tests --- .factory/automation.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.factory/automation.yml b/.factory/automation.yml index b455725cfe..b7d7b4490b 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -95,7 +95,8 @@ build: source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars bazel test //rust/tests --test_output=streamed --test_env=ROOT_CA=$ROOT_CA --test_arg=-- \ --test_arg=integration::queries::cloud \ - --test_arg=integration::runtimes && + --test_arg=integration::runtimes \ + --test_arg=integration::network && export CLOUD_FAILED= || export CLOUD_FAILED=1 tool/test/stop-cloud-servers.sh if [[ -n "$CLOUD_FAILED" ]]; then exit 1; fi From 7895caebe3f559ece9c8602b10f6177598185849 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:26:38 +0100 Subject: [PATCH 08/57] Don't fail on unknown connection address --- rust/src/common/error.rs | 4 +- rust/src/connection/connection.rs | 24 ++++-------- rust/src/database/database.rs | 56 ++++++++++++++------------- rust/src/database/database_manager.rs | 9 +++-- rust/src/database/session.rs | 6 +-- rust/src/user/user_manager.rs | 2 +- rust/tests/integration/network.rs | 7 +--- 7 files changed, 48 insertions(+), 60 deletions(-) diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index d1a3776260..1e8926ac91 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -84,10 +84,8 @@ error_messages! { InternalError 3: "Unexpected request type for remote procedure call: {request_type}.", UnexpectedResponseType { response_type: String } = 4: "Unexpected response type for remote procedure call: {response_type}.", - UnknownConnection { name: String } = - 5: "Received unrecognized node ID from the server: {name}.", EnumOutOfBounds { value: i32, enum_name: &'static str } = - 6: "Value '{value}' is out of bounds for enum '{enum_name}'.", + 5: "Value '{value}' is out of bounds for enum '{enum_name}'.", } /// Represents errors encountered during operation. diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 1ed9f133e8..9f84cb51d9 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -57,7 +57,6 @@ use crate::{ /// A connection to a TypeDB server which serves as the starting point for all interaction. #[derive(Clone)] pub struct Connection { - server_addresses: HashMap, server_connections: HashMap, background_runtime: Arc, username: Option, @@ -80,7 +79,7 @@ impl Connection { let name = address.as_ref().to_owned(); let address: Address = address.as_ref().parse()?; let background_runtime = Arc::new(BackgroundRuntime::new()?); - let mut server_connection = ServerConnection::new_core(background_runtime.clone(), name, address.clone())?; + let mut server_connection = ServerConnection::new_core(background_runtime.clone(), name, address)?; let advertised_name = server_connection .servers_all()? @@ -92,8 +91,7 @@ impl Connection { match server_connection.validate() { Ok(()) => Ok(Self { - server_connections: [(advertised_name.clone(), server_connection)].into(), - server_addresses: [(advertised_name, address)].into(), + server_connections: [(advertised_name, server_connection)].into(), background_runtime, username: None, is_cloud: false, @@ -183,15 +181,10 @@ impl Connection { credential: Credential, ) -> Result { let server_connections: HashMap = server_addresses - .iter() + .into_iter() .map(|(name, address)| { - ServerConnection::new_cloud( - background_runtime.clone(), - name.clone(), - address.clone(), - credential.clone(), - ) - .map(|server_connection| (name.clone(), server_connection)) + ServerConnection::new_cloud(background_runtime.clone(), name.clone(), address, credential.clone()) + .map(|server_connection| (name, server_connection)) }) .try_collect()?; @@ -203,7 +196,6 @@ impl Connection { })? } else { Ok(Self { - server_addresses, server_connections, background_runtime, username: Some(credential.username().to_string()), @@ -287,10 +279,8 @@ impl Connection { self.server_connections.keys().map(|name| name.as_str()) } - pub(crate) fn connection(&self, address: &str) -> Result<&ServerConnection> { - self.server_connections - .get(address) - .ok_or_else(|| InternalError::UnknownConnection { name: address.to_owned() }.into()) + pub(crate) fn connection(&self, address: &str) -> Option<&ServerConnection> { + self.server_connections.get(address) } pub(crate) fn connections(&self) -> impl Iterator + '_ { diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index 075d3f26df..42eab0b971 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -21,7 +21,6 @@ use std::future::Future; use std::{fmt, sync::RwLock, thread::sleep, time::Duration}; -use itertools::Itertools; use log::{debug, error}; use crate::{ @@ -46,10 +45,10 @@ impl Database { const FETCH_REPLICAS_MAX_RETRIES: usize = 10; const WAIT_FOR_PRIMARY_REPLICA_SELECTION: Duration = Duration::from_secs(2); - pub(super) fn new(database_info: DatabaseInfo, connection: Connection) -> Result { + pub(super) fn new(database_info: DatabaseInfo, connection: Connection) -> Self { let name = database_info.name.clone(); - let replicas = RwLock::new(Replica::try_from_info(database_info, &connection)?); - Ok(Self { name, replicas, connection }) + let replicas = RwLock::new(Replica::try_from_info(database_info, &connection)); + Self { name, replicas, connection } } #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] @@ -117,7 +116,7 @@ impl Database { /// ``` #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub async fn delete(self) -> Result { - self.run_on_primary_replica(|database, _| database.delete()).await + self.run_on_primary_replica(|database| database.delete()).await } /// Returns a full schema text as a valid TypeQL define query string. @@ -130,7 +129,7 @@ impl Database { /// ``` #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub async fn schema(&self) -> Result { - self.run_failsafe(|database, _| async move { database.schema().await }).await + self.run_failsafe(|database| async move { database.schema().await }).await } /// Returns the types in the schema as a valid TypeQL define query string. @@ -143,7 +142,7 @@ impl Database { /// ``` #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub async fn type_schema(&self) -> Result { - self.run_failsafe(|database, _| async move { database.type_schema().await }).await + self.run_failsafe(|database| async move { database.type_schema().await }).await } /// Returns the rules in the schema as a valid TypeQL define query string. @@ -156,13 +155,13 @@ impl Database { /// ``` #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub async fn rule_schema(&self) -> Result { - self.run_failsafe(|database, _| async move { database.rule_schema().await }).await + self.run_failsafe(|database| async move { database.rule_schema().await }).await } #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub(crate) async fn run_failsafe(&self, task: F) -> Result where - F: Fn(ServerDatabase, ServerConnection) -> P, + F: Fn(ServerDatabase) -> P, P: Future>, { match self.run_on_any_replica(&task).await { @@ -177,12 +176,16 @@ impl Database { #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub(super) async fn run_on_any_replica(&self, task: F) -> Result where - F: Fn(ServerDatabase, ServerConnection) -> P, + F: Fn(ServerDatabase) -> P, P: Future>, { let replicas = self.replicas.read().unwrap().clone(); for replica in replicas { - match task(replica.database.clone(), self.connection.connection(&replica.server_name)?.clone()).await { + let Some(database) = replica.database.clone() else { + debug!("Skipping replica @ {} (address translation not found)", replica.server_name); + continue; + }; + match task(database).await { Err(Error::Connection( ConnectionError::ServerConnectionFailedStatusError { .. } | ConnectionError::ConnectionFailed, )) => { @@ -197,19 +200,18 @@ impl Database { #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub(super) async fn run_on_primary_replica(&self, task: F) -> Result where - F: Fn(ServerDatabase, ServerConnection) -> P, + F: Fn(ServerDatabase) -> P, P: Future>, { let mut primary_replica = if let Some(replica) = self.primary_replica() { replica } else { self.seek_primary_replica().await? }; for _ in 0..Self::PRIMARY_REPLICA_TASK_MAX_RETRIES { - match task( - primary_replica.database.clone(), - self.connection.connection(&primary_replica.server_name)?.clone(), - ) - .await - { + let Some(database) = primary_replica.database.clone() else { + error!("Address translation not found for primary replica @ {} ", primary_replica.server_name); + return Err(self.connection.unable_to_connect_error()); + }; + match task(database).await { Err(Error::Connection( ConnectionError::CloudReplicaNotPrimary | ConnectionError::ServerConnectionFailedStatusError { .. } @@ -273,30 +275,30 @@ pub(super) struct Replica { /// Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. is_preferred: bool, /// Retrieves the database for which this is a replica - database: ServerDatabase, + database: Option, } impl Replica { - fn new(name: String, metadata: ReplicaInfo, server_connection: ServerConnection) -> Self { + fn new(name: String, metadata: ReplicaInfo, server_connection: Option) -> Self { Self { server_name: metadata.server_name, database_name: name.clone(), is_primary: metadata.is_primary, term: metadata.term, is_preferred: metadata.is_preferred, - database: ServerDatabase::new(name, server_connection), + database: server_connection.map(|server_connection| ServerDatabase::new(name, server_connection)), } } - fn try_from_info(database_info: DatabaseInfo, connection: &Connection) -> Result> { + fn try_from_info(database_info: DatabaseInfo, connection: &Connection) -> Vec { database_info .replicas .into_iter() .map(|replica| { - let server_connection = connection.connection(&replica.server_name)?.clone(); - Ok(Self::new(database_info.name.clone(), replica, server_connection)) + let server_connection = connection.connection(&replica.server_name).cloned(); + Self::new(database_info.name.clone(), replica, server_connection) }) - .try_collect() + .collect() } fn to_info(&self) -> ReplicaInfo { @@ -314,7 +316,7 @@ impl Replica { let res = server_connection.get_database_replicas(name.clone()).await; match res { Ok(info) => { - return Self::try_from_info(info, &connection); + return Ok(Self::try_from_info(info, &connection)); } Err(Error::Connection( ConnectionError::DatabaseDoesNotExist { .. } @@ -361,7 +363,7 @@ impl ServerDatabase { self.name.as_str() } - pub(super) fn connection(&self) -> &ServerConnection { + pub(crate) fn connection(&self) -> &ServerConnection { &self.connection } diff --git a/rust/src/database/database_manager.rs b/rust/src/database/database_manager.rs index 55191e36b5..e78b720c8d 100644 --- a/rust/src/database/database_manager.rs +++ b/rust/src/database/database_manager.rs @@ -112,7 +112,10 @@ impl DatabaseManager { for server_connection in self.connection.connections() { match server_connection.all_databases().await { Ok(list) => { - return list.into_iter().map(|db_info| Database::new(db_info, self.connection.clone())).collect() + return Ok(list + .into_iter() + .map(|db_info| Database::new(db_info, self.connection.clone())) + .collect()) } Err(err) => error_buffer.push(format!("- {}: {}", server_connection.name(), err)), } @@ -133,9 +136,9 @@ impl DatabaseManager { Err(Error::Connection(ConnectionError::CloudReplicaNotPrimary)) => { return Database::get(name, self.connection.clone()) .await? - .run_on_primary_replica(|database, server_connection| { + .run_on_primary_replica(|database| { let task = &task; - async move { task(server_connection, database.name().to_owned()).await } + async move { task(database.connection().clone(), database.name().to_owned()).await } }) .await } diff --git a/rust/src/database/session.rs b/rust/src/database/session.rs index ed6722467e..c03c3f9b52 100644 --- a/rust/src/database/session.rs +++ b/rust/src/database/session.rs @@ -84,7 +84,7 @@ impl Session { #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub async fn new_with_options(database: Database, session_type: SessionType, options: Options) -> Result { let server_session_info = database - .run_failsafe(|database, _| async move { + .run_failsafe(|database| async move { database.connection().open_session(database.name().to_owned(), session_type, options).await }) .await?; @@ -224,7 +224,7 @@ impl Session { let SessionInfo { server_name, session_id, network_latency, .. } = self.server_session_info.read().unwrap().clone(); - let server_connection = &self.database.connection().connection(&server_name)?; + let server_connection = &self.database.connection().connection(&server_name).unwrap(); let (transaction_stream, transaction_shutdown_sink) = match server_connection .open_transaction(session_id.clone(), transaction_type, options, network_latency) @@ -237,7 +237,7 @@ impl Session { let (session_info, (transaction_stream, transaction_shutdown_sink)) = self .database - .run_failsafe(|database, _| { + .run_failsafe(|database| { let session_type = self.session_type; async move { let connection = database.connection(); diff --git a/rust/src/user/user_manager.rs b/rust/src/user/user_manager.rs index fe48b9ca52..229013b618 100644 --- a/rust/src/user/user_manager.rs +++ b/rust/src/user/user_manager.rs @@ -190,7 +190,7 @@ impl UserManager { DatabaseManager::new(self.connection.clone()) .get(Self::SYSTEM_DB) .await? - .run_failsafe(|_, server_connection| task(server_connection)) + .run_failsafe(|database| task(database.connection().clone())) .await } } diff --git a/rust/tests/integration/network.rs b/rust/tests/integration/network.rs index 75e018c21c..d470129bc3 100644 --- a/rust/tests/integration/network.rs +++ b/rust/tests/integration/network.rs @@ -26,12 +26,7 @@ use typedb_driver::{Connection, Credential}; #[serial] fn address_translation() { Connection::new_cloud_address_map( - [ - ("localhost:11729", "localhost:11729"), - ("localhost:21729", "localhost:21729"), - ("localhost:31729", "localhost:31729"), - ] - .into(), + [("localhost:21729", "localhost:21729")].into(), Credential::with_tls( "admin", "password", From fac499eb8a146142375873831cff0126072942a6 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:34:54 +0100 Subject: [PATCH 09/57] Java address mapping test --- java/test/integration/AddressMappingTest.java | 76 +++++++++++++++++++ java/test/integration/BUILD | 23 ++++++ 2 files changed, 99 insertions(+) create mode 100644 java/test/integration/AddressMappingTest.java diff --git a/java/test/integration/AddressMappingTest.java b/java/test/integration/AddressMappingTest.java new file mode 100644 index 0000000000..7e0b2cdd18 --- /dev/null +++ b/java/test/integration/AddressMappingTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.vaticle.typedb.driver.test.integration; + +import com.vaticle.typedb.cloud.tool.runner.TypeDBCloudRunner; +import com.vaticle.typedb.common.collection.Pair; +import com.vaticle.typedb.driver.TypeDB; +import com.vaticle.typedb.driver.api.TypeDBCredential; +import com.vaticle.typedb.driver.api.TypeDBDriver; +import com.vaticle.typedb.driver.api.TypeDBOptions; +import com.vaticle.typedb.driver.api.TypeDBSession; +import com.vaticle.typedb.driver.api.TypeDBTransaction; +import com.vaticle.typedb.driver.api.concept.type.EntityType; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.vaticle.typedb.common.collection.Collections.map; +import static com.vaticle.typedb.common.collection.Collections.pair; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +@SuppressWarnings("Duplicates") +public class AddressMappingTest { + private static final Logger LOG = LoggerFactory.getLogger(AddressMappingTest.class); + + private static final Map serverOptions = map( + pair("--diagnostics.reporting.errors", "false"), + pair("--diagnostics.reporting.statistics", "false"), + pair("--diagnostics.monitoring.enable", "false") + ); + private static final TypeDBCredential credential = new TypeDBCredential("admin", "password", false); + + @Test + public void testAllNodesMapped() { + TypeDBCloudRunner typedb = TypeDBCloudRunner.create(Paths.get("."), 3, serverOptions); + typedb.start(); + Map addresses = typedb.externalAddresses().stream().map(address -> pair(address, address)).collect(Collectors.toMap(Pair::first, Pair::second)); + TypeDBDriver driver = TypeDB.cloudDriver(addresses, credential); + driver.databases().create("typedb"); + TypeDBSession session = driver.session("typedb", TypeDBSession.Type.DATA); + TypeDBTransaction tx = session.transaction(TypeDBTransaction.Type.WRITE); + EntityType root = tx.concepts().getRootEntityType(); + assertNotNull(root); + assertEquals(1, root.getSubtypes(tx).collect(Collectors.toList()).size()); + tx.close(); + session.close(); + driver.close(); + } +} diff --git a/java/test/integration/BUILD b/java/test/integration/BUILD index 5c7dacd507..49b5562940 100644 --- a/java/test/integration/BUILD +++ b/java/test/integration/BUILD @@ -44,6 +44,29 @@ typedb_java_test( ], ) +typedb_java_test( + name = "test-address-mapping", + srcs = ["AddressMappingTest.java"], + server_artifacts = { + "@vaticle_bazel_distribution//platform:is_linux_arm64": "@vaticle_typedb_cloud_artifact_linux-arm64//file", + "@vaticle_bazel_distribution//platform:is_linux_x86_64": "@vaticle_typedb_cloud_artifact_linux-x86_64//file", + "@vaticle_bazel_distribution//platform:is_mac_arm64": "@vaticle_typedb_cloud_artifact_mac-arm64//file", + "@vaticle_bazel_distribution//platform:is_mac_x86_64": "@vaticle_typedb_cloud_artifact_mac-x86_64//file", + "@vaticle_bazel_distribution//platform:is_windows_x86_64": "@vaticle_typedb_cloud_artifact_windows-x86_64//file", + }, + test_class = "com.vaticle.typedb.driver.test.integration.AddressMappingTest", + deps = [ + # Internal dependencies + "//java:driver-java", + "//java/api", + + # External dependencies from @vaticle + "@vaticle_typeql//common/java:common", + "@maven//:org_slf4j_slf4j_api", + "@maven//:com_vaticle_typedb_typedb_cloud_runner", + ], +) + checkstyle_test( name = "checkstyle", include = glob(["*"]), From dd8230fbd1844e82fa3d61987e4894b2ec719053 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:10:53 +0100 Subject: [PATCH 10/57] Revert "Open cloud connection with address translation, treat advertised addresses as names" This reverts commit f6f4c6fbf788a18d9d1235a418d597a25f722e5e. --- c/src/database.rs | 2 +- rust/src/common/error.rs | 6 +- rust/src/common/info.rs | 6 +- rust/src/connection/connection.rs | 115 +++++------------- rust/src/connection/network/proto/database.rs | 2 +- rust/src/database/database.rs | 33 +++-- rust/src/database/database_manager.rs | 4 +- rust/src/database/session.rs | 2 +- rust/src/user/user.rs | 2 +- 9 files changed, 56 insertions(+), 116 deletions(-) diff --git a/c/src/database.rs b/c/src/database.rs index 259c46a6ba..aa65b984fb 100644 --- a/c/src/database.rs +++ b/c/src/database.rs @@ -110,7 +110,7 @@ pub extern "C" fn replica_info_drop(replica_info: *mut ReplicaInfo) { /// Retrieves the address of the server hosting this replica #[no_mangle] pub extern "C" fn replica_info_get_address(replica_info: *const ReplicaInfo) -> *mut c_char { - release_string(borrow(replica_info).server_name.to_string()) + release_string(borrow(replica_info).address.to_string()) } /// Checks whether this is the primary replica of the raft cluster. diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index 1e8926ac91..d141e3d23a 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -22,7 +22,7 @@ use std::{error::Error as StdError, fmt}; use tonic::{Code, Status}; use typeql::error_messages; -use super::RequestID; +use super::{address::Address, RequestID}; error_messages! { ConnectionError code: "CXN", type: "Connection Error", @@ -84,8 +84,10 @@ error_messages! { InternalError 3: "Unexpected request type for remote procedure call: {request_type}.", UnexpectedResponseType { response_type: String } = 4: "Unexpected response type for remote procedure call: {response_type}.", + UnknownConnectionAddress { address: Address } = + 5: "Received unrecognized address from the server: {address}.", EnumOutOfBounds { value: i32, enum_name: &'static str } = - 5: "Value '{value}' is out of bounds for enum '{enum_name}'.", + 6: "Value '{value}' is out of bounds for enum '{enum_name}'.", } /// Represents errors encountered during operation. diff --git a/rust/src/common/info.rs b/rust/src/common/info.rs index 5fffad5b1a..2ce6b74f33 100644 --- a/rust/src/common/info.rs +++ b/rust/src/common/info.rs @@ -21,12 +21,12 @@ use std::time::Duration; use tokio::sync::mpsc::UnboundedSender; -use super::SessionID; +use super::{address::Address, SessionID}; use crate::common::Callback; #[derive(Clone, Debug)] pub(crate) struct SessionInfo { - pub(crate) server_name: String, + pub(crate) address: Address, pub(crate) session_id: SessionID, pub(crate) network_latency: Duration, pub(crate) on_close_register_sink: UnboundedSender, @@ -42,7 +42,7 @@ pub(crate) struct DatabaseInfo { #[derive(Debug)] pub struct ReplicaInfo { /// The address of the server hosting this replica - pub server_name: String, + pub address: Address, /// Whether this is the primary replica of the raft cluster. pub is_primary: bool, /// Whether this is the preferred replica of the raft cluster. diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 9f84cb51d9..fb41c6fb06 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -76,7 +76,6 @@ impl Connection { /// Connection::new_core("127.0.0.1:1729") /// ``` pub fn new_core(address: impl AsRef) -> Result { - let name = address.as_ref().to_owned(); let address: Address = address.as_ref().parse()?; let background_runtime = Arc::new(BackgroundRuntime::new()?); let mut server_connection = ServerConnection::new_core(background_runtime.clone(), name, address)?; @@ -128,63 +127,11 @@ impl Connection { let init_addresses = init_addresses.iter().map(|addr| addr.as_ref().parse()).try_collect()?; let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; - Self::new_cloud_impl( - background_runtime, - addresses.into_iter().map(|addr| (addr.to_string(), addr)).collect(), - credential, - ) - } - - /// Creates a new TypeDB Cloud connection. - /// - /// # Arguments - /// - /// * `addresses` -- Translation map from addresses received from the TypeDB server(s) to - /// addresses to be used by the driver for connection - /// * `credential` -- User credential and TLS encryption setting - /// - /// # Examples - /// - /// ```rust - /// Connection::new_cloud( - /// &["localhost:11729", "localhost:21729", "localhost:31729"], - /// Credential::with_tls( - /// "admin", - /// "password", - /// Some(&PathBuf::from( - /// std::env::var("ROOT_CA") - /// .expect("ROOT_CA environment variable needs to be set for cloud tests to run"), - /// )), - /// )?, - /// ) - /// ``` - pub fn new_cloud_address_map + Sync, U: AsRef + Sync>( - addresses: HashMap, - credential: Credential, - ) -> Result { - let background_runtime = Arc::new(BackgroundRuntime::new()?); - - let server_addresses: HashMap<_, _> = addresses - .into_iter() - .map(|(name, address)| { - let name = name.as_ref().to_owned(); - address.as_ref().parse::
().map(|address| (name, address)) - }) - .try_collect()?; - - Self::new_cloud_impl(background_runtime, server_addresses, credential) - } - - fn new_cloud_impl( - background_runtime: Arc, - server_addresses: HashMap, - credential: Credential, - ) -> Result { - let server_connections: HashMap = server_addresses + let server_connections: HashMap = addresses .into_iter() - .map(|(name, address)| { - ServerConnection::new_cloud(background_runtime.clone(), name.clone(), address, credential.clone()) - .map(|server_connection| (name, server_connection)) + .map(|address| { + ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()) + .map(|server_connection| (address, server_connection)) }) .try_collect()?; @@ -210,12 +157,8 @@ impl Connection { credential: Credential, ) -> Result> { for address in &addresses { - let server_connection = ServerConnection::new_cloud( - background_runtime.clone(), - address.to_string(), - address.clone(), - credential.clone(), - ); + let server_connection = + ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()); match server_connection { Ok(server_connection) => match server_connection.servers_all() { Ok(servers) => return Ok(servers.into_iter().collect()), @@ -275,12 +218,14 @@ impl Connection { self.server_connections.len() } - pub(crate) fn server_names(&self) -> impl Iterator { - self.server_connections.keys().map(|name| name.as_str()) + pub(crate) fn addresses(&self) -> impl Iterator { + self.server_connections.keys() } - pub(crate) fn connection(&self, address: &str) -> Option<&ServerConnection> { - self.server_connections.get(address) + pub(crate) fn connection(&self, address: &Address) -> Result<&ServerConnection> { + self.server_connections + .get(address) + .ok_or_else(|| InternalError::UnknownConnectionAddress { address: address.clone() }.into()) } pub(crate) fn connections(&self) -> impl Iterator + '_ { @@ -292,7 +237,9 @@ impl Connection { } pub(crate) fn unable_to_connect_error(&self) -> Error { - Error::Connection(ConnectionError::ServerConnectionFailedStatusError { error: self.server_names().join(", ") }) + Error::Connection(ConnectionError::ServerConnectionFailedStatusError { + error: self.addresses().map(Address::to_string).collect::>().join(", "), + }) } } @@ -304,26 +251,22 @@ impl fmt::Debug for Connection { #[derive(Clone)] pub(crate) struct ServerConnection { - name: String, + address: Address, background_runtime: Arc, open_sessions: Arc>>>, request_transmitter: Arc, } impl ServerConnection { - fn new_core(background_runtime: Arc, name: String, address: Address) -> Result { - let request_transmitter = Arc::new(RPCTransmitter::start_core(address, &background_runtime)?); - Ok(Self { name, background_runtime, open_sessions: Default::default(), request_transmitter }) + fn new_core(background_runtime: Arc, address: Address) -> Result { + let request_transmitter = Arc::new(RPCTransmitter::start_core(address.clone(), &background_runtime)?); + Ok(Self { address, background_runtime, open_sessions: Default::default(), request_transmitter }) } - fn new_cloud( - background_runtime: Arc, - name: String, - address: Address, - credential: Credential, - ) -> Result { - let request_transmitter = Arc::new(RPCTransmitter::start_cloud(address, credential, &background_runtime)?); - Ok(Self { name, background_runtime, open_sessions: Default::default(), request_transmitter }) + fn new_cloud(background_runtime: Arc, address: Address, credential: Credential) -> Result { + let request_transmitter = + Arc::new(RPCTransmitter::start_cloud(address.clone(), credential, &background_runtime)?); + Ok(Self { address, background_runtime, open_sessions: Default::default(), request_transmitter }) } pub(crate) fn validate(&self) -> Result { @@ -333,12 +276,12 @@ impl ServerConnection { } } - fn set_name(&mut self, name: String) { - self.name = name; + fn set_address(&mut self, address: Address) { + self.address = address; } - pub(crate) fn name(&self) -> &str { - &self.name + pub(crate) fn address(&self) -> &Address { + &self.address } #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] @@ -452,7 +395,7 @@ impl ServerConnection { pulse_shutdown_source, )); Ok(SessionInfo { - server_name: self.name.clone(), + address: self.address.clone(), session_id, network_latency: start.elapsed().saturating_sub(server_duration), on_close_register_sink, @@ -567,7 +510,7 @@ impl ServerConnection { impl fmt::Debug for ServerConnection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ServerConnection") - .field("name", &self.name) + .field("address", &self.address) .field("open_sessions", &self.open_sessions) .finish() } diff --git a/rust/src/connection/network/proto/database.rs b/rust/src/connection/network/proto/database.rs index c2b3310109..471d116619 100644 --- a/rust/src/connection/network/proto/database.rs +++ b/rust/src/connection/network/proto/database.rs @@ -38,7 +38,7 @@ impl TryFromProto for DatabaseInfo { impl TryFromProto for ReplicaInfo { fn try_from_proto(proto: ReplicaProto) -> Result { Ok(Self { - server_name: proto.address, + address: proto.address.parse()?, is_primary: proto.primary, is_preferred: proto.preferred, term: proto.term, diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index 42eab0b971..e7abea1feb 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -25,6 +25,7 @@ use log::{debug, error}; use crate::{ common::{ + address::Address, error::ConnectionError, info::{DatabaseInfo, ReplicaInfo}, Error, Result, @@ -181,15 +182,11 @@ impl Database { { let replicas = self.replicas.read().unwrap().clone(); for replica in replicas { - let Some(database) = replica.database.clone() else { - debug!("Skipping replica @ {} (address translation not found)", replica.server_name); - continue; - }; - match task(database).await { + match task(replica.database.clone(), self.connection.connection(&replica.address)?.clone()).await { Err(Error::Connection( ConnectionError::ServerConnectionFailedStatusError { .. } | ConnectionError::ConnectionFailed, )) => { - debug!("Unable to connect to {}. Attempting next server.", replica.server_name); + debug!("Unable to connect to {}. Attempting next server.", replica.address); } res => return res, } @@ -207,11 +204,9 @@ impl Database { if let Some(replica) = self.primary_replica() { replica } else { self.seek_primary_replica().await? }; for _ in 0..Self::PRIMARY_REPLICA_TASK_MAX_RETRIES { - let Some(database) = primary_replica.database.clone() else { - error!("Address translation not found for primary replica @ {} ", primary_replica.server_name); - return Err(self.connection.unable_to_connect_error()); - }; - match task(database).await { + match task(primary_replica.database.clone(), self.connection.connection(&primary_replica.address)?.clone()) + .await + { Err(Error::Connection( ConnectionError::CloudReplicaNotPrimary | ConnectionError::ServerConnectionFailedStatusError { .. } @@ -264,8 +259,8 @@ impl fmt::Debug for Database { /// The metadata and state of an individual raft replica of a database. #[derive(Clone)] pub(super) struct Replica { - /// Retrieves the name of the server hosting this replica - server_name: String, + /// Retrieves address of the server hosting this replica + address: Address, /// Retrieves the database name for which this is a replica database_name: String, /// Checks whether this is the primary replica of the raft cluster. @@ -281,7 +276,7 @@ pub(super) struct Replica { impl Replica { fn new(name: String, metadata: ReplicaInfo, server_connection: Option) -> Self { Self { - server_name: metadata.server_name, + address: metadata.address, database_name: name.clone(), is_primary: metadata.is_primary, term: metadata.term, @@ -295,15 +290,15 @@ impl Replica { .replicas .into_iter() .map(|replica| { - let server_connection = connection.connection(&replica.server_name).cloned(); - Self::new(database_info.name.clone(), replica, server_connection) + let server_connection = connection.connection(&replica.address)?.clone(); + Ok(Self::new(database_info.name.clone(), replica, server_connection)) }) .collect() } fn to_info(&self) -> ReplicaInfo { ReplicaInfo { - server_name: self.server_name.clone(), + address: self.address.clone(), is_primary: self.is_primary, is_preferred: self.is_preferred, term: self.term, @@ -326,7 +321,7 @@ impl Replica { error!( "Failed to fetch replica info for database '{}' from {}. Attempting next server.", name, - server_connection.name() + server_connection.address() ); } Err(err) => return Err(err), @@ -339,7 +334,7 @@ impl Replica { impl fmt::Debug for Replica { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Replica") - .field("address", &self.server_name) + .field("address", &self.address) .field("database_name", &self.database_name) .field("is_primary", &self.is_primary) .field("term", &self.term) diff --git a/rust/src/database/database_manager.rs b/rust/src/database/database_manager.rs index e78b720c8d..192d8d2856 100644 --- a/rust/src/database/database_manager.rs +++ b/rust/src/database/database_manager.rs @@ -117,7 +117,7 @@ impl DatabaseManager { .map(|db_info| Database::new(db_info, self.connection.clone())) .collect()) } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.name(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } } Err(ConnectionError::ServerConnectionFailedWithError { error: error_buffer.join("\n") })? @@ -143,7 +143,7 @@ impl DatabaseManager { .await } err @ Err(Error::Connection(ConnectionError::ConnectionIsClosed)) => return err, - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.name(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } } Err(ConnectionError::ServerConnectionFailedWithError { error: error_buffer.join("\n") })? diff --git a/rust/src/database/session.rs b/rust/src/database/session.rs index c03c3f9b52..9b471dc352 100644 --- a/rust/src/database/session.rs +++ b/rust/src/database/session.rs @@ -143,7 +143,7 @@ impl Session { pub fn force_close(&self) -> Result { if self.is_open.compare_exchange(true, false).is_ok() { let session_info = self.server_session_info.write().unwrap(); - let connection = self.database.connection().connection(&session_info.server_name).unwrap(); + let connection = self.database.connection().connection(&session_info.address).unwrap(); connection.close_session(session_info.session_id.clone())?; } Ok(()) diff --git a/rust/src/user/user.rs b/rust/src/user/user.rs index 11f99dc4d7..a3f9582dbd 100644 --- a/rust/src/user/user.rs +++ b/rust/src/user/user.rs @@ -58,7 +58,7 @@ impl User { .await { Ok(()) => return Ok(()), - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.name(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } } Err(ConnectionError::CloudAllNodesFailed { errors: error_buffer.join("\n") })? From ecd277dfaced546efa4f72bb6a6e521143c1505d Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:37:12 +0100 Subject: [PATCH 11/57] Post-revert fixes + keep server name instead of address --- c/src/database.rs | 2 +- rust/src/common/error.rs | 4 +- rust/src/common/info.rs | 7 +- rust/src/connection/connection.rs | 66 +++++++++++-------- rust/src/connection/network/proto/database.rs | 28 +++----- rust/src/connection/network/proto/message.rs | 14 ++-- .../src/connection/network/transmitter/rpc.rs | 2 +- rust/src/database/database.rs | 35 +++++----- rust/src/database/database_manager.rs | 5 +- rust/src/database/session.rs | 2 +- 10 files changed, 80 insertions(+), 85 deletions(-) diff --git a/c/src/database.rs b/c/src/database.rs index aa65b984fb..259c46a6ba 100644 --- a/c/src/database.rs +++ b/c/src/database.rs @@ -110,7 +110,7 @@ pub extern "C" fn replica_info_drop(replica_info: *mut ReplicaInfo) { /// Retrieves the address of the server hosting this replica #[no_mangle] pub extern "C" fn replica_info_get_address(replica_info: *const ReplicaInfo) -> *mut c_char { - release_string(borrow(replica_info).address.to_string()) + release_string(borrow(replica_info).server_name.to_string()) } /// Checks whether this is the primary replica of the raft cluster. diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index d141e3d23a..23f3bdbc36 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -22,7 +22,7 @@ use std::{error::Error as StdError, fmt}; use tonic::{Code, Status}; use typeql::error_messages; -use super::{address::Address, RequestID}; +use super::RequestID; error_messages! { ConnectionError code: "CXN", type: "Connection Error", @@ -84,7 +84,7 @@ error_messages! { InternalError 3: "Unexpected request type for remote procedure call: {request_type}.", UnexpectedResponseType { response_type: String } = 4: "Unexpected response type for remote procedure call: {response_type}.", - UnknownConnectionAddress { address: Address } = + UnknownConnectionAddress { address: String } = 5: "Received unrecognized address from the server: {address}.", EnumOutOfBounds { value: i32, enum_name: &'static str } = 6: "Value '{value}' is out of bounds for enum '{enum_name}'.", diff --git a/rust/src/common/info.rs b/rust/src/common/info.rs index 2ce6b74f33..bdda7ec2d0 100644 --- a/rust/src/common/info.rs +++ b/rust/src/common/info.rs @@ -21,12 +21,11 @@ use std::time::Duration; use tokio::sync::mpsc::UnboundedSender; -use super::{address::Address, SessionID}; -use crate::common::Callback; +use super::{Callback, SessionID}; #[derive(Clone, Debug)] pub(crate) struct SessionInfo { - pub(crate) address: Address, + pub(crate) server_name: String, pub(crate) session_id: SessionID, pub(crate) network_latency: Duration, pub(crate) on_close_register_sink: UnboundedSender, @@ -42,7 +41,7 @@ pub(crate) struct DatabaseInfo { #[derive(Debug)] pub struct ReplicaInfo { /// The address of the server hosting this replica - pub address: Address, + pub server_name: String, /// Whether this is the primary replica of the raft cluster. pub is_primary: bool, /// Whether this is the preferred replica of the raft cluster. diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index fb41c6fb06..2cabb98122 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -76,7 +76,8 @@ impl Connection { /// Connection::new_core("127.0.0.1:1729") /// ``` pub fn new_core(address: impl AsRef) -> Result { - let address: Address = address.as_ref().parse()?; + let name = address.as_ref().to_string(); + let address: Address = name.parse()?; let background_runtime = Arc::new(BackgroundRuntime::new()?); let mut server_connection = ServerConnection::new_core(background_runtime.clone(), name, address)?; @@ -127,11 +128,16 @@ impl Connection { let init_addresses = init_addresses.iter().map(|addr| addr.as_ref().parse()).try_collect()?; let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; - let server_connections: HashMap = addresses + let server_connections: HashMap = addresses .into_iter() .map(|address| { - ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()) - .map(|server_connection| (address, server_connection)) + ServerConnection::new_cloud( + background_runtime.clone(), + address.to_string(), + address.clone(), + credential.clone(), + ) + .map(|server_connection| (address.to_string(), server_connection)) }) .try_collect()?; @@ -157,8 +163,12 @@ impl Connection { credential: Credential, ) -> Result> { for address in &addresses { - let server_connection = - ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()); + let server_connection = ServerConnection::new_cloud( + background_runtime.clone(), + address.to_string(), + address.clone(), + credential.clone(), + ); match server_connection { Ok(server_connection) => match server_connection.servers_all() { Ok(servers) => return Ok(servers.into_iter().collect()), @@ -218,14 +228,14 @@ impl Connection { self.server_connections.len() } - pub(crate) fn addresses(&self) -> impl Iterator { - self.server_connections.keys() + pub(crate) fn servers(&self) -> impl Iterator { + self.server_connections.keys().map(String::as_str) } - pub(crate) fn connection(&self, address: &Address) -> Result<&ServerConnection> { + pub(crate) fn connection(&self, id: &str) -> Result<&ServerConnection> { self.server_connections - .get(address) - .ok_or_else(|| InternalError::UnknownConnectionAddress { address: address.clone() }.into()) + .get(id) + .ok_or_else(|| InternalError::UnknownConnectionAddress { address: id.to_owned() }.into()) } pub(crate) fn connections(&self) -> impl Iterator + '_ { @@ -238,7 +248,7 @@ impl Connection { pub(crate) fn unable_to_connect_error(&self) -> Error { Error::Connection(ConnectionError::ServerConnectionFailedStatusError { - error: self.addresses().map(Address::to_string).collect::>().join(", "), + error: self.servers().map(str::to_owned).collect_vec().join(", "), }) } } @@ -251,22 +261,26 @@ impl fmt::Debug for Connection { #[derive(Clone)] pub(crate) struct ServerConnection { - address: Address, + name: String, background_runtime: Arc, open_sessions: Arc>>>, request_transmitter: Arc, } impl ServerConnection { - fn new_core(background_runtime: Arc, address: Address) -> Result { - let request_transmitter = Arc::new(RPCTransmitter::start_core(address.clone(), &background_runtime)?); - Ok(Self { address, background_runtime, open_sessions: Default::default(), request_transmitter }) + fn new_core(background_runtime: Arc, name: String, address: Address) -> Result { + let request_transmitter = Arc::new(RPCTransmitter::start_core(address, &background_runtime)?); + Ok(Self { name, background_runtime, open_sessions: Default::default(), request_transmitter }) } - fn new_cloud(background_runtime: Arc, address: Address, credential: Credential) -> Result { - let request_transmitter = - Arc::new(RPCTransmitter::start_cloud(address.clone(), credential, &background_runtime)?); - Ok(Self { address, background_runtime, open_sessions: Default::default(), request_transmitter }) + fn new_cloud( + background_runtime: Arc, + name: String, + address: Address, + credential: Credential, + ) -> Result { + let request_transmitter = Arc::new(RPCTransmitter::start_cloud(address, credential, &background_runtime)?); + Ok(Self { name, background_runtime, open_sessions: Default::default(), request_transmitter }) } pub(crate) fn validate(&self) -> Result { @@ -276,12 +290,12 @@ impl ServerConnection { } } - fn set_address(&mut self, address: Address) { - self.address = address; + fn set_name(&mut self, name: String) { + self.name = name; } - pub(crate) fn address(&self) -> &Address { - &self.address + pub(crate) fn address(&self) -> &str { + &self.name } #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] @@ -395,7 +409,7 @@ impl ServerConnection { pulse_shutdown_source, )); Ok(SessionInfo { - address: self.address.clone(), + server_name: self.name.clone(), session_id, network_latency: start.elapsed().saturating_sub(server_duration), on_close_register_sink, @@ -510,7 +524,7 @@ impl ServerConnection { impl fmt::Debug for ServerConnection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ServerConnection") - .field("address", &self.address) + .field("address", &self.name) .field("open_sessions", &self.open_sessions) .finish() } diff --git a/rust/src/connection/network/proto/database.rs b/rust/src/connection/network/proto/database.rs index 471d116619..2b3ebe9fa3 100644 --- a/rust/src/connection/network/proto/database.rs +++ b/rust/src/connection/network/proto/database.rs @@ -17,31 +17,19 @@ * under the License. */ -use itertools::Itertools; use typedb_protocol::{database_replicas::Replica as ReplicaProto, DatabaseReplicas as DatabaseProto}; -use super::TryFromProto; -use crate::{ - common::info::{DatabaseInfo, ReplicaInfo}, - Result, -}; +use super::FromProto; +use crate::common::info::{DatabaseInfo, ReplicaInfo}; -impl TryFromProto for DatabaseInfo { - fn try_from_proto(proto: DatabaseProto) -> Result { - Ok(Self { - name: proto.name, - replicas: proto.replicas.into_iter().map(ReplicaInfo::try_from_proto).try_collect()?, - }) +impl FromProto for DatabaseInfo { + fn from_proto(proto: DatabaseProto) -> Self { + Self { name: proto.name, replicas: proto.replicas.into_iter().map(ReplicaInfo::from_proto).collect() } } } -impl TryFromProto for ReplicaInfo { - fn try_from_proto(proto: ReplicaProto) -> Result { - Ok(Self { - address: proto.address.parse()?, - is_primary: proto.primary, - is_preferred: proto.preferred, - term: proto.term, - }) +impl FromProto for ReplicaInfo { + fn from_proto(proto: ReplicaProto) -> Self { + Self { server_name: proto.address, is_primary: proto.primary, is_preferred: proto.preferred, term: proto.term } } } diff --git a/rust/src/connection/network/proto/message.rs b/rust/src/connection/network/proto/message.rs index 7adad840f5..0fcd67389a 100644 --- a/rust/src/connection/network/proto/message.rs +++ b/rust/src/connection/network/proto/message.rs @@ -267,18 +267,18 @@ impl FromProto for Response { impl TryFromProto for Response { fn try_from_proto(proto: database_manager::get::Res) -> Result { Ok(Self::DatabaseGet { - database: DatabaseInfo::try_from_proto( + database: DatabaseInfo::from_proto( proto.database.ok_or(ConnectionError::MissingResponseField { field: "database" })?, - )?, + ), }) } } -impl TryFromProto for Response { - fn try_from_proto(proto: database_manager::all::Res) -> Result { - Ok(Self::DatabasesAll { - databases: proto.databases.into_iter().map(DatabaseInfo::try_from_proto).try_collect()?, - }) +impl FromProto for Response { + fn from_proto(proto: database_manager::all::Res) -> Self { + Self::DatabasesAll { + databases: proto.databases.into_iter().map(DatabaseInfo::from_proto).collect(), + } } } diff --git a/rust/src/connection/network/transmitter/rpc.rs b/rust/src/connection/network/transmitter/rpc.rs index a867d305f3..4231e762b5 100644 --- a/rust/src/connection/network/transmitter/rpc.rs +++ b/rust/src/connection/network/transmitter/rpc.rs @@ -133,7 +133,7 @@ impl RPCTransmitter { rpc.databases_get(request.try_into_proto()?).await.and_then(Response::try_from_proto) } Request::DatabasesAll => { - rpc.databases_all(request.try_into_proto()?).await.and_then(Response::try_from_proto) + rpc.databases_all(request.try_into_proto()?).await.map(Response::from_proto) } Request::DatabaseDelete { .. } => { diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index e7abea1feb..daf3ba029d 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -25,7 +25,6 @@ use log::{debug, error}; use crate::{ common::{ - address::Address, error::ConnectionError, info::{DatabaseInfo, ReplicaInfo}, Error, Result, @@ -46,10 +45,10 @@ impl Database { const FETCH_REPLICAS_MAX_RETRIES: usize = 10; const WAIT_FOR_PRIMARY_REPLICA_SELECTION: Duration = Duration::from_secs(2); - pub(super) fn new(database_info: DatabaseInfo, connection: Connection) -> Self { + pub(super) fn new(database_info: DatabaseInfo, connection: Connection) -> Result { let name = database_info.name.clone(); - let replicas = RwLock::new(Replica::try_from_info(database_info, &connection)); - Self { name, replicas, connection } + let replicas = RwLock::new(Replica::try_from_info(database_info, &connection)?); + Ok(Self { name, replicas, connection }) } #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] @@ -182,11 +181,11 @@ impl Database { { let replicas = self.replicas.read().unwrap().clone(); for replica in replicas { - match task(replica.database.clone(), self.connection.connection(&replica.address)?.clone()).await { + match task(replica.database.clone()).await { Err(Error::Connection( ConnectionError::ServerConnectionFailedStatusError { .. } | ConnectionError::ConnectionFailed, )) => { - debug!("Unable to connect to {}. Attempting next server.", replica.address); + debug!("Unable to connect to {}. Attempting next server.", replica.server_name); } res => return res, } @@ -204,9 +203,7 @@ impl Database { if let Some(replica) = self.primary_replica() { replica } else { self.seek_primary_replica().await? }; for _ in 0..Self::PRIMARY_REPLICA_TASK_MAX_RETRIES { - match task(primary_replica.database.clone(), self.connection.connection(&primary_replica.address)?.clone()) - .await - { + match task(primary_replica.database.clone()).await { Err(Error::Connection( ConnectionError::CloudReplicaNotPrimary | ConnectionError::ServerConnectionFailedStatusError { .. } @@ -260,7 +257,7 @@ impl fmt::Debug for Database { #[derive(Clone)] pub(super) struct Replica { /// Retrieves address of the server hosting this replica - address: Address, + server_name: String, /// Retrieves the database name for which this is a replica database_name: String, /// Checks whether this is the primary replica of the raft cluster. @@ -270,27 +267,27 @@ pub(super) struct Replica { /// Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. is_preferred: bool, /// Retrieves the database for which this is a replica - database: Option, + database: ServerDatabase, } impl Replica { - fn new(name: String, metadata: ReplicaInfo, server_connection: Option) -> Self { + fn new(name: String, metadata: ReplicaInfo, server_connection: ServerConnection) -> Self { Self { - address: metadata.address, + server_name: metadata.server_name, database_name: name.clone(), is_primary: metadata.is_primary, term: metadata.term, is_preferred: metadata.is_preferred, - database: server_connection.map(|server_connection| ServerDatabase::new(name, server_connection)), + database: ServerDatabase::new(name, server_connection), } } - fn try_from_info(database_info: DatabaseInfo, connection: &Connection) -> Vec { + fn try_from_info(database_info: DatabaseInfo, connection: &Connection) -> Result> { database_info .replicas .into_iter() .map(|replica| { - let server_connection = connection.connection(&replica.address)?.clone(); + let server_connection = connection.connection(&replica.server_name)?.clone(); Ok(Self::new(database_info.name.clone(), replica, server_connection)) }) .collect() @@ -298,7 +295,7 @@ impl Replica { fn to_info(&self) -> ReplicaInfo { ReplicaInfo { - address: self.address.clone(), + server_name: self.server_name.clone(), is_primary: self.is_primary, is_preferred: self.is_preferred, term: self.term, @@ -311,7 +308,7 @@ impl Replica { let res = server_connection.get_database_replicas(name.clone()).await; match res { Ok(info) => { - return Ok(Self::try_from_info(info, &connection)); + return Self::try_from_info(info, &connection); } Err(Error::Connection( ConnectionError::DatabaseDoesNotExist { .. } @@ -334,7 +331,7 @@ impl Replica { impl fmt::Debug for Replica { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Replica") - .field("address", &self.address) + .field("address", &self.server_name) .field("database_name", &self.database_name) .field("is_primary", &self.is_primary) .field("term", &self.term) diff --git a/rust/src/database/database_manager.rs b/rust/src/database/database_manager.rs index 192d8d2856..c1231a6a40 100644 --- a/rust/src/database/database_manager.rs +++ b/rust/src/database/database_manager.rs @@ -112,10 +112,7 @@ impl DatabaseManager { for server_connection in self.connection.connections() { match server_connection.all_databases().await { Ok(list) => { - return Ok(list - .into_iter() - .map(|db_info| Database::new(db_info, self.connection.clone())) - .collect()) + return list.into_iter().map(|db_info| Database::new(db_info, self.connection.clone())).collect() } Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } diff --git a/rust/src/database/session.rs b/rust/src/database/session.rs index 9b471dc352..c03c3f9b52 100644 --- a/rust/src/database/session.rs +++ b/rust/src/database/session.rs @@ -143,7 +143,7 @@ impl Session { pub fn force_close(&self) -> Result { if self.is_open.compare_exchange(true, false).is_ok() { let session_info = self.server_session_info.write().unwrap(); - let connection = self.database.connection().connection(&session_info.address).unwrap(); + let connection = self.database.connection().connection(&session_info.server_name).unwrap(); connection.close_session(session_info.session_id.clone())?; } Ok(()) From 1d4960e882c6f3d2f328fa2f5176f716ef6ec1fa Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:04:01 +0100 Subject: [PATCH 12/57] Server ID rather than name --- c/src/database.rs | 2 +- rust/src/common/info.rs | 4 +- rust/src/connection/connection.rs | 41 +++++++++---------- rust/src/connection/network/proto/database.rs | 2 +- rust/src/database/database.rs | 16 ++++---- rust/src/database/database_manager.rs | 4 +- rust/src/database/session.rs | 4 +- rust/src/user/user.rs | 2 +- 8 files changed, 36 insertions(+), 39 deletions(-) diff --git a/c/src/database.rs b/c/src/database.rs index 259c46a6ba..81c0a62039 100644 --- a/c/src/database.rs +++ b/c/src/database.rs @@ -110,7 +110,7 @@ pub extern "C" fn replica_info_drop(replica_info: *mut ReplicaInfo) { /// Retrieves the address of the server hosting this replica #[no_mangle] pub extern "C" fn replica_info_get_address(replica_info: *const ReplicaInfo) -> *mut c_char { - release_string(borrow(replica_info).server_name.to_string()) + release_string(borrow(replica_info).server_id.to_string()) } /// Checks whether this is the primary replica of the raft cluster. diff --git a/rust/src/common/info.rs b/rust/src/common/info.rs index bdda7ec2d0..afe41d844e 100644 --- a/rust/src/common/info.rs +++ b/rust/src/common/info.rs @@ -25,7 +25,7 @@ use super::{Callback, SessionID}; #[derive(Clone, Debug)] pub(crate) struct SessionInfo { - pub(crate) server_name: String, + pub(crate) server_id: String, pub(crate) session_id: SessionID, pub(crate) network_latency: Duration, pub(crate) on_close_register_sink: UnboundedSender, @@ -41,7 +41,7 @@ pub(crate) struct DatabaseInfo { #[derive(Debug)] pub struct ReplicaInfo { /// The address of the server hosting this replica - pub server_name: String, + pub server_id: String, /// Whether this is the primary replica of the raft cluster. pub is_primary: bool, /// Whether this is the preferred replica of the raft cluster. diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 2cabb98122..267f297127 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -76,22 +76,22 @@ impl Connection { /// Connection::new_core("127.0.0.1:1729") /// ``` pub fn new_core(address: impl AsRef) -> Result { - let name = address.as_ref().to_string(); - let address: Address = name.parse()?; + let id = address.as_ref().to_string(); + let address: Address = id.parse()?; let background_runtime = Arc::new(BackgroundRuntime::new()?); - let mut server_connection = ServerConnection::new_core(background_runtime.clone(), name, address)?; + let mut server_connection = ServerConnection::new_core(background_runtime.clone(), id, address)?; - let advertised_name = server_connection + let advertised_id = server_connection .servers_all()? .into_iter() .exactly_one() .map_err(|e| ConnectionError::ServerConnectionFailedStatusError { error: e.to_string() })? .to_string(); - server_connection.set_name(advertised_name.clone()); + server_connection.set_id(advertised_id.clone()); match server_connection.validate() { Ok(()) => Ok(Self { - server_connections: [(advertised_name, server_connection)].into(), + server_connections: [(advertised_id, server_connection)].into(), background_runtime, username: None, is_cloud: false, @@ -151,7 +151,7 @@ impl Connection { Ok(Self { server_connections, background_runtime, - username: Some(credential.username().to_string()), + username: Some(credential.username().to_owned()), is_cloud: true, }) } @@ -243,7 +243,7 @@ impl Connection { } pub(crate) fn username(&self) -> Option<&str> { - self.username.as_ref().map(|s| s.as_ref()) + self.username.as_deref() } pub(crate) fn unable_to_connect_error(&self) -> Error { @@ -261,26 +261,26 @@ impl fmt::Debug for Connection { #[derive(Clone)] pub(crate) struct ServerConnection { - name: String, + id: String, background_runtime: Arc, open_sessions: Arc>>>, request_transmitter: Arc, } impl ServerConnection { - fn new_core(background_runtime: Arc, name: String, address: Address) -> Result { + fn new_core(background_runtime: Arc, id: String, address: Address) -> Result { let request_transmitter = Arc::new(RPCTransmitter::start_core(address, &background_runtime)?); - Ok(Self { name, background_runtime, open_sessions: Default::default(), request_transmitter }) + Ok(Self { id, background_runtime, open_sessions: Default::default(), request_transmitter }) } fn new_cloud( background_runtime: Arc, - name: String, + id: String, address: Address, credential: Credential, ) -> Result { let request_transmitter = Arc::new(RPCTransmitter::start_cloud(address, credential, &background_runtime)?); - Ok(Self { name, background_runtime, open_sessions: Default::default(), request_transmitter }) + Ok(Self { id, background_runtime, open_sessions: Default::default(), request_transmitter }) } pub(crate) fn validate(&self) -> Result { @@ -290,12 +290,12 @@ impl ServerConnection { } } - fn set_name(&mut self, name: String) { - self.name = name; + fn set_id(&mut self, id: String) { + self.id = id; } - pub(crate) fn address(&self) -> &str { - &self.name + pub(crate) fn id(&self) -> &str { + &self.id } #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] @@ -409,7 +409,7 @@ impl ServerConnection { pulse_shutdown_source, )); Ok(SessionInfo { - server_name: self.name.clone(), + server_id: self.id.clone(), session_id, network_latency: start.elapsed().saturating_sub(server_duration), on_close_register_sink, @@ -523,10 +523,7 @@ impl ServerConnection { impl fmt::Debug for ServerConnection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ServerConnection") - .field("address", &self.name) - .field("open_sessions", &self.open_sessions) - .finish() + f.debug_struct("ServerConnection").field("id", &self.id).field("open_sessions", &self.open_sessions).finish() } } diff --git a/rust/src/connection/network/proto/database.rs b/rust/src/connection/network/proto/database.rs index 2b3ebe9fa3..b8538610db 100644 --- a/rust/src/connection/network/proto/database.rs +++ b/rust/src/connection/network/proto/database.rs @@ -30,6 +30,6 @@ impl FromProto for DatabaseInfo { impl FromProto for ReplicaInfo { fn from_proto(proto: ReplicaProto) -> Self { - Self { server_name: proto.address, is_primary: proto.primary, is_preferred: proto.preferred, term: proto.term } + Self { server_id: proto.address, is_primary: proto.primary, is_preferred: proto.preferred, term: proto.term } } } diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index daf3ba029d..7c6b9fcb87 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -185,7 +185,7 @@ impl Database { Err(Error::Connection( ConnectionError::ServerConnectionFailedStatusError { .. } | ConnectionError::ConnectionFailed, )) => { - debug!("Unable to connect to {}. Attempting next server.", replica.server_name); + debug!("Unable to connect to {}. Attempting next server.", replica.server_id); } res => return res, } @@ -256,8 +256,8 @@ impl fmt::Debug for Database { /// The metadata and state of an individual raft replica of a database. #[derive(Clone)] pub(super) struct Replica { - /// Retrieves address of the server hosting this replica - server_name: String, + /// Retrieves id of the server hosting this replica + server_id: String, /// Retrieves the database name for which this is a replica database_name: String, /// Checks whether this is the primary replica of the raft cluster. @@ -273,7 +273,7 @@ pub(super) struct Replica { impl Replica { fn new(name: String, metadata: ReplicaInfo, server_connection: ServerConnection) -> Self { Self { - server_name: metadata.server_name, + server_id: metadata.server_id, database_name: name.clone(), is_primary: metadata.is_primary, term: metadata.term, @@ -287,7 +287,7 @@ impl Replica { .replicas .into_iter() .map(|replica| { - let server_connection = connection.connection(&replica.server_name)?.clone(); + let server_connection = connection.connection(&replica.server_id)?.clone(); Ok(Self::new(database_info.name.clone(), replica, server_connection)) }) .collect() @@ -295,7 +295,7 @@ impl Replica { fn to_info(&self) -> ReplicaInfo { ReplicaInfo { - server_name: self.server_name.clone(), + server_id: self.server_id.clone(), is_primary: self.is_primary, is_preferred: self.is_preferred, term: self.term, @@ -318,7 +318,7 @@ impl Replica { error!( "Failed to fetch replica info for database '{}' from {}. Attempting next server.", name, - server_connection.address() + server_connection.id() ); } Err(err) => return Err(err), @@ -331,7 +331,7 @@ impl Replica { impl fmt::Debug for Replica { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Replica") - .field("address", &self.server_name) + .field("server_id", &self.server_id) .field("database_name", &self.database_name) .field("is_primary", &self.is_primary) .field("term", &self.term) diff --git a/rust/src/database/database_manager.rs b/rust/src/database/database_manager.rs index c1231a6a40..ccf07949e9 100644 --- a/rust/src/database/database_manager.rs +++ b/rust/src/database/database_manager.rs @@ -114,7 +114,7 @@ impl DatabaseManager { Ok(list) => { return list.into_iter().map(|db_info| Database::new(db_info, self.connection.clone())).collect() } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.id(), err)), } } Err(ConnectionError::ServerConnectionFailedWithError { error: error_buffer.join("\n") })? @@ -140,7 +140,7 @@ impl DatabaseManager { .await } err @ Err(Error::Connection(ConnectionError::ConnectionIsClosed)) => return err, - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.id(), err)), } } Err(ConnectionError::ServerConnectionFailedWithError { error: error_buffer.join("\n") })? diff --git a/rust/src/database/session.rs b/rust/src/database/session.rs index c03c3f9b52..ee2abe2243 100644 --- a/rust/src/database/session.rs +++ b/rust/src/database/session.rs @@ -143,7 +143,7 @@ impl Session { pub fn force_close(&self) -> Result { if self.is_open.compare_exchange(true, false).is_ok() { let session_info = self.server_session_info.write().unwrap(); - let connection = self.database.connection().connection(&session_info.server_name).unwrap(); + let connection = self.database.connection().connection(&session_info.server_id).unwrap(); connection.close_session(session_info.session_id.clone())?; } Ok(()) @@ -222,7 +222,7 @@ impl Session { return Err(ConnectionError::SessionIsClosed.into()); } - let SessionInfo { server_name, session_id, network_latency, .. } = + let SessionInfo { server_id: server_name, session_id, network_latency, .. } = self.server_session_info.read().unwrap().clone(); let server_connection = &self.database.connection().connection(&server_name).unwrap(); diff --git a/rust/src/user/user.rs b/rust/src/user/user.rs index a3f9582dbd..05ddbc2a93 100644 --- a/rust/src/user/user.rs +++ b/rust/src/user/user.rs @@ -58,7 +58,7 @@ impl User { .await { Ok(()) => return Ok(()), - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.id(), err)), } } Err(ConnectionError::CloudAllNodesFailed { errors: error_buffer.join("\n") })? From bc4c9a97b337c67a30c027801e19bae04a3be166 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:33:52 +0100 Subject: [PATCH 13/57] ServerConnection no longer stores its own id --- rust/src/common/info.rs | 1 - rust/src/connection/connection.rs | 83 +++++++++++++++------------ rust/src/database/database.rs | 9 +-- rust/src/database/database_manager.rs | 8 +-- rust/src/database/session.rs | 47 +++++++++------ rust/src/user/user.rs | 4 +- 6 files changed, 82 insertions(+), 70 deletions(-) diff --git a/rust/src/common/info.rs b/rust/src/common/info.rs index afe41d844e..9f17fc589e 100644 --- a/rust/src/common/info.rs +++ b/rust/src/common/info.rs @@ -25,7 +25,6 @@ use super::{Callback, SessionID}; #[derive(Clone, Debug)] pub(crate) struct SessionInfo { - pub(crate) server_id: String, pub(crate) session_id: SessionID, pub(crate) network_latency: Duration, pub(crate) on_close_register_sink: UnboundedSender, diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 267f297127..5ca0018bd3 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -79,7 +79,7 @@ impl Connection { let id = address.as_ref().to_string(); let address: Address = id.parse()?; let background_runtime = Arc::new(BackgroundRuntime::new()?); - let mut server_connection = ServerConnection::new_core(background_runtime.clone(), id, address)?; + let server_connection = ServerConnection::new_core(background_runtime.clone(), address)?; let advertised_id = server_connection .servers_all()? @@ -87,7 +87,6 @@ impl Connection { .exactly_one() .map_err(|e| ConnectionError::ServerConnectionFailedStatusError { error: e.to_string() })? .to_string(); - server_connection.set_id(advertised_id.clone()); match server_connection.validate() { Ok(()) => Ok(Self { @@ -131,13 +130,42 @@ impl Connection { let server_connections: HashMap = addresses .into_iter() .map(|address| { - ServerConnection::new_cloud( - background_runtime.clone(), - address.to_string(), - address.clone(), - credential.clone(), - ) - .map(|server_connection| (address.to_string(), server_connection)) + ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()) + .map(|server_connection| (address.to_string(), server_connection)) + }) + .try_collect()?; + + let errors: Vec = + server_connections.values().map(|conn| conn.validate()).filter_map(Result::err).collect(); + if errors.len() == server_connections.len() { + Err(ConnectionError::CloudAllNodesFailed { + errors: errors.into_iter().map(|err| err.to_string()).collect::>().join("\n"), + })? + } else { + Ok(Self { + server_connections, + background_runtime, + username: Some(credential.username().to_owned()), + is_cloud: true, + }) + } + } + + pub fn new_cloud_address_map(init_addresses: HashMap, credential: Credential) -> Result + where + T: AsRef + Sync, + U: AsRef + Sync, + { + let background_runtime = Arc::new(BackgroundRuntime::new()?); + + let init_addresses = init_addresses.values().map(|addr| addr.as_ref().parse()).try_collect()?; + let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; + + let server_connections: HashMap = addresses + .into_iter() + .map(|address| { + ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()) + .map(|server_connection| (address.to_string(), server_connection)) }) .try_collect()?; @@ -163,12 +191,8 @@ impl Connection { credential: Credential, ) -> Result> { for address in &addresses { - let server_connection = ServerConnection::new_cloud( - background_runtime.clone(), - address.to_string(), - address.clone(), - credential.clone(), - ); + let server_connection = + ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()); match server_connection { Ok(server_connection) => match server_connection.servers_all() { Ok(servers) => return Ok(servers.into_iter().collect()), @@ -238,8 +262,8 @@ impl Connection { .ok_or_else(|| InternalError::UnknownConnectionAddress { address: id.to_owned() }.into()) } - pub(crate) fn connections(&self) -> impl Iterator + '_ { - self.server_connections.values() + pub(crate) fn connections(&self) -> impl Iterator + '_ { + self.server_connections.iter().map(|(id, conn)| (id.as_str(), conn)) } pub(crate) fn username(&self) -> Option<&str> { @@ -261,26 +285,20 @@ impl fmt::Debug for Connection { #[derive(Clone)] pub(crate) struct ServerConnection { - id: String, background_runtime: Arc, open_sessions: Arc>>>, request_transmitter: Arc, } impl ServerConnection { - fn new_core(background_runtime: Arc, id: String, address: Address) -> Result { + fn new_core(background_runtime: Arc, address: Address) -> Result { let request_transmitter = Arc::new(RPCTransmitter::start_core(address, &background_runtime)?); - Ok(Self { id, background_runtime, open_sessions: Default::default(), request_transmitter }) + Ok(Self { background_runtime, open_sessions: Default::default(), request_transmitter }) } - fn new_cloud( - background_runtime: Arc, - id: String, - address: Address, - credential: Credential, - ) -> Result { + fn new_cloud(background_runtime: Arc, address: Address, credential: Credential) -> Result { let request_transmitter = Arc::new(RPCTransmitter::start_cloud(address, credential, &background_runtime)?); - Ok(Self { id, background_runtime, open_sessions: Default::default(), request_transmitter }) + Ok(Self { background_runtime, open_sessions: Default::default(), request_transmitter }) } pub(crate) fn validate(&self) -> Result { @@ -290,14 +308,6 @@ impl ServerConnection { } } - fn set_id(&mut self, id: String) { - self.id = id; - } - - pub(crate) fn id(&self) -> &str { - &self.id - } - #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] async fn request(&self, request: Request) -> Result { if !self.background_runtime.is_open() { @@ -409,7 +419,6 @@ impl ServerConnection { pulse_shutdown_source, )); Ok(SessionInfo { - server_id: self.id.clone(), session_id, network_latency: start.elapsed().saturating_sub(server_duration), on_close_register_sink, @@ -523,7 +532,7 @@ impl ServerConnection { impl fmt::Debug for ServerConnection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ServerConnection").field("id", &self.id).field("open_sessions", &self.open_sessions).finish() + f.debug_struct("ServerConnection").field("open_sessions", &self.open_sessions).finish() } } diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index 7c6b9fcb87..6094125967 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -102,10 +102,6 @@ impl Database { self.preferred_replica().map(|replica| replica.to_info()) } - pub(super) fn connection(&self) -> &Connection { - &self.connection - } - /// Deletes this database. /// /// # Examples @@ -304,7 +300,7 @@ impl Replica { #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] async fn fetch_all(name: String, connection: Connection) -> Result> { - for server_connection in connection.connections() { + for (server_id, server_connection) in connection.connections() { let res = server_connection.get_database_replicas(name.clone()).await; match res { Ok(info) => { @@ -317,8 +313,7 @@ impl Replica { )) => { error!( "Failed to fetch replica info for database '{}' from {}. Attempting next server.", - name, - server_connection.id() + name, server_id ); } Err(err) => return Err(err), diff --git a/rust/src/database/database_manager.rs b/rust/src/database/database_manager.rs index ccf07949e9..df87df823a 100644 --- a/rust/src/database/database_manager.rs +++ b/rust/src/database/database_manager.rs @@ -109,12 +109,12 @@ impl DatabaseManager { #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub async fn all(&self) -> Result> { let mut error_buffer = Vec::with_capacity(self.connection.server_count()); - for server_connection in self.connection.connections() { + for (server_id, server_connection) in self.connection.connections() { match server_connection.all_databases().await { Ok(list) => { return list.into_iter().map(|db_info| Database::new(db_info, self.connection.clone())).collect() } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.id(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_id, err)), } } Err(ConnectionError::ServerConnectionFailedWithError { error: error_buffer.join("\n") })? @@ -127,7 +127,7 @@ impl DatabaseManager { P: Future>, { let mut error_buffer = Vec::with_capacity(self.connection.server_count()); - for server_connection in self.connection.connections() { + for (server_id, server_connection) in self.connection.connections() { match task(server_connection.clone(), name.clone()).await { Ok(res) => return Ok(res), Err(Error::Connection(ConnectionError::CloudReplicaNotPrimary)) => { @@ -140,7 +140,7 @@ impl DatabaseManager { .await } err @ Err(Error::Connection(ConnectionError::ConnectionIsClosed)) => return err, - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.id(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_id, err)), } } Err(ConnectionError::ServerConnectionFailedWithError { error: error_buffer.join("\n") })? diff --git a/rust/src/database/session.rs b/rust/src/database/session.rs index ee2abe2243..4f6f41bd6f 100644 --- a/rust/src/database/session.rs +++ b/rust/src/database/session.rs @@ -25,6 +25,7 @@ use log::warn; use crate::{ common::{error::ConnectionError, info::SessionInfo, Result, SessionType, TransactionType}, + connection::ServerConnection, Database, Options, Transaction, }; @@ -33,19 +34,27 @@ type Callback = Box; /// A session with a TypeDB database. pub struct Session { database: Database, - server_session_info: RwLock, + + server_session: RwLock, + session_type: SessionType, is_open: Arc>, on_close: Arc>>, on_reopen: Mutex>, } +#[derive(Clone, Debug)] +struct ServerSession { + connection: ServerConnection, + info: SessionInfo, +} + impl fmt::Debug for Session { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Session") .field("database", &self.database) .field("session_type", &self.session_type) - .field("server_session_info", &self.server_session_info) + .field("server_session", &self.server_session) .field("is_open", &self.is_open) .finish() } @@ -83,9 +92,11 @@ impl Session { /// ``` #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] pub async fn new_with_options(database: Database, session_type: SessionType, options: Options) -> Result { - let server_session_info = database + let server_session = database .run_failsafe(|database| async move { - database.connection().open_session(database.name().to_owned(), session_type, options).await + let session_info = + database.connection().open_session(database.name().to_owned(), session_type, options).await?; + Ok(ServerSession { connection: database.connection().clone(), info: session_info }) }) .await?; @@ -94,12 +105,12 @@ impl Session { let is_open = is_open.clone(); move || is_open.store(false) })])); - register_persistent_on_close(&server_session_info, on_close.clone()); + register_persistent_on_close(&server_session.info, on_close.clone()); Ok(Self { database, session_type, - server_session_info: RwLock::new(server_session_info), + server_session: RwLock::new(server_session), is_open, on_close, on_reopen: Mutex::default(), @@ -142,9 +153,8 @@ impl Session { /// ``` pub fn force_close(&self) -> Result { if self.is_open.compare_exchange(true, false).is_ok() { - let session_info = self.server_session_info.write().unwrap(); - let connection = self.database.connection().connection(&session_info.server_id).unwrap(); - connection.close_session(session_info.session_id.clone())?; + let server_session = self.server_session.write().unwrap(); + server_session.connection.close_session(server_session.info.session_id.clone())?; } Ok(()) } @@ -165,8 +175,8 @@ impl Session { } fn on_server_session_close(&self, callback: impl FnOnce() + Send + 'static) { - let session_info = self.server_session_info.write().unwrap(); - session_info.on_close_register_sink.send(Box::new(callback)).ok(); + let server_session = self.server_session.write().unwrap(); + server_session.info.on_close_register_sink.send(Box::new(callback)).ok(); } /// Registers a callback function which will be executed when this session is reopened. @@ -188,8 +198,8 @@ impl Session { fn reopened(&self) { self.on_reopen.lock().unwrap().iter_mut().for_each(|callback| (callback)()); - let session_info = self.server_session_info.write().unwrap(); - register_persistent_on_close(&session_info, self.on_close.clone()); + let server_session = self.server_session.write().unwrap(); + register_persistent_on_close(&server_session.info, self.on_close.clone()); } /// Opens a transaction to perform read or write queries on the database connected to the session. @@ -222,9 +232,8 @@ impl Session { return Err(ConnectionError::SessionIsClosed.into()); } - let SessionInfo { server_id: server_name, session_id, network_latency, .. } = - self.server_session_info.read().unwrap().clone(); - let server_connection = &self.database.connection().connection(&server_name).unwrap(); + let ServerSession { connection: server_connection, info: SessionInfo { session_id, network_latency, .. } } = + self.server_session.read().unwrap().clone(); let (transaction_stream, transaction_shutdown_sink) = match server_connection .open_transaction(session_id.clone(), transaction_type, options, network_latency) @@ -235,7 +244,7 @@ impl Session { self.is_open.store(false); server_connection.close_session(session_id).ok(); - let (session_info, (transaction_stream, transaction_shutdown_sink)) = self + let (server_session, (transaction_stream, transaction_shutdown_sink)) = self .database .run_failsafe(|database| { let session_type = self.session_type; @@ -244,7 +253,7 @@ impl Session { let database_name = database.name().to_owned(); let session_info = connection.open_session(database_name, session_type, options).await?; Ok(( - session_info.clone(), + ServerSession { connection: connection.clone(), info: session_info.clone() }, connection .open_transaction( session_info.session_id, @@ -257,7 +266,7 @@ impl Session { } }) .await?; - *self.server_session_info.write().unwrap() = session_info; + *self.server_session.write().unwrap() = server_session; self.is_open.store(true); self.reopened(); (transaction_stream, transaction_shutdown_sink) diff --git a/rust/src/user/user.rs b/rust/src/user/user.rs index 05ddbc2a93..292cda4fd8 100644 --- a/rust/src/user/user.rs +++ b/rust/src/user/user.rs @@ -52,13 +52,13 @@ impl User { let password_old = password_old.into(); let password_new = password_new.into(); let mut error_buffer = Vec::with_capacity(connection.server_count()); - for server_connection in connection.connections() { + for (server_id, server_connection) in connection.connections() { match server_connection .update_user_password(self.username.clone(), password_old.clone(), password_new.clone()) .await { Ok(()) => return Ok(()), - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.id(), err)), + Err(err) => error_buffer.push(format!("- {}: {}", server_id, err)), } } Err(ConnectionError::CloudAllNodesFailed { errors: error_buffer.join("\n") })? From fdfd49e8f38246e930b315e8189fb87920f6adc9 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:55:52 +0100 Subject: [PATCH 14/57] Open cloud with address translation redux --- rust/src/common/error.rs | 4 +- rust/src/connection/connection.rs | 77 +++++++++++-------- rust/src/connection/message.rs | 4 +- rust/src/connection/network/proto/message.rs | 12 ++- .../src/connection/network/transmitter/rpc.rs | 2 +- 5 files changed, 55 insertions(+), 44 deletions(-) diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index 23f3bdbc36..c77bd9db8a 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -17,7 +17,7 @@ * under the License. */ -use std::{error::Error as StdError, fmt}; +use std::{collections::HashSet, error::Error as StdError, fmt}; use tonic::{Code, Status}; use typeql::error_messages; @@ -72,6 +72,8 @@ error_messages! { ConnectionError 22: "Connection failed. Please check the server is running and the address is accessible. Encrypted Cloud endpoints may also have misconfigured SSL certificates.", MissingPort { address: String } = 23: "Invalid URL '{address}': missing port.", + AddressTranslationMismatch { unknown: HashSet, unmapped: HashSet } = + 24: "Address translation map does not match the advertised server list. Mapped servers not known to the remote server: {unknown:?}. Servers not mapped to addresses: {unmapped:?}.", } error_messages! { InternalError diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 5ca0018bd3..f69ca71f6d 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -124,48 +124,59 @@ impl Connection { pub fn new_cloud + Sync>(init_addresses: &[T], credential: Credential) -> Result { let background_runtime = Arc::new(BackgroundRuntime::new()?); - let init_addresses = init_addresses.iter().map(|addr| addr.as_ref().parse()).try_collect()?; - let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; + let servers = Self::fetch_server_list(background_runtime.clone(), init_addresses, credential.clone())?; - let server_connections: HashMap = addresses + let server_to_address: HashMap = servers .into_iter() .map(|address| { - ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()) - .map(|server_connection| (address.to_string(), server_connection)) + let id = address.clone(); + address.parse().map(|address| (id, address)) }) .try_collect()?; - let errors: Vec = - server_connections.values().map(|conn| conn.validate()).filter_map(Result::err).collect(); - if errors.len() == server_connections.len() { - Err(ConnectionError::CloudAllNodesFailed { - errors: errors.into_iter().map(|err| err.to_string()).collect::>().join("\n"), - })? - } else { - Ok(Self { - server_connections, - background_runtime, - username: Some(credential.username().to_owned()), - is_cloud: true, - }) - } + Self::new_cloud_impl(server_to_address, background_runtime, credential) } - pub fn new_cloud_address_map(init_addresses: HashMap, credential: Credential) -> Result + pub fn new_cloud_address_map(address_translation: HashMap, credential: Credential) -> Result where T: AsRef + Sync, U: AsRef + Sync, { let background_runtime = Arc::new(BackgroundRuntime::new()?); - let init_addresses = init_addresses.values().map(|addr| addr.as_ref().parse()).try_collect()?; - let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; + let servers = + Self::fetch_server_list(background_runtime.clone(), address_translation.values(), credential.clone())?; - let server_connections: HashMap = addresses + let server_to_address: HashMap = address_translation .into_iter() - .map(|address| { - ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()) - .map(|server_connection| (address.to_string(), server_connection)) + .map(|(id, address)| { + let id = id.as_ref().to_owned(); + address.as_ref().parse().map(|address| (id, address)) + }) + .try_collect()?; + + let translated: HashSet = server_to_address.keys().cloned().collect(); + let unmapped = &servers - &translated; + let unknown = &translated - &servers; + if !unmapped.is_empty() && !unknown.is_empty() { + return Err(ConnectionError::AddressTranslationMismatch { unknown, unmapped }.into()); + } + + debug_assert_eq!(servers, translated); + + Self::new_cloud_impl(server_to_address, background_runtime, credential) + } + + fn new_cloud_impl( + server_to_address: HashMap, + background_runtime: Arc, + credential: Credential, + ) -> Result { + let server_connections: HashMap = server_to_address + .into_iter() + .map(|(server_id, address)| { + ServerConnection::new_cloud(background_runtime.clone(), address, credential.clone()) + .map(|server_connection| (server_id, server_connection)) }) .try_collect()?; @@ -185,14 +196,14 @@ impl Connection { } } - fn fetch_current_addresses( + fn fetch_server_list( background_runtime: Arc, - addresses: Vec
, + addresses: impl IntoIterator> + Clone, credential: Credential, - ) -> Result> { - for address in &addresses { + ) -> Result> { + for address in addresses.clone() { let server_connection = - ServerConnection::new_cloud(background_runtime.clone(), address.clone(), credential.clone()); + ServerConnection::new_cloud(background_runtime.clone(), address.as_ref().parse()?, credential.clone()); match server_connection { Ok(server_connection) => match server_connection.servers_all() { Ok(servers) => return Ok(servers.into_iter().collect()), @@ -208,7 +219,7 @@ impl Connection { } } Err(ConnectionError::ServerConnectionFailed { - addresses: addresses.into_iter().map(|a| a.to_string()).collect::>().join(","), + addresses: addresses.into_iter().map(|addr| addr.as_ref().to_owned()).join(", "), } .into()) } @@ -331,7 +342,7 @@ impl ServerConnection { self.request_transmitter.force_close() } - pub(crate) fn servers_all(&self) -> Result> { + pub(crate) fn servers_all(&self) -> Result> { match self.request_blocking(Request::ServersAll)? { Response::ServersAll { servers } => Ok(servers), other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()), diff --git a/rust/src/connection/message.rs b/rust/src/connection/message.rs index a710dd748f..03bf77f2d7 100644 --- a/rust/src/connection/message.rs +++ b/rust/src/connection/message.rs @@ -26,7 +26,7 @@ use typeql::pattern::{Conjunction, Statement}; use crate::{ answer::{readable_concept, ConceptMap, ConceptMapGroup, ValueGroup}, - common::{address::Address, info::DatabaseInfo, RequestID, SessionID, IID}, + common::{info::DatabaseInfo, RequestID, SessionID, IID}, concept::{ Annotation, Attribute, AttributeType, Entity, EntityType, Relation, RelationType, RoleType, SchemaException, Thing, ThingType, Transitivity, Value, ValueType, @@ -73,7 +73,7 @@ pub(super) enum Response { ConnectionOpen, ServersAll { - servers: Vec
, + servers: Vec, }, DatabasesContains { diff --git a/rust/src/connection/network/proto/message.rs b/rust/src/connection/network/proto/message.rs index 0fcd67389a..a94cc152bd 100644 --- a/rust/src/connection/network/proto/message.rs +++ b/rust/src/connection/network/proto/message.rs @@ -245,10 +245,10 @@ impl FromProto for Response { } } -impl TryFromProto for Response { - fn try_from_proto(proto: server_manager::all::Res) -> Result { - let servers = proto.servers.into_iter().map(|server| server.address.parse()).try_collect()?; - Ok(Self::ServersAll { servers }) +impl FromProto for Response { + fn from_proto(proto: server_manager::all::Res) -> Self { + let servers = proto.servers.into_iter().map(|server| server.address).collect(); + Self::ServersAll { servers } } } @@ -276,9 +276,7 @@ impl TryFromProto for Response { impl FromProto for Response { fn from_proto(proto: database_manager::all::Res) -> Self { - Self::DatabasesAll { - databases: proto.databases.into_iter().map(DatabaseInfo::from_proto).collect(), - } + Self::DatabasesAll { databases: proto.databases.into_iter().map(DatabaseInfo::from_proto).collect() } } } diff --git a/rust/src/connection/network/transmitter/rpc.rs b/rust/src/connection/network/transmitter/rpc.rs index 4231e762b5..8dc14a6337 100644 --- a/rust/src/connection/network/transmitter/rpc.rs +++ b/rust/src/connection/network/transmitter/rpc.rs @@ -121,7 +121,7 @@ impl RPCTransmitter { match request { Request::ConnectionOpen => rpc.connection_open(request.try_into_proto()?).await.map(Response::from_proto), - Request::ServersAll => rpc.servers_all(request.try_into_proto()?).await.and_then(Response::try_from_proto), + Request::ServersAll => rpc.servers_all(request.try_into_proto()?).await.map(Response::from_proto), Request::DatabasesContains { .. } => { rpc.databases_contains(request.try_into_proto()?).await.map(Response::from_proto) From c17499fbdc26c5c236b5712c6c724ddaef2c55d7 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:56:02 +0100 Subject: [PATCH 15/57] test: partial address translation no longer planned --- rust/tests/integration/network.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/tests/integration/network.rs b/rust/tests/integration/network.rs index d470129bc3..75e018c21c 100644 --- a/rust/tests/integration/network.rs +++ b/rust/tests/integration/network.rs @@ -26,7 +26,12 @@ use typedb_driver::{Connection, Credential}; #[serial] fn address_translation() { Connection::new_cloud_address_map( - [("localhost:21729", "localhost:21729")].into(), + [ + ("localhost:11729", "localhost:11729"), + ("localhost:21729", "localhost:21729"), + ("localhost:31729", "localhost:31729"), + ] + .into(), Credential::with_tls( "admin", "password", From 2baa1f9a68f008d7c1cadc04af00aa233a993d6a Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:06:33 +0100 Subject: [PATCH 16/57] reorder --- rust/src/connection/connection.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index f69ca71f6d..9cf0618b54 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -156,9 +156,9 @@ impl Connection { .try_collect()?; let translated: HashSet = server_to_address.keys().cloned().collect(); - let unmapped = &servers - &translated; let unknown = &translated - &servers; - if !unmapped.is_empty() && !unknown.is_empty() { + let unmapped = &servers - &translated; + if !unknown.is_empty() && !unmapped.is_empty() { return Err(ConnectionError::AddressTranslationMismatch { unknown, unmapped }.into()); } From 1c9079c33a94f0d372be93d821a90f150abbc4b4 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:21:19 +0100 Subject: [PATCH 17/57] logic fix --- rust/src/connection/connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 9cf0618b54..4bd52c0299 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -158,7 +158,7 @@ impl Connection { let translated: HashSet = server_to_address.keys().cloned().collect(); let unknown = &translated - &servers; let unmapped = &servers - &translated; - if !unknown.is_empty() && !unmapped.is_empty() { + if !unknown.is_empty() || !unmapped.is_empty() { return Err(ConnectionError::AddressTranslationMismatch { unknown, unmapped }.into()); } From c5e43c217fe931fafd4718552858b0ea48286966 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:21:44 +0100 Subject: [PATCH 18/57] fmt --- java/test/integration/AddressMappingTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/test/integration/AddressMappingTest.java b/java/test/integration/AddressMappingTest.java index 7e0b2cdd18..566cd72453 100644 --- a/java/test/integration/AddressMappingTest.java +++ b/java/test/integration/AddressMappingTest.java @@ -61,7 +61,8 @@ public class AddressMappingTest { public void testAllNodesMapped() { TypeDBCloudRunner typedb = TypeDBCloudRunner.create(Paths.get("."), 3, serverOptions); typedb.start(); - Map addresses = typedb.externalAddresses().stream().map(address -> pair(address, address)).collect(Collectors.toMap(Pair::first, Pair::second)); + Map addresses = typedb.externalAddresses().stream().map(address -> pair(address, address)) + .collect(Collectors.toMap(Pair::first, Pair::second)); TypeDBDriver driver = TypeDB.cloudDriver(addresses, credential); driver.databases().create("typedb"); TypeDBSession session = driver.session("typedb", TypeDBSession.Type.DATA); From 7c6c4b51be74cdf4c92feaf9083b7d2e8748b9ed Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:29:55 +0100 Subject: [PATCH 19/57] FFI: crash on length mismatch --- c/src/connection.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/c/src/connection.rs b/c/src/connection.rs index 33be4ce662..a9d3548570 100644 --- a/c/src/connection.rs +++ b/c/src/connection.rs @@ -17,8 +17,9 @@ * under the License. */ -use std::{collections::HashMap, ffi::c_char, path::Path}; +use std::{ffi::c_char, path::Path}; +use itertools::Itertools; use typedb_driver::{Connection, Credential}; use super::{ @@ -62,8 +63,7 @@ pub extern "C" fn connection_open_cloud_translated( translated_addresses: *const *const c_char, credential: *const Credential, ) -> *mut Connection { - let addresses: HashMap<&str, &str> = - string_array_view(advertised_addresses).zip(string_array_view(translated_addresses)).collect(); + let addresses = string_array_view(advertised_addresses).zip_eq(string_array_view(translated_addresses)).collect(); try_release(Connection::new_cloud_address_map(addresses, borrow(credential).clone())) } From b4128830c3c9c2eb710e27aefdd7d959347dab94 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:30:14 +0100 Subject: [PATCH 20/57] unused imports --- java/test/integration/AddressMappingTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/test/integration/AddressMappingTest.java b/java/test/integration/AddressMappingTest.java index 566cd72453..3e28f83296 100644 --- a/java/test/integration/AddressMappingTest.java +++ b/java/test/integration/AddressMappingTest.java @@ -28,8 +28,6 @@ import com.vaticle.typedb.driver.api.TypeDBSession; import com.vaticle.typedb.driver.api.TypeDBTransaction; import com.vaticle.typedb.driver.api.concept.type.EntityType; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From e53c116f3d07137451ade4e416a3f76c0fedac3a Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:31:43 +0100 Subject: [PATCH 21/57] java param name --- java/TypeDB.java | 6 +++--- java/connection/TypeDBDriverImpl.java | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/java/TypeDB.java b/java/TypeDB.java index 364a51545f..70346d6f9d 100644 --- a/java/TypeDB.java +++ b/java/TypeDB.java @@ -86,11 +86,11 @@ public static TypeDBDriver cloudDriver(Set addresses, TypeDBCredential c * TypeDB.cloudDriver(addresses, credential); * * - * @param addresses Translation map from addresses received from the TypeDB server(s) + * @param addressTranslation Translation map from addresses received from the TypeDB server(s) * to addresses to be used by the driver for connection * @param credential The credential to connect with */ - public static TypeDBDriver cloudDriver(Map addressesTranslation, TypeDBCredential credential) { - return new TypeDBDriverImpl(addressesTranslation, credential); + public static TypeDBDriver cloudDriver(Map addressTranslation, TypeDBCredential credential) { + return new TypeDBDriverImpl(addressTranslation, credential); } } diff --git a/java/connection/TypeDBDriverImpl.java b/java/connection/TypeDBDriverImpl.java index 682ad62295..278fb7b285 100644 --- a/java/connection/TypeDBDriverImpl.java +++ b/java/connection/TypeDBDriverImpl.java @@ -53,8 +53,8 @@ public TypeDBDriverImpl(Set initAddresses, TypeDBCredential credential) this(openCloud(initAddresses, credential)); } - public TypeDBDriverImpl(Map addresses, TypeDBCredential credential) throws TypeDBDriverException { - this(openCloud(addresses, credential)); + public TypeDBDriverImpl(Map addressTranslation, TypeDBCredential credential) throws TypeDBDriverException { + this(openCloud(addressTranslation, credential)); } private TypeDBDriverImpl(com.vaticle.typedb.driver.jni.Connection connection) { @@ -79,11 +79,11 @@ private static com.vaticle.typedb.driver.jni.Connection openCloud(Set in } } - private static com.vaticle.typedb.driver.jni.Connection openCloud(Map addresses, TypeDBCredential credential) { + private static com.vaticle.typedb.driver.jni.Connection openCloud(Map addressTranslation, TypeDBCredential credential) { try { List advertised = new ArrayList(); List translated = new ArrayList(); - for (Map.Entry entry: addresses.entrySet()) { + for (Map.Entry entry: addressTranslation.entrySet()) { advertised.add(entry.getKey()); translated.add(entry.getValue()); } From 0d7aab6272070b98f66399795f63d4da7462ac80 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:35:20 +0100 Subject: [PATCH 22/57] Docs --- rust/src/connection/connection.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 4bd52c0299..d17b13cd7d 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -137,6 +137,33 @@ impl Connection { Self::new_cloud_impl(server_to_address, background_runtime, credential) } + /// Creates a new TypeDB Cloud connection. + /// + /// # Arguments + /// + /// * `address_translation` -- Translation map from addresses received from the TypeDB server(s) + /// to addresses to be used by the driver for connection + /// * `credential` -- User credential and TLS encryption setting + /// + /// # Examples + /// + /// ```rust + /// Connection::new_cloud( + /// [ + /// ("typedb-cloud.ext:11729", "localhost:11729"), + /// ("typedb-cloud.ext:21729", "localhost:21729"), + /// ("typedb-cloud.ext:31729", "localhost:31729"), + /// ].into(), + /// Credential::with_tls( + /// "admin", + /// "password", + /// Some(&PathBuf::from( + /// std::env::var("ROOT_CA") + /// .expect("ROOT_CA environment variable needs to be set for cloud tests to run"), + /// )), + /// )?, + /// ) + /// ``` pub fn new_cloud_address_map(address_translation: HashMap, credential: Credential) -> Result where T: AsRef + Sync, From 51744502edc4e06c21968c81ac902c7205b3c20d Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:43:46 +0100 Subject: [PATCH 23/57] Rust integration fuller test --- rust/tests/integration/network.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/rust/tests/integration/network.rs b/rust/tests/integration/network.rs index 75e018c21c..8ebf6b5deb 100644 --- a/rust/tests/integration/network.rs +++ b/rust/tests/integration/network.rs @@ -19,13 +19,16 @@ use std::path::PathBuf; +use futures::StreamExt; use serial_test::serial; -use typedb_driver::{Connection, Credential}; +use typedb_driver::{Connection, Credential, DatabaseManager, Session, SessionType::Data, TransactionType::Write}; -#[test] +use super::common; + +#[tokio::test] #[serial] -fn address_translation() { - Connection::new_cloud_address_map( +async fn address_translation() { + let connection = Connection::new_cloud_address_map( [ ("localhost:11729", "localhost:11729"), ("localhost:21729", "localhost:21729"), @@ -42,4 +45,16 @@ fn address_translation() { .unwrap(), ) .unwrap(); + + common::create_test_database_with_schema(connection.clone(), "define person sub entity;").await.unwrap(); + let databases = DatabaseManager::new(connection); + assert!(databases.contains(common::TEST_DATABASE).await.unwrap()); + + let session = Session::new(databases.get(common::TEST_DATABASE).await.unwrap(), Data).await.unwrap(); + let transaction = session.transaction(Write).await.unwrap(); + let answer_stream = transaction.query().get("match $x sub thing; get;").unwrap(); + let results: Vec<_> = answer_stream.collect().await; + transaction.commit().await.unwrap(); + assert_eq!(results.len(), 5); + assert!(results.into_iter().all(|res| res.is_ok())); } From 3547e471f0b8a7ef759bff2d6bc51659675ae4b0 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:45:26 +0100 Subject: [PATCH 24/57] FFI docs --- c/src/connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/src/connection.rs b/c/src/connection.rs index a9d3548570..ac72b2034d 100644 --- a/c/src/connection.rs +++ b/c/src/connection.rs @@ -49,7 +49,7 @@ pub extern "C" fn connection_open_cloud( try_release(Connection::new_cloud(&addresses, borrow(credential).clone())) } -/// Open a TypeDB Driver to TypeDB Cloud server(s) available at the provided addresses, using +/// Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with /// the provided credential. /// /// @param advertised_addresses a null-terminated array holding the address(es) the TypeDB server(s) From 20237e62f91051c012d8ed0246c0cf58bd0731d1 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:53:15 +0100 Subject: [PATCH 25/57] fmt --- rust/src/database/session.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rust/src/database/session.rs b/rust/src/database/session.rs index 4f6f41bd6f..60093bfe14 100644 --- a/rust/src/database/session.rs +++ b/rust/src/database/session.rs @@ -34,9 +34,7 @@ type Callback = Box; /// A session with a TypeDB database. pub struct Session { database: Database, - server_session: RwLock, - session_type: SessionType, is_open: Arc>, on_close: Arc>>, From c74d0fdd42306391fe2bb91955474bf0e8b1bc9d Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:28:53 +0100 Subject: [PATCH 26/57] new_cloud_address_map => new_cloud_with_translation --- c/src/connection.rs | 2 +- java/test/integration/AddressMappingTest.java | 1 - rust/src/connection/connection.rs | 4 ++-- rust/tests/integration/network.rs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/c/src/connection.rs b/c/src/connection.rs index ac72b2034d..cacef15a4c 100644 --- a/c/src/connection.rs +++ b/c/src/connection.rs @@ -64,7 +64,7 @@ pub extern "C" fn connection_open_cloud_translated( credential: *const Credential, ) -> *mut Connection { let addresses = string_array_view(advertised_addresses).zip_eq(string_array_view(translated_addresses)).collect(); - try_release(Connection::new_cloud_address_map(addresses, borrow(credential).clone())) + try_release(Connection::new_cloud_with_translation(addresses, borrow(credential).clone())) } /// Closes the driver. Before instantiating a new driver, the driver that’s currently open should first be closed. diff --git a/java/test/integration/AddressMappingTest.java b/java/test/integration/AddressMappingTest.java index 3e28f83296..1d9d02d12b 100644 --- a/java/test/integration/AddressMappingTest.java +++ b/java/test/integration/AddressMappingTest.java @@ -24,7 +24,6 @@ import com.vaticle.typedb.driver.TypeDB; import com.vaticle.typedb.driver.api.TypeDBCredential; import com.vaticle.typedb.driver.api.TypeDBDriver; -import com.vaticle.typedb.driver.api.TypeDBOptions; import com.vaticle.typedb.driver.api.TypeDBSession; import com.vaticle.typedb.driver.api.TypeDBTransaction; import com.vaticle.typedb.driver.api.concept.type.EntityType; diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index d17b13cd7d..ae1d985623 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -148,7 +148,7 @@ impl Connection { /// # Examples /// /// ```rust - /// Connection::new_cloud( + /// Connection::new_cloud_with_translation( /// [ /// ("typedb-cloud.ext:11729", "localhost:11729"), /// ("typedb-cloud.ext:21729", "localhost:21729"), @@ -164,7 +164,7 @@ impl Connection { /// )?, /// ) /// ``` - pub fn new_cloud_address_map(address_translation: HashMap, credential: Credential) -> Result + pub fn new_cloud_with_translation(address_translation: HashMap, credential: Credential) -> Result where T: AsRef + Sync, U: AsRef + Sync, diff --git a/rust/tests/integration/network.rs b/rust/tests/integration/network.rs index 8ebf6b5deb..250e4199ba 100644 --- a/rust/tests/integration/network.rs +++ b/rust/tests/integration/network.rs @@ -28,7 +28,7 @@ use super::common; #[tokio::test] #[serial] async fn address_translation() { - let connection = Connection::new_cloud_address_map( + let connection = Connection::new_cloud_with_translation( [ ("localhost:11729", "localhost:11729"), ("localhost:21729", "localhost:21729"), From ab957ebbbe0264d37bad08e2c949fd47a744a135 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:31:16 +0100 Subject: [PATCH 27/57] error message --- rust/src/common/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index c77bd9db8a..a7b5c99bfa 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -73,7 +73,7 @@ error_messages! { ConnectionError MissingPort { address: String } = 23: "Invalid URL '{address}': missing port.", AddressTranslationMismatch { unknown: HashSet, unmapped: HashSet } = - 24: "Address translation map does not match the advertised server list. Mapped servers not known to the remote server: {unknown:?}. Servers not mapped to addresses: {unmapped:?}.", + 24: "Address translation map does not match the server's advertised address list. User-provided servers not in the advertised list: {unknown:?}. Advertised servers not mapped by user: {unmapped:?}.", } error_messages! { InternalError From d18658dad7dc401202b62ea29125cf975081ed1d Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:55:36 +0100 Subject: [PATCH 28/57] replica_info_{get_address => get_server_id} --- c/src/database.rs | 4 ++-- c/swig/typedb_driver_csharp.swg | 2 +- c/swig/typedb_driver_java.swg | 2 +- c/typedb_driver.i | 2 +- cpp/lib/database/database.cpp | 2 +- csharp/Connection/TypeDBDatabase.cs | 2 +- java/connection/TypeDBDatabaseImpl.java | 4 ++-- python/typedb/connection/database.py | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/c/src/database.rs b/c/src/database.rs index 81c0a62039..48f1166bd5 100644 --- a/c/src/database.rs +++ b/c/src/database.rs @@ -107,9 +107,9 @@ pub extern "C" fn replica_info_drop(replica_info: *mut ReplicaInfo) { free(replica_info); } -/// Retrieves the address of the server hosting this replica +/// Retrieves the ID of the server hosting this replica #[no_mangle] -pub extern "C" fn replica_info_get_address(replica_info: *const ReplicaInfo) -> *mut c_char { +pub extern "C" fn replica_info_get_server_id(replica_info: *const ReplicaInfo) -> *mut c_char { release_string(borrow(replica_info).server_id.to_string()) } diff --git a/c/swig/typedb_driver_csharp.swg b/c/swig/typedb_driver_csharp.swg index 5957f1b90c..55959ecb17 100644 --- a/c/swig/typedb_driver_csharp.swg +++ b/c/swig/typedb_driver_csharp.swg @@ -191,7 +191,7 @@ %noexception user_get_username; %noexception user_get_password_expiry_seconds; -%noexception replica_info_get_address; +%noexception replica_info_get_server_id; %noexception replica_info_is_primary; %noexception replica_info_is_preferred; %noexception replica_info_get_term; diff --git a/c/swig/typedb_driver_java.swg b/c/swig/typedb_driver_java.swg index 0229b33011..38b2ce53df 100644 --- a/c/swig/typedb_driver_java.swg +++ b/c/swig/typedb_driver_java.swg @@ -136,7 +136,7 @@ %nojavaexception user_get_username; %nojavaexception user_get_password_expiry_seconds; -%nojavaexception replica_info_get_address; +%nojavaexception replica_info_get_server_id; %nojavaexception replica_info_is_primary; %nojavaexception replica_info_is_preferred; %nojavaexception replica_info_get_term; diff --git a/c/typedb_driver.i b/c/typedb_driver.i index 79223eb7c1..d1c8f577b7 100644 --- a/c/typedb_driver.i +++ b/c/typedb_driver.i @@ -358,7 +358,7 @@ void transaction_on_close_register(const Transaction* transaction, TransactionCa %newobject database_get_primary_replica_info; %newobject database_get_replicas_info; -%newobject replica_info_get_address; +%newobject replica_info_get_server_id; %newobject replica_info_iterator_next; %newobject databases_all; diff --git a/cpp/lib/database/database.cpp b/cpp/lib/database/database.cpp index 045c5526be..ae30042247 100644 --- a/cpp/lib/database/database.cpp +++ b/cpp/lib/database/database.cpp @@ -33,7 +33,7 @@ ReplicaInfo::ReplicaInfo(_native::ReplicaInfo* replicaInfoNative) std::string ReplicaInfo::address() { CHECK_NATIVE(replicaInfoNative); - return Utils::stringFromNative(_native::replica_info_get_address(replicaInfoNative.get())); + return Utils::stringFromNative(_native::replica_info_get_server_id(replicaInfoNative.get())); } bool ReplicaInfo::isPrimary() { diff --git a/csharp/Connection/TypeDBDatabase.cs b/csharp/Connection/TypeDBDatabase.cs index 44a9dabd07..dae181f719 100644 --- a/csharp/Connection/TypeDBDatabase.cs +++ b/csharp/Connection/TypeDBDatabase.cs @@ -188,7 +188,7 @@ public bool IsPreferred() public string Address { - get { return _address ?? (_address = Pinvoke.typedb_driver.replica_info_get_address(NativeObject)); } + get { return _address ?? (_address = Pinvoke.typedb_driver.replica_info_get_server_id(NativeObject)); } } public long Term diff --git a/java/connection/TypeDBDatabaseImpl.java b/java/connection/TypeDBDatabaseImpl.java index 61fd7fd16a..9bc8281353 100644 --- a/java/connection/TypeDBDatabaseImpl.java +++ b/java/connection/TypeDBDatabaseImpl.java @@ -37,7 +37,7 @@ import static com.vaticle.typedb.driver.jni.typedb_driver.database_rule_schema; import static com.vaticle.typedb.driver.jni.typedb_driver.database_schema; import static com.vaticle.typedb.driver.jni.typedb_driver.database_type_schema; -import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_get_address; +import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_get_server_id; import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_get_term; import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_is_preferred; import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_is_primary; @@ -128,7 +128,7 @@ public static class Replica extends NativeObject Database: pass def address(self) -> str: - return replica_info_get_address(self._info) + return replica_info_get_server_id(self._info) def is_primary(self) -> bool: return replica_info_is_primary(self._info) From 2da9a72b7226b231b6e98dd9f0383b8558938d5f Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:57:28 +0100 Subject: [PATCH 29/57] UnknownAddress => UnknownServer, + handle in replica --- rust/src/common/error.rs | 4 ++-- rust/src/connection/connection.rs | 10 ++++------ rust/src/database/database.rs | 7 +++++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index a7b5c99bfa..498015e8c0 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -86,8 +86,8 @@ error_messages! { InternalError 3: "Unexpected request type for remote procedure call: {request_type}.", UnexpectedResponseType { response_type: String } = 4: "Unexpected response type for remote procedure call: {response_type}.", - UnknownConnectionAddress { address: String } = - 5: "Received unrecognized address from the server: {address}.", + UnknownServer { server_id: String } = + 5: "Received replica at unrecognized server: {server_id}.", EnumOutOfBounds { value: i32, enum_name: &'static str } = 6: "Value '{value}' is out of bounds for enum '{enum_name}'.", } diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index ae1d985623..0ead819d68 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -150,8 +150,8 @@ impl Connection { /// ```rust /// Connection::new_cloud_with_translation( /// [ - /// ("typedb-cloud.ext:11729", "localhost:11729"), - /// ("typedb-cloud.ext:21729", "localhost:21729"), + /// ("typedb-cloud.ext:11729", "localhost:11729"), + /// ("typedb-cloud.ext:21729", "localhost:21729"), /// ("typedb-cloud.ext:31729", "localhost:31729"), /// ].into(), /// Credential::with_tls( @@ -294,10 +294,8 @@ impl Connection { self.server_connections.keys().map(String::as_str) } - pub(crate) fn connection(&self, id: &str) -> Result<&ServerConnection> { - self.server_connections - .get(id) - .ok_or_else(|| InternalError::UnknownConnectionAddress { address: id.to_owned() }.into()) + pub(crate) fn connection(&self, id: &str) -> Option<&ServerConnection> { + self.server_connections.get(id) } pub(crate) fn connections(&self) -> impl Iterator + '_ { diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index 6094125967..37f79df6f6 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -30,6 +30,7 @@ use crate::{ Error, Result, }, connection::ServerConnection, + error::InternalError, Connection, }; @@ -283,8 +284,10 @@ impl Replica { .replicas .into_iter() .map(|replica| { - let server_connection = connection.connection(&replica.server_id)?.clone(); - Ok(Self::new(database_info.name.clone(), replica, server_connection)) + let server_connection = connection + .connection(&replica.server_id) + .ok_or_else(|| InternalError::UnknownServer { server_id: replica.server_id.clone() })?; + Ok(Self::new(database_info.name.clone(), replica, server_connection.clone())) }) .collect() } From a13c8ef9a866b2693d4abf9f168144fa2aacdaee Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:59:18 +0100 Subject: [PATCH 30/57] replica docs --- rust/src/common/info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/common/info.rs b/rust/src/common/info.rs index 9f17fc589e..299ce8af03 100644 --- a/rust/src/common/info.rs +++ b/rust/src/common/info.rs @@ -39,7 +39,7 @@ pub(crate) struct DatabaseInfo { /// The metadata and state of an individual raft replica of a database. #[derive(Debug)] pub struct ReplicaInfo { - /// The address of the server hosting this replica + /// The server hosting this replica pub server_id: String, /// Whether this is the primary replica of the raft cluster. pub is_primary: bool, From 550d55299000885f37fe9466bcdd0c87b81b4948 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:03:02 +0100 Subject: [PATCH 31/57] Java: address => serverID --- java/api/database/Database.java | 4 ++-- java/connection/TypeDBDatabaseImpl.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/api/database/Database.java b/java/api/database/Database.java index 542d9039e0..43756717b7 100644 --- a/java/api/database/Database.java +++ b/java/api/database/Database.java @@ -116,10 +116,10 @@ public interface Database { interface Replica { /** - * Retrieves the address of the server hosting this replica + * Retrieves the server hosting this replica */ @CheckReturnValue - String address(); + String serverID(); /** * Checks whether this is the primary replica of the raft cluster. diff --git a/java/connection/TypeDBDatabaseImpl.java b/java/connection/TypeDBDatabaseImpl.java index 9bc8281353..3a1f6b7983 100644 --- a/java/connection/TypeDBDatabaseImpl.java +++ b/java/connection/TypeDBDatabaseImpl.java @@ -127,7 +127,7 @@ public static class Replica extends NativeObject Date: Tue, 9 Apr 2024 18:20:17 +0100 Subject: [PATCH 32/57] docs + var names --- cpp/include/typedb/connection/driver.hpp | 21 +++++++++++++++++-- cpp/lib/connection/driver.cpp | 26 ++++++++++++++++++++---- java/TypeDB.java | 2 +- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/cpp/include/typedb/connection/driver.hpp b/cpp/include/typedb/connection/driver.hpp index 79f93074b0..7b5dce3022 100644 --- a/cpp/include/typedb/connection/driver.hpp +++ b/cpp/include/typedb/connection/driver.hpp @@ -24,6 +24,8 @@ #include "typedb/user/user_manager.hpp" #include +#include +#include // The namespace comment is needed to document enums. /** @@ -72,7 +74,7 @@ class Driver { * * @param address The address of the TypeDB server */ - static Driver coreDriver(const std::string& coreAddress); + static Driver coreDriver(const std::string& address); /** * Open a TypeDB Driver to TypeDB Cloud server(s) available at the provided addresses, using @@ -86,7 +88,22 @@ class Driver { * @param addresses The address(es) of the TypeDB server(s) * @param credential The Credential to connect with */ - static Driver cloudDriver(const std::vector& cloudAddresses, const Credential& credential); + static Driver cloudDriver(const std::vector& addresses, const Credential& credential); + + /** + * Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with + * the provided credential. + * + *

Examples

+ *
+     * TypeDB.cloudDriver(addresses, credential);
+     * 
+ * + * @param addressTranslation Translation map from addresses received from the TypeDB server(s) + * to addresses to be used by the driver for connection + * @param credential The credential to connect with + */ + static Driver cloudDriver(const std::unordered_map& addressTranslation, const Credential& credential); Driver(const Driver&) = delete; Driver(Driver&& from) = default; diff --git a/cpp/lib/connection/driver.cpp b/cpp/lib/connection/driver.cpp index fb3629c935..4601386830 100644 --- a/cpp/lib/connection/driver.cpp +++ b/cpp/lib/connection/driver.cpp @@ -30,15 +30,15 @@ void Driver::initLogging() { _native::init_logging(); } -Driver Driver::coreDriver(const std::string& coreAddress) { - auto p = _native::connection_open_core(coreAddress.c_str()); +Driver Driver::coreDriver(const std::string& address) { + auto p = _native::connection_open_core(address.c_str()); DriverException::check_and_throw(); return Driver(p); } -Driver Driver::cloudDriver(const std::vector& cloudAddresses, const Credential& credential) { +Driver Driver::cloudDriver(const std::vector& addresses, const Credential& credential) { std::vector addressesNative; - for (auto& addr : cloudAddresses) + for (auto& addr : addresses) addressesNative.push_back(addr.c_str()); addressesNative.push_back(nullptr); auto p = _native::connection_open_cloud(addressesNative.data(), credential.getNative()); @@ -46,6 +46,24 @@ Driver Driver::cloudDriver(const std::vector& cloudAddresses, const return Driver(p); } +Driver Driver::cloudDriver(const std::unordered_map& addressTranslation, const Credential& credential) { + std::vector advertisedAddressesNative; + std::vector translatedAddressesNative; + for (auto& [advertised, translated] : addressTranslation) { + advertisedAddressesNative.push_back(advertised.c_str()); + translatedAddressesNative.push_back(translated.c_str()); + } + advertisedAddressesNative.push_back(nullptr); + translatedAddressesNative.push_back(nullptr); + auto p = _native::connection_open_cloud_translated( + advertisedAddressesNative.data(), + translatedAddressesNative.data(), + credential.getNative() + ); + DriverException::check_and_throw(); + return Driver(p); +} + Driver::Driver(_native::Connection* conn) noexcept : connectionNative(conn, _native::connection_close), databases(this->connectionNative.get()), diff --git a/java/TypeDB.java b/java/TypeDB.java index 70346d6f9d..a0714e333d 100644 --- a/java/TypeDB.java +++ b/java/TypeDB.java @@ -78,7 +78,7 @@ public static TypeDBDriver cloudDriver(Set addresses, TypeDBCredential c } /** - * Open a TypeDB Driver to TypeDB Cloud server(s) available at the provided addresses, using + * Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with * the provided credential. * *

Examples

From 9c51f06f6b969001b1d62e63fb0e185db6f0915c Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:21:33 +0100 Subject: [PATCH 33/57] C++: address => serverID --- cpp/include/typedb/database/database.hpp | 4 ++-- cpp/lib/database/database.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/include/typedb/database/database.hpp b/cpp/include/typedb/database/database.hpp index da00e24413..1b2cb00f26 100644 --- a/cpp/include/typedb/database/database.hpp +++ b/cpp/include/typedb/database/database.hpp @@ -39,9 +39,9 @@ class ReplicaInfo { ReplicaInfo& operator=(ReplicaInfo&&) = default; /** - * Retrieves the address of the server hosting this replica + * Retrieves the server hosting this replica */ - std::string address(); + std::string serverID(); /** * Checks whether this is the primary replica of the raft cluster. diff --git a/cpp/lib/database/database.cpp b/cpp/lib/database/database.cpp index ae30042247..e0acff03cc 100644 --- a/cpp/lib/database/database.cpp +++ b/cpp/lib/database/database.cpp @@ -31,7 +31,7 @@ namespace TypeDB { ReplicaInfo::ReplicaInfo(_native::ReplicaInfo* replicaInfoNative) : replicaInfoNative(replicaInfoNative, _native::replica_info_drop) {} -std::string ReplicaInfo::address() { +std::string ReplicaInfo::serverID() { CHECK_NATIVE(replicaInfoNative); return Utils::stringFromNative(_native::replica_info_get_server_id(replicaInfoNative.get())); } From 55a4c2667b20cd951578dd900538b07601aa1b63 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:27:28 +0100 Subject: [PATCH 34/57] C#: address => serverID --- csharp/Api/Database/IDatabase.cs | 2 +- csharp/Connection/TypeDBDatabase.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/Api/Database/IDatabase.cs b/csharp/Api/Database/IDatabase.cs index 1a3d8b86fc..7221095bc8 100644 --- a/csharp/Api/Database/IDatabase.cs +++ b/csharp/Api/Database/IDatabase.cs @@ -114,7 +114,7 @@ public interface IReplica /** * Retrieves the address of the server hosting this replica. */ - public string Address { get; } + public string ServerID { get; } /** * The raft protocol ‘term’ of this replica. diff --git a/csharp/Connection/TypeDBDatabase.cs b/csharp/Connection/TypeDBDatabase.cs index dae181f719..6f9a7846d7 100644 --- a/csharp/Connection/TypeDBDatabase.cs +++ b/csharp/Connection/TypeDBDatabase.cs @@ -169,7 +169,7 @@ public override string ToString() public class Replica : NativeObjectWrapper, IDatabase.IReplica { - private string? _address; + private string? _serverID; private long? _term; public Replica(Pinvoke.ReplicaInfo replicaInfo) @@ -186,9 +186,9 @@ public bool IsPreferred() return Pinvoke.typedb_driver.replica_info_is_preferred(NativeObject); } - public string Address + public string ServerID { - get { return _address ?? (_address = Pinvoke.typedb_driver.replica_info_get_server_id(NativeObject)); } + get { return _serverID ?? (_serverID = Pinvoke.typedb_driver.replica_info_get_server_id(NativeObject)); } } public long Term From 90f845f5a4fc6ac60851727de75e04448c274dc3 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:36:05 +0100 Subject: [PATCH 35/57] C# address translation --- csharp/Connection/TypeDBDriver.cs | 25 +++++++++++++++++++++++++ csharp/Drivers.cs | 18 ++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/csharp/Connection/TypeDBDriver.cs b/csharp/Connection/TypeDBDriver.cs index d23c7cd920..acd44a947c 100644 --- a/csharp/Connection/TypeDBDriver.cs +++ b/csharp/Connection/TypeDBDriver.cs @@ -41,6 +41,10 @@ public TypeDBDriver(ICollection initAddresses, TypeDBCredential credenti : this(OpenCloud(initAddresses, credential)) {} + public TypeDBDriver(IDictionary addressTranslation, TypeDBCredential credential) + : this(OpenCloud(addressTranslation, credential)) + {} + private TypeDBDriver(Pinvoke.Connection connection) : base(connection) { @@ -72,6 +76,27 @@ private static Pinvoke.Connection OpenCloud(ICollection initAddresses, T } } + private static Pinvoke.Connection OpenCloud(IDictionary addressTranslation, TypeDBCredential credential) + { + try + { + string[] advertisedAddresses = new string[addressTranslation.Count]; + string[] translatedAddresses = new string[addressTranslation.Count]; + int index = 0; + foreach (KeyValuePair translation in addressTranslation) + { + advertisedAddresses[index] = translation.Key; + translatedAddresses[index] = translation.Value; + index++; + } + return Pinvoke.typedb_driver.connection_open_cloud_translated(advertisedAddresses, translatedAddresses, credential.NativeObject); + } + catch (Pinvoke.Error e) + { + throw new TypeDBDriverException(e); + } + } + public bool IsOpen() { return Pinvoke.typedb_driver.connection_is_open(NativeObject); diff --git a/csharp/Drivers.cs b/csharp/Drivers.cs index eb26c215ff..5dc38d8782 100644 --- a/csharp/Drivers.cs +++ b/csharp/Drivers.cs @@ -77,5 +77,23 @@ public static ITypeDBDriver CloudDriver(ICollection addresses, TypeDBCre { return new TypeDBDriver(addresses, credential); } + + /** + * Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with + * the provided credential. + * + *

Examples

+ *
+         * TypeDB.cloudDriver(addressTranslation, credential);
+         * 
+ * + * @param addressTranslation Translation map from addresses received from the TypeDB server(s) + * to addresses to be used by the driver for connection + * @param credential The credential to connect with + */ + public static ITypeDBDriver CloudDriver(IDictionary addressTranslation, TypeDBCredential credential) + { + return new TypeDBDriver(addressTranslation, credential); + } } } From 0dc7873c92f39a0175b184ca3742794cb0b89e59 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:25:03 +0100 Subject: [PATCH 36/57] C#: address translation test --- .../Network/AddressTranslationTest.cs | 76 +++++++++++++++++++ csharp/Test/Integration/Network/BUILD | 39 ++++++++++ 2 files changed, 115 insertions(+) create mode 100644 csharp/Test/Integration/Network/AddressTranslationTest.cs create mode 100644 csharp/Test/Integration/Network/BUILD diff --git a/csharp/Test/Integration/Network/AddressTranslationTest.cs b/csharp/Test/Integration/Network/AddressTranslationTest.cs new file mode 100644 index 0000000000..4ccda1c3de --- /dev/null +++ b/csharp/Test/Integration/Network/AddressTranslationTest.cs @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + +using TypeDB.Driver; +using TypeDB.Driver.Api; +using TypeDB.Driver.Common; +using static TypeDB.Driver.Api.IThingType; +using static TypeDB.Driver.Api.IThingType.Annotation; + +namespace TypeDB.Driver.Test.Integration +{ + [TestFixture] + public class AddressTranslationTestFixture + { + [Test] + public void TestCloudConnectionWithTranslation() + { + try + { + IDictionary addressTranslation = new Dictionary() { + {"localhost:11729", "localhost:11729"}, + {"localhost:21729", "localhost:21729"}, + {"localhost:31729", "localhost:31729"}, + }; + + TypeDBCredential connectCredential = new TypeDBCredential( + "admin", + "password", + Environment.GetEnvironmentVariable("ROOT_CA")!); + + using (ITypeDBDriver driver = Drivers.CloudDriver(addressTranslation, connectCredential)) + { + driver.Databases.Create("typedb"); + using (ITypeDBSession session = driver.Session("typedb", SessionType.Data)) + { + using (ITypeDBTransaction transaction = session.Transaction(TransactionType.Write)) + { + IEntityType root = transaction.Concepts.RootEntityType; + Assert.IsNotNull(root); + Assert.AreEqual(1, root.GetSubtypes(transaction).Count()); + } + } + driver.Databases.Get("typedb").Delete(); + } + } + catch (TypeDBDriverException e) + { + Console.WriteLine($"Caught TypeDB Driver Exception: {e}"); + // ... + Assert.Fail("You can handle exceptions here. However, we do not expect exceptions in CI, so we fail."); + } + } + } +} + diff --git a/csharp/Test/Integration/Network/BUILD b/csharp/Test/Integration/Network/BUILD new file mode 100644 index 0000000000..4532438cab --- /dev/null +++ b/csharp/Test/Integration/Network/BUILD @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +load("//csharp:build_opts.bzl", "nullable_context", "target_frameworks", "targeting_packs") +load("//csharp/Test:rules.bzl", "csharp_integration_test") +load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") + +csharp_integration_test( + name = "address-translation-cloud", + srcs = ["AddressTranslationTest.cs"], + deps = [ + "//csharp:driver-csharp", + "//csharp/Api:api", + "//csharp/Common:common", + ], + target_frameworks = target_frameworks, + targeting_packs = targeting_packs, +) + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache-header", +) + From 099f711a93fe05038ee4a286feb33c313ee02f7d Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:25:22 +0100 Subject: [PATCH 37/57] Java: doc fix --- java/TypeDB.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/TypeDB.java b/java/TypeDB.java index a0714e333d..7fc7511225 100644 --- a/java/TypeDB.java +++ b/java/TypeDB.java @@ -83,7 +83,7 @@ public static TypeDBDriver cloudDriver(Set addresses, TypeDBCredential c * *

Examples

*
-     * TypeDB.cloudDriver(addresses, credential);
+     * TypeDB.cloudDriver(addressTranslation, credential);
      * 
* * @param addressTranslation Translation map from addresses received from the TypeDB server(s) From 94137ae99fdfbce55f7c1d6e76724ca6ba8cb2d9 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:27:05 +0100 Subject: [PATCH 38/57] Address{Mapping => Translation}Test --- ...{AddressMappingTest.java => AddressTranslationTest.java} | 6 +++--- java/test/integration/BUILD | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename java/test/integration/{AddressMappingTest.java => AddressTranslationTest.java} (96%) diff --git a/java/test/integration/AddressMappingTest.java b/java/test/integration/AddressTranslationTest.java similarity index 96% rename from java/test/integration/AddressMappingTest.java rename to java/test/integration/AddressTranslationTest.java index 1d9d02d12b..f25da6bbae 100644 --- a/java/test/integration/AddressMappingTest.java +++ b/java/test/integration/AddressTranslationTest.java @@ -44,8 +44,8 @@ import static org.junit.Assert.fail; @SuppressWarnings("Duplicates") -public class AddressMappingTest { - private static final Logger LOG = LoggerFactory.getLogger(AddressMappingTest.class); +public class AddressTranslationTest { + private static final Logger LOG = LoggerFactory.getLogger(AddressTranslationTest.class); private static final Map serverOptions = map( pair("--diagnostics.reporting.errors", "false"), @@ -55,7 +55,7 @@ public class AddressMappingTest { private static final TypeDBCredential credential = new TypeDBCredential("admin", "password", false); @Test - public void testAllNodesMapped() { + public void testAddressTranslation() { TypeDBCloudRunner typedb = TypeDBCloudRunner.create(Paths.get("."), 3, serverOptions); typedb.start(); Map addresses = typedb.externalAddresses().stream().map(address -> pair(address, address)) diff --git a/java/test/integration/BUILD b/java/test/integration/BUILD index 49b5562940..14f6b44e56 100644 --- a/java/test/integration/BUILD +++ b/java/test/integration/BUILD @@ -45,8 +45,8 @@ typedb_java_test( ) typedb_java_test( - name = "test-address-mapping", - srcs = ["AddressMappingTest.java"], + name = "test-address-translation", + srcs = ["AddressTranslationTest.java"], server_artifacts = { "@vaticle_bazel_distribution//platform:is_linux_arm64": "@vaticle_typedb_cloud_artifact_linux-arm64//file", "@vaticle_bazel_distribution//platform:is_linux_x86_64": "@vaticle_typedb_cloud_artifact_linux-x86_64//file", @@ -54,7 +54,7 @@ typedb_java_test( "@vaticle_bazel_distribution//platform:is_mac_x86_64": "@vaticle_typedb_cloud_artifact_mac-x86_64//file", "@vaticle_bazel_distribution//platform:is_windows_x86_64": "@vaticle_typedb_cloud_artifact_windows-x86_64//file", }, - test_class = "com.vaticle.typedb.driver.test.integration.AddressMappingTest", + test_class = "com.vaticle.typedb.driver.test.integration.AddressTranslationTest", deps = [ # Internal dependencies "//java:driver-java", From f19c7a6c8f57aa62a702740af1cd64001395ac09 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 15:29:20 +0100 Subject: [PATCH 39/57] C#: remove example usage messages --- csharp/Test/Integration/Network/AddressTranslationTest.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/csharp/Test/Integration/Network/AddressTranslationTest.cs b/csharp/Test/Integration/Network/AddressTranslationTest.cs index 4ccda1c3de..da73708a09 100644 --- a/csharp/Test/Integration/Network/AddressTranslationTest.cs +++ b/csharp/Test/Integration/Network/AddressTranslationTest.cs @@ -41,7 +41,7 @@ public void TestCloudConnectionWithTranslation() IDictionary addressTranslation = new Dictionary() { {"localhost:11729", "localhost:11729"}, {"localhost:21729", "localhost:21729"}, - {"localhost:31729", "localhost:31729"}, + {"localhost:1729", "localhost:31729"}, }; TypeDBCredential connectCredential = new TypeDBCredential( @@ -67,8 +67,7 @@ public void TestCloudConnectionWithTranslation() catch (TypeDBDriverException e) { Console.WriteLine($"Caught TypeDB Driver Exception: {e}"); - // ... - Assert.Fail("You can handle exceptions here. However, we do not expect exceptions in CI, so we fail."); + Assert.Fail(); } } } From 883ee39ba54a4283917e91e1942ca687e9b041f9 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 15:29:29 +0100 Subject: [PATCH 40/57] Python: impl address translation --- python/tests/integration/test_connection.py | 19 +++++++++++++++++++ python/typedb/connection/driver.py | 14 ++++++++++---- python/typedb/driver.py | 7 +++++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/python/tests/integration/test_connection.py b/python/tests/integration/test_connection.py index bf9720ca17..0ddff99154 100644 --- a/python/tests/integration/test_connection.py +++ b/python/tests/integration/test_connection.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +import os import unittest from unittest import TestCase @@ -22,6 +23,10 @@ from typedb.driver import * +TYPEDB = "typedb" +DATA = SessionType.DATA +WRITE = TransactionType.WRITE + class TestDebug(TestCase): @@ -29,6 +34,20 @@ def test_missing_port(self): assert_that(calling(lambda: TypeDB.core_driver("localhost")), raises(TypeDBDriverException)) + def test_address_translation(self): + address_translation = { + "localhost:11729": "localhost:11729", + "localhost:21729": "localhost:21729", + "localhost:31729": "localhost:31729" + } + credential = TypeDBCredential("admin", "password", tls_enabled=True, tls_root_ca_path=os.environ["ROOT_CA"]) + with TypeDB.cloud_driver(address_translation, credential) as driver: + if TYPEDB not in [db.name for db in driver.databases.all()]: + driver.databases.create(TYPEDB) + with driver.session(TYPEDB, DATA) as session, session.transaction(WRITE) as tx: + root = tx.concepts.get_root_entity_type() + assert_that(len(list(root.get_subtypes(tx))), equal_to(1)) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/python/typedb/connection/driver.py b/python/typedb/connection/driver.py index f3ae5e243f..bbaa52eb98 100644 --- a/python/typedb/connection/driver.py +++ b/python/typedb/connection/driver.py @@ -19,8 +19,8 @@ from typing import Optional, TYPE_CHECKING -from typedb.native_driver_wrapper import connection_open_core, connection_open_cloud, connection_is_open, \ - connection_force_close, Connection as NativeConnection, TypeDBDriverExceptionNative +from typedb.native_driver_wrapper import connection_open_core, connection_open_cloud, connection_open_cloud_translated, \ + connection_is_open, connection_force_close, Connection as NativeConnection, TypeDBDriverExceptionNative from typedb.api.connection.driver import TypeDBDriver from typedb.api.connection.options import TypeDBOptions @@ -38,10 +38,16 @@ class _Driver(TypeDBDriver, NativeWrapper[NativeConnection]): - def __init__(self, addresses: list[str], credential: Optional[TypeDBCredential] = None): + def __init__(self, addresses: list[str] | dict[str], credential: Optional[TypeDBCredential] = None): if credential: try: - native_connection = connection_open_cloud(addresses, credential.native_object) + if isinstance(addresses, list): + native_connection = connection_open_cloud(addresses, credential.native_object) + else: + advertised_addresses = list(addresses.keys()) + translated_addresses = [addresses[advertised] for advertised in advertised_addresses] + native_connection = connection_open_cloud_translated( + advertised_addresses, translated_addresses, credential.native_object) except TypeDBDriverExceptionNative as e: raise TypeDBDriverException.of(e) else: diff --git a/python/typedb/driver.py b/python/typedb/driver.py index 1acaaf5ff5..45a00413f1 100644 --- a/python/typedb/driver.py +++ b/python/typedb/driver.py @@ -15,7 +15,8 @@ # specific language governing permissions and limitations # under the License. -from typing import Iterable, Union +from collections.abc import Iterable, Mapping +from typing import Union from typedb.api.answer.concept_map import * # noqa # pylint: disable=unused-import from typedb.api.answer.concept_map_group import * # noqa # pylint: disable=unused-import @@ -67,7 +68,7 @@ def core_driver(address: str) -> TypeDBDriver: return _Driver([address]) @staticmethod - def cloud_driver(addresses: Union[Iterable[str], str], credential: TypeDBCredential) -> TypeDBDriver: + def cloud_driver(addresses: Union[Mapping[str, str], Iterable[str], str], credential: TypeDBCredential) -> TypeDBDriver: """ Creates a connection to TypeDB Cloud, authenticating with the provided credentials. @@ -77,5 +78,7 @@ def cloud_driver(addresses: Union[Iterable[str], str], credential: TypeDBCredent """ if isinstance(addresses, str): return _Driver([addresses], credential) + elif isinstance(addresses, Mapping): + return _Driver(dict(addresses), credential) else: return _Driver(list(addresses), credential) From 4ee0cc5fb658c16318c5b199c1268614f36f1ce9 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:10:19 +0100 Subject: [PATCH 41/57] C++: test address translation --- cpp/test/integration/BUILD | 15 +++++++++ cpp/test/integration/test_cloud.cpp | 52 +++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 cpp/test/integration/test_cloud.cpp diff --git a/cpp/test/integration/BUILD b/cpp/test/integration/BUILD index 055ca63dc7..4cfdeb5c11 100644 --- a/cpp/test/integration/BUILD +++ b/cpp/test/integration/BUILD @@ -34,6 +34,21 @@ cc_test( linkstatic = True ) +cc_test( + name = "test-cpp-driver-cloud", + srcs = ["test_cloud.cpp"], + deps = [ + "//cpp:typedb-driver-cpp-import", + # External + "@gtest//:gtest", + "@gtest//:gtest_main" + ], + copts = cxxopts, + includes = ["include"], + env = {"RUST_BACKTRACE": "full"}, + linkstatic = True +) + clang_format_test( name = "clang_format", include = glob(["*"]), diff --git a/cpp/test/integration/test_cloud.cpp b/cpp/test/integration/test_cloud.cpp new file mode 100644 index 0000000000..9448a47632 --- /dev/null +++ b/cpp/test/integration/test_cloud.cpp @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "gtest/gtest.h" + +#include "typedb_driver.hpp" + +void delete_if_exists(const TypeDB::Driver& driver, const std::string& name) { + if (driver.databases.contains(name)) { + driver.databases.get(name).deleteDatabase(); + } +} + +TEST(TestConnection, TestAddressTranslation) { + std::string dbName = "hello_from_cpp"; + auto addressTranslation = std::unordered_map { + {"localhost:11729", "localhost:11729"}, + {"localhost:21729", "localhost:21729"}, + {"localhost:31729", "localhost:31729"}, + }; + auto credential = TypeDB::Credential("admin", "password", true, std::getenv("ROOT_CA")); + TypeDB::Driver driver = TypeDB::Driver::cloudDriver(addressTranslation, credential); + + delete_if_exists(driver, dbName); + driver.databases.create(dbName); + + auto sess = driver.session(dbName, TypeDB::SessionType::DATA); + auto tx = sess.transaction(TypeDB::TransactionType::WRITE); + + auto root = tx.concepts.getRootEntityType(); + int subtypeCount = 0; + for ([[maybe_unused]] auto& it : root->getSubtypes(tx)) + subtypeCount++; + ASSERT_EQ(1, subtypeCount); +} + From 93b14c0c6e829a3b2afade1cd919dcec37670513 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:13:35 +0100 Subject: [PATCH 42/57] rustfmt --- rust/src/connection/network/transmitter/rpc.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rust/src/connection/network/transmitter/rpc.rs b/rust/src/connection/network/transmitter/rpc.rs index 8dc14a6337..2d66aa7ea0 100644 --- a/rust/src/connection/network/transmitter/rpc.rs +++ b/rust/src/connection/network/transmitter/rpc.rs @@ -132,9 +132,7 @@ impl RPCTransmitter { Request::DatabaseGet { .. } => { rpc.databases_get(request.try_into_proto()?).await.and_then(Response::try_from_proto) } - Request::DatabasesAll => { - rpc.databases_all(request.try_into_proto()?).await.map(Response::from_proto) - } + Request::DatabasesAll => rpc.databases_all(request.try_into_proto()?).await.map(Response::from_proto), Request::DatabaseDelete { .. } => { rpc.database_delete(request.try_into_proto()?).await.map(Response::from_proto) From 68f4ecea6d163e5e4a984131a2fcb396b66ca069 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:21:06 +0100 Subject: [PATCH 43/57] Replica::server_id => server --- c/src/database.rs | 4 ++-- c/swig/typedb_driver_csharp.swg | 2 +- c/swig/typedb_driver_java.swg | 2 +- c/typedb_driver.i | 2 +- cpp/include/typedb/database/database.hpp | 2 +- cpp/lib/database/database.cpp | 4 ++-- csharp/Api/Database/IDatabase.cs | 2 +- csharp/Connection/TypeDBDatabase.cs | 6 +++--- java/connection/TypeDBDatabaseImpl.java | 4 ++-- python/typedb/connection/database.py | 4 ++-- rust/src/common/info.rs | 2 +- rust/src/connection/network/proto/database.rs | 2 +- rust/src/database/database.rs | 8 ++++---- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/c/src/database.rs b/c/src/database.rs index 48f1166bd5..6bed95854c 100644 --- a/c/src/database.rs +++ b/c/src/database.rs @@ -109,8 +109,8 @@ pub extern "C" fn replica_info_drop(replica_info: *mut ReplicaInfo) { /// Retrieves the ID of the server hosting this replica #[no_mangle] -pub extern "C" fn replica_info_get_server_id(replica_info: *const ReplicaInfo) -> *mut c_char { - release_string(borrow(replica_info).server_id.to_string()) +pub extern "C" fn replica_info_get_server(replica_info: *const ReplicaInfo) -> *mut c_char { + release_string(borrow(replica_info).server.to_string()) } /// Checks whether this is the primary replica of the raft cluster. diff --git a/c/swig/typedb_driver_csharp.swg b/c/swig/typedb_driver_csharp.swg index 55959ecb17..1e2ae95417 100644 --- a/c/swig/typedb_driver_csharp.swg +++ b/c/swig/typedb_driver_csharp.swg @@ -191,7 +191,7 @@ %noexception user_get_username; %noexception user_get_password_expiry_seconds; -%noexception replica_info_get_server_id; +%noexception replica_info_get_server; %noexception replica_info_is_primary; %noexception replica_info_is_preferred; %noexception replica_info_get_term; diff --git a/c/swig/typedb_driver_java.swg b/c/swig/typedb_driver_java.swg index 38b2ce53df..37982673dd 100644 --- a/c/swig/typedb_driver_java.swg +++ b/c/swig/typedb_driver_java.swg @@ -136,7 +136,7 @@ %nojavaexception user_get_username; %nojavaexception user_get_password_expiry_seconds; -%nojavaexception replica_info_get_server_id; +%nojavaexception replica_info_get_server; %nojavaexception replica_info_is_primary; %nojavaexception replica_info_is_preferred; %nojavaexception replica_info_get_term; diff --git a/c/typedb_driver.i b/c/typedb_driver.i index d1c8f577b7..b49586ca75 100644 --- a/c/typedb_driver.i +++ b/c/typedb_driver.i @@ -358,7 +358,7 @@ void transaction_on_close_register(const Transaction* transaction, TransactionCa %newobject database_get_primary_replica_info; %newobject database_get_replicas_info; -%newobject replica_info_get_server_id; +%newobject replica_info_get_server; %newobject replica_info_iterator_next; %newobject databases_all; diff --git a/cpp/include/typedb/database/database.hpp b/cpp/include/typedb/database/database.hpp index 1b2cb00f26..b50d3a7c8e 100644 --- a/cpp/include/typedb/database/database.hpp +++ b/cpp/include/typedb/database/database.hpp @@ -41,7 +41,7 @@ class ReplicaInfo { /** * Retrieves the server hosting this replica */ - std::string serverID(); + std::string server(); /** * Checks whether this is the primary replica of the raft cluster. diff --git a/cpp/lib/database/database.cpp b/cpp/lib/database/database.cpp index e0acff03cc..22063d1153 100644 --- a/cpp/lib/database/database.cpp +++ b/cpp/lib/database/database.cpp @@ -31,9 +31,9 @@ namespace TypeDB { ReplicaInfo::ReplicaInfo(_native::ReplicaInfo* replicaInfoNative) : replicaInfoNative(replicaInfoNative, _native::replica_info_drop) {} -std::string ReplicaInfo::serverID() { +std::string ReplicaInfo::server() { CHECK_NATIVE(replicaInfoNative); - return Utils::stringFromNative(_native::replica_info_get_server_id(replicaInfoNative.get())); + return Utils::stringFromNative(_native::replica_info_get_server(replicaInfoNative.get())); } bool ReplicaInfo::isPrimary() { diff --git a/csharp/Api/Database/IDatabase.cs b/csharp/Api/Database/IDatabase.cs index 7221095bc8..6483872b49 100644 --- a/csharp/Api/Database/IDatabase.cs +++ b/csharp/Api/Database/IDatabase.cs @@ -114,7 +114,7 @@ public interface IReplica /** * Retrieves the address of the server hosting this replica. */ - public string ServerID { get; } + public string Server { get; } /** * The raft protocol ‘term’ of this replica. diff --git a/csharp/Connection/TypeDBDatabase.cs b/csharp/Connection/TypeDBDatabase.cs index 6f9a7846d7..a4328d3ccb 100644 --- a/csharp/Connection/TypeDBDatabase.cs +++ b/csharp/Connection/TypeDBDatabase.cs @@ -169,7 +169,7 @@ public override string ToString() public class Replica : NativeObjectWrapper, IDatabase.IReplica { - private string? _serverID; + private string? _server; private long? _term; public Replica(Pinvoke.ReplicaInfo replicaInfo) @@ -186,9 +186,9 @@ public bool IsPreferred() return Pinvoke.typedb_driver.replica_info_is_preferred(NativeObject); } - public string ServerID + public string Server { - get { return _serverID ?? (_serverID = Pinvoke.typedb_driver.replica_info_get_server_id(NativeObject)); } + get { return _server ?? (_server = Pinvoke.typedb_driver.replica_info_get_server(NativeObject)); } } public long Term diff --git a/java/connection/TypeDBDatabaseImpl.java b/java/connection/TypeDBDatabaseImpl.java index 3a1f6b7983..73afdf6264 100644 --- a/java/connection/TypeDBDatabaseImpl.java +++ b/java/connection/TypeDBDatabaseImpl.java @@ -37,7 +37,7 @@ import static com.vaticle.typedb.driver.jni.typedb_driver.database_rule_schema; import static com.vaticle.typedb.driver.jni.typedb_driver.database_schema; import static com.vaticle.typedb.driver.jni.typedb_driver.database_type_schema; -import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_get_server_id; +import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_get_server; import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_get_term; import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_is_preferred; import static com.vaticle.typedb.driver.jni.typedb_driver.replica_info_is_primary; @@ -128,7 +128,7 @@ public static class Replica extends NativeObject Database: pass def address(self) -> str: - return replica_info_get_server_id(self._info) + return replica_info_get_server(self._info) def is_primary(self) -> bool: return replica_info_is_primary(self._info) diff --git a/rust/src/common/info.rs b/rust/src/common/info.rs index 299ce8af03..3122fa050a 100644 --- a/rust/src/common/info.rs +++ b/rust/src/common/info.rs @@ -40,7 +40,7 @@ pub(crate) struct DatabaseInfo { #[derive(Debug)] pub struct ReplicaInfo { /// The server hosting this replica - pub server_id: String, + pub server: String, /// Whether this is the primary replica of the raft cluster. pub is_primary: bool, /// Whether this is the preferred replica of the raft cluster. diff --git a/rust/src/connection/network/proto/database.rs b/rust/src/connection/network/proto/database.rs index b8538610db..dc19fe3ae2 100644 --- a/rust/src/connection/network/proto/database.rs +++ b/rust/src/connection/network/proto/database.rs @@ -30,6 +30,6 @@ impl FromProto for DatabaseInfo { impl FromProto for ReplicaInfo { fn from_proto(proto: ReplicaProto) -> Self { - Self { server_id: proto.address, is_primary: proto.primary, is_preferred: proto.preferred, term: proto.term } + Self { server: proto.address, is_primary: proto.primary, is_preferred: proto.preferred, term: proto.term } } } diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index 37f79df6f6..408bb9a6fa 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -270,7 +270,7 @@ pub(super) struct Replica { impl Replica { fn new(name: String, metadata: ReplicaInfo, server_connection: ServerConnection) -> Self { Self { - server_id: metadata.server_id, + server_id: metadata.server, database_name: name.clone(), is_primary: metadata.is_primary, term: metadata.term, @@ -285,8 +285,8 @@ impl Replica { .into_iter() .map(|replica| { let server_connection = connection - .connection(&replica.server_id) - .ok_or_else(|| InternalError::UnknownServer { server_id: replica.server_id.clone() })?; + .connection(&replica.server) + .ok_or_else(|| InternalError::UnknownServer { server_id: replica.server.clone() })?; Ok(Self::new(database_info.name.clone(), replica, server_connection.clone())) }) .collect() @@ -294,7 +294,7 @@ impl Replica { fn to_info(&self) -> ReplicaInfo { ReplicaInfo { - server_id: self.server_id.clone(), + server: self.server_id.clone(), is_primary: self.is_primary, is_preferred: self.is_preferred, term: self.term, From d9405d8a7b28f453d45bfe98a51892dde77e0738 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:24:00 +0100 Subject: [PATCH 44/57] Python: address => server --- python/typedb/api/connection/database.py | 4 ++-- python/typedb/connection/database.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/typedb/api/connection/database.py b/python/typedb/api/connection/database.py index ee16b60ec8..ab7e93d065 100644 --- a/python/typedb/api/connection/database.py +++ b/python/typedb/api/connection/database.py @@ -154,9 +154,9 @@ def database(self) -> Database: pass @abstractmethod - def address(self) -> str: + def server(self) -> str: """ - Retrieves address of the server hosting this replica + Retrieves the server hosting this replica :return: """ diff --git a/python/typedb/connection/database.py b/python/typedb/connection/database.py index 383a5283f8..b71d84d231 100644 --- a/python/typedb/connection/database.py +++ b/python/typedb/connection/database.py @@ -105,7 +105,7 @@ def __init__(self, replica_info: ReplicaInfo): def database(self) -> Database: pass - def address(self) -> str: + def server(self) -> str: return replica_info_get_server(self._info) def is_primary(self) -> bool: From bcd45ec46a621d8f8683a4ae455ac17d74d88e26 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:32:45 +0100 Subject: [PATCH 45/57] NodeJS: implement and test translation --- nodejs/TypeDB.ts | 2 +- nodejs/common/errors/ErrorMessage.ts | 1 + nodejs/connection/TypeDBDriverImpl.ts | 47 ++++++++++++--- .../test/integration/test-connection-cloud.js | 57 +++++++++++++++++++ 4 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 nodejs/test/integration/test-connection-cloud.js diff --git a/nodejs/TypeDB.ts b/nodejs/TypeDB.ts index 633ea5925e..4b6372ef80 100644 --- a/nodejs/TypeDB.ts +++ b/nodejs/TypeDB.ts @@ -50,7 +50,7 @@ export namespace TypeDB { * const driver = TypeDB.cloudDriver(["127.0.0.1:11729"], new TypeDBCredential(username, password)); * ``` */ - export function cloudDriver(addresses: string | string[], credential: TypeDBCredential): Promise { + export function cloudDriver(addresses: string | string[] | Record, credential: TypeDBCredential): Promise { if (typeof addresses === 'string') addresses = [addresses]; return new TypeDBDriverImpl(addresses, credential).open(); } diff --git a/nodejs/common/errors/ErrorMessage.ts b/nodejs/common/errors/ErrorMessage.ts index 6cf02d49aa..63b651449f 100644 --- a/nodejs/common/errors/ErrorMessage.ts +++ b/nodejs/common/errors/ErrorMessage.ts @@ -102,6 +102,7 @@ export namespace ErrorMessage { export const CLOUD_INVALID_ROOT_CA_PATH = new Driver(21, (args: Stringable[]) => `The provided Root CA path '${args[0]}' does not exist`); export const UNRECOGNISED_SESSION_TYPE = new Driver(22, (args: Stringable[]) => `Session type '${args[1]}' was not recognised.`); export const MISSING_PORT = new Driver(23, (args: Stringable[]) => `Invalid URL '${args[1]}': missing port.`); + export const ADDRESS_TRANSLATION_MISMATCH = new Driver(24, (args: Stringable[]) => `Address translation map does not match the server's advertised address list. User-provided servers not in the advertised list: {${args[0]}}. Advertised servers not mapped by user: {${args[1]}}.`); } export class Concept extends ErrorMessage { diff --git a/nodejs/connection/TypeDBDriverImpl.ts b/nodejs/connection/TypeDBDriverImpl.ts index fc6dcc1ddc..c2f227bd97 100644 --- a/nodejs/connection/TypeDBDriverImpl.ts +++ b/nodejs/connection/TypeDBDriverImpl.ts @@ -37,12 +37,13 @@ import CLOUD_UNABLE_TO_CONNECT = ErrorMessage.Driver.CLOUD_UNABLE_TO_CONNECT; import SESSION_ID_EXISTS = ErrorMessage.Driver.SESSION_ID_EXISTS; import UNABLE_TO_CONNECT = ErrorMessage.Driver.UNABLE_TO_CONNECT; import MISSING_PORT = ErrorMessage.Driver.MISSING_PORT; +import ADDRESS_TRANSLATION_MISMATCH = ErrorMessage.Driver.ADDRESS_TRANSLATION_MISMATCH; export class TypeDBDriverImpl implements TypeDBDriver { private _isOpen: boolean; private readonly _isCloud: boolean; - private readonly _initAddresses: string[]; + private readonly _initAddresses: string[] | Record; private readonly _credential: TypeDBCredential; private _userManager: UserManagerImpl; @@ -53,10 +54,10 @@ export class TypeDBDriverImpl implements TypeDBDriver { private readonly _sessions: { [id: string]: TypeDBSessionImpl }; - constructor(addresses: string | string[], credential?: TypeDBCredential) { + constructor(addresses: string | string[] | Record, credential?: TypeDBCredential) { if (typeof addresses === 'string') addresses = [addresses]; - for (const address of addresses) + for (const [_, address] of Object.entries(addresses)) if (!/:\d+/.test(address)) throw new TypeDBDriverError(MISSING_PORT.message(address)); @@ -77,7 +78,7 @@ export class TypeDBDriverImpl implements TypeDBDriver { } private async openCore(): Promise { - const serverAddress = this._initAddresses[0]; + const serverAddress = (this._initAddresses as string[])[0]; const serverStub = new TypeDBStubImpl(serverAddress, this._credential); await serverStub.open(); const advertisedAddress = (await serverStub.serversAll(RequestBuilder.ServerManager.allReq())).servers[0].address; @@ -89,10 +90,38 @@ export class TypeDBDriverImpl implements TypeDBDriver { private async openCloud(): Promise { const serverAddresses = await this.fetchCloudServerAddresses(); const openReqs: Promise[] = [] - for (const addr of serverAddresses) { - const serverStub = new TypeDBStubImpl(addr, this._credential); + + let addressTranslation: Record; + if (Array.isArray(this._initAddresses)) { + addressTranslation = {}; + for (const address of serverAddresses) { + addressTranslation[address] = address; + } + } else { + addressTranslation = this._initAddresses; + const unknown = []; + for (const [advertised, _] of Object.entries(addressTranslation)) { + if (serverAddresses.indexOf(advertised) === -1) { + unknown.push(advertised); + } + } + const unmapped = []; + for (const advertisedAddress of serverAddresses) { + if (!(advertisedAddress in addressTranslation)) { + unmapped.push(advertisedAddress); + } + } + if (unknown.length > 0 || unmapped.length > 0) { + throw new TypeDBDriverError( + ADDRESS_TRANSLATION_MISMATCH.message(unknown.join(", "), unmapped.join(", ")) + ); + } + } + + for (const [serverID, address] of Object.entries(addressTranslation)) { + const serverStub = new TypeDBStubImpl(address, this._credential); openReqs.push(serverStub.open()); - this.serverDrivers.set(addr, new ServerDriver(addr, serverStub)); + this.serverDrivers.set(serverID, new ServerDriver(address, serverStub)); } try { await Promise.any(openReqs); @@ -105,7 +134,7 @@ export class TypeDBDriverImpl implements TypeDBDriver { } private async fetchCloudServerAddresses(): Promise { - for (const address of this._initAddresses) { + for (const [_, address] of Object.entries(this._initAddresses)) { try { const stub = new TypeDBStubImpl(address, this._credential); await stub.open(); @@ -116,7 +145,7 @@ export class TypeDBDriverImpl implements TypeDBDriver { console.error(`Fetching cloud servers from ${address} failed.`, e); } } - throw new TypeDBDriverError(CLOUD_UNABLE_TO_CONNECT.message(this._initAddresses.join(","))); + throw new TypeDBDriverError(CLOUD_UNABLE_TO_CONNECT.message(Object.values(this._initAddresses).join(","))); } isOpen(): boolean { diff --git a/nodejs/test/integration/test-connection-cloud.js b/nodejs/test/integration/test-connection-cloud.js new file mode 100644 index 0000000000..ca04d5ac79 --- /dev/null +++ b/nodejs/test/integration/test-connection-cloud.js @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const { TypeDB, SessionType, TransactionType, TypeDBCredential } = require("../../dist"); +const assert = require("assert"); + +async function run() { + try { + const driver = await TypeDB.cloudDriver( + { + "localhost:1729": "localhost:11729", + "localhost:21729": "localhost:1729", + "localhost:31729": "localhost:31729", + }, + new TypeDBCredential("admin", "password", process.env.ROOT_CA) + ); + + const dbs = await driver.databases.all(); + const typedb = dbs.find(x => x.name === "typedb"); + if (typedb) { + await typedb.delete(); + } + await driver.databases.create("typedb"); + const session = await driver.session("typedb", SessionType.DATA); + const tx = await session.transaction(TransactionType.WRITE); + + const root = await tx.concepts.getRootEntityType(); + const subtypes = await root.getSubtypes(tx).collect(); + assert(subtypes.length === 1); + + await tx.close(); + await session.close(); + await driver.close(); + } catch (err) { + console.error(`ERROR: ${err.stack || err}`); + process.exit(1); + } +} + +run(); + From d6e626cc51a60017bedd9e9f1a48f77e0d1b021b Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:56:06 +0100 Subject: [PATCH 46/57] test names + factory CI --- .factory/automation.yml | 93 +++++++------------ cpp/test/integration/BUILD | 4 +- .../integration/{test.cpp => test_core.cpp} | 0 .../test/integration/test-connection-cloud.js | 4 +- ...-connection.js => test-connection-core.js} | 0 5 files changed, 38 insertions(+), 63 deletions(-) rename cpp/test/integration/{test.cpp => test_core.cpp} (100%) rename nodejs/test/integration/{test-connection.js => test-connection-core.js} (100%) diff --git a/.factory/automation.yml b/.factory/automation.yml index b7d7b4490b..1fde3f3a2f 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -273,7 +273,7 @@ build: tool/test/stop-cloud-servers.sh exit $TEST_SUCCESS - test-python-integration-core: + test-python-integration: image: vaticle-ubuntu-22.04 dependencies: - build @@ -286,29 +286,18 @@ build: bazel run @vaticle_dependencies//distribution/artifact:create-netrc tool/test/start-core-server.sh && - bazel test //python/tests/integration:test_connection --test_output=streamed --jobs=1 && bazel test //python/tests/integration:test_stream --test_output=streamed --jobs=1 && - export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + export CORE_FAILED= || export CORE_FAILED=1 tool/test/stop-core-server.sh - exit $TEST_SUCCESS + if [[ -n "$CORE_FAILED" ]]; then exit 1; fi -# test-python-integration-cloud-failover: -# machine: 4-core-16-gb -# image: vaticle-ubuntu-22.04 -# dependencies: -# - build -# type: foreground -# command: | -# export PATH="$HOME/.local/bin:$PATH" -# sudo apt-get update -# sudo apt install python3-pip -y -# python3 -m pip install -U pip -# python3 -m pip install -r python/requirements_dev.txt -# export ARTIFACT_USERNAME=$REPO_TYPEDB_USERNAME -# export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD -# bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh -# bazel run @vaticle_dependencies//distribution/artifact:create-netrc -# bazel test //python/tests/integration:test_cloud_failover --test_output=errors + source tool/test/start-cloud-servers.sh && # use source to receive export vars + bazel test //python/tests/integration:test_connection --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && + # TODO currently broken test + # bazel test //python/tests/integration:test_cloud_failover --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && + export CLOUD_FAILED= || export CLOUD_FAILED=1 + tool/test/stop-cloud-servers.sh + if [[ -n "$CLOUD_FAILED" ]]; then exit 1; fi test-nodejs-integration: image: vaticle-ubuntu-22.04 @@ -323,29 +312,18 @@ build: cp -rL bazel-bin/nodejs/dist nodejs/. tool/test/start-core-server.sh && node nodejs/test/integration/test-concept.js && - node nodejs/test/integration/test-connection.js && node nodejs/test/integration/test-query.js && - export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + node nodejs/test/integration/test-connection-core.js && + export CORE_FAILED= || export CORE_FAILED=1 tool/test/stop-core-server.sh - exit $TEST_SUCCESS + if [[ -n "$CORE_FAILED" ]]; then exit 1; fi - test-nodejs-cloud-failover: - machine: 4-core-16-gb - image: vaticle-ubuntu-22.04 - dependencies: - - build - command: | - export ARTIFACT_USERNAME=$REPO_TYPEDB_USERNAME - export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD - bazel run @vaticle_dependencies//distribution/artifact:create-netrc - bazel build //nodejs/... - cp -rL bazel-bin/nodejs/node_modules nodejs/. - cp -rL bazel-bin/nodejs/dist nodejs/. - source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars + source tool/test/start-cloud-servers.sh && # use source to receive export vars + node nodejs/test/integration/test-connection-cloud.js && node nodejs/test/integration/test-cloud-failover.js && - export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + export CLOUD_FAILED= || export CLOUD_FAILED=1 tool/test/stop-cloud-servers.sh - exit $TEST_SUCCESS + if [[ -n "$CLOUD_FAILED" ]]; then exit 1; fi test-nodejs-behaviour-core: image: vaticle-ubuntu-22.04 @@ -386,7 +364,7 @@ build: tool/test/stop-cloud-servers.sh exit $TEST_SUCCESS - test-cpp-integration-core: + test-cpp-integration: image: vaticle-ubuntu-22.04 dependencies: - build @@ -399,10 +377,16 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc tool/test/start-core-server.sh && - bazel test //cpp/test/integration/... --test_output=streamed --jobs=1 && - export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //cpp/test/integration:test-cpp-driver-core --test_output=streamed --jobs=1 && + export CORE_FAILED= || export CORE_FAILED=1 tool/test/stop-core-server.sh - exit $TEST_SUCCESS + if [[ -n "$CORE_FAILED" ]]; then exit 1; fi + + source tool/test/start-cloud-servers.sh && # use source to receive export vars + bazel test //cpp/test/integration:test-cpp-driver-cloud --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && + export CLOUD_FAILED= || export CLOUD_FAILED=1 + tool/test/stop-cloud-servers.sh + if [[ -n "$CLOUD_FAILED" ]]; then exit 1; fi test-cpp-behaviour-core: image: vaticle-ubuntu-22.04 @@ -449,7 +433,7 @@ build: tool/test/stop-cloud-servers.sh exit $TEST_SUCCESS - test-csharp-integration-core: + test-csharp-integration: image: vaticle-ubuntu-22.04 dependencies: - build @@ -464,25 +448,16 @@ build: bazel test //csharp/Test/Integration/Data/... --test_output=streamed --jobs=1 && bazel test //csharp/Test/Integration/Marshal/... --test_output=streamed --jobs=1 && .factory/test-core.sh //csharp/Test/Integration/Examples/... --test_output=streamed --jobs=1 && - export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + export CORE_FAILED= || export CORE_FAILED=1 tool/test/stop-core-server.sh - exit $TEST_SUCCESS + if [[ -n "$CORE_FAILED" ]]; then exit 1; fi - test-csharp-integration-cloud: - image: vaticle-ubuntu-22.04 - dependencies: - - build - type: foreground - command: | - export ARTIFACT_USERNAME=$REPO_TYPEDB_USERNAME - export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD - bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh - bazel run @vaticle_dependencies//distribution/artifact:create-netrc - source tool/test/start-cloud-servers.sh && + source tool/test/start-cloud-servers.sh && # use source to receive export vars + bazel test //csharp/Test/Integration/Network/... --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && .factory/test-cloud.sh //csharp/Test/Integration/Examples/... --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && - export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + export CLOUD_FAILED= || export CLOUD_FAILED=1 tool/test/stop-cloud-servers.sh - exit $TEST_SUCCESS + if [[ -n "$CLOUD_FAILED" ]]; then exit 1; fi test-csharp-behaviour-core: image: vaticle-ubuntu-22.04 diff --git a/cpp/test/integration/BUILD b/cpp/test/integration/BUILD index 4cfdeb5c11..63c77a07bb 100644 --- a/cpp/test/integration/BUILD +++ b/cpp/test/integration/BUILD @@ -20,8 +20,8 @@ load("@vaticle_dependencies//builder/cpp:rules.bzl", "clang_format_test") load("//cpp:build_opts.bzl", "cxxopts") cc_test( - name = "test-cpp-driver", - srcs = ["test.cpp"], + name = "test-cpp-driver-core", + srcs = ["test_core.cpp"], deps = [ "//cpp:typedb-driver-cpp-import", # External diff --git a/cpp/test/integration/test.cpp b/cpp/test/integration/test_core.cpp similarity index 100% rename from cpp/test/integration/test.cpp rename to cpp/test/integration/test_core.cpp diff --git a/nodejs/test/integration/test-connection-cloud.js b/nodejs/test/integration/test-connection-cloud.js index ca04d5ac79..5d589c5f57 100644 --- a/nodejs/test/integration/test-connection-cloud.js +++ b/nodejs/test/integration/test-connection-cloud.js @@ -24,8 +24,8 @@ async function run() { try { const driver = await TypeDB.cloudDriver( { - "localhost:1729": "localhost:11729", - "localhost:21729": "localhost:1729", + "localhost:11729": "localhost:11729", + "localhost:21729": "localhost:21729", "localhost:31729": "localhost:31729", }, new TypeDBCredential("admin", "password", process.env.ROOT_CA) diff --git a/nodejs/test/integration/test-connection.js b/nodejs/test/integration/test-connection-core.js similarity index 100% rename from nodejs/test/integration/test-connection.js rename to nodejs/test/integration/test-connection-core.js From 14b814cb59916520b7d22cb213caf68fa472f85b Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:59:08 +0100 Subject: [PATCH 47/57] 3 servers in Cloud integration tests --- .factory/automation.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.factory/automation.yml b/.factory/automation.yml index 1fde3f3a2f..ed0fa52b9a 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -291,7 +291,7 @@ build: tool/test/stop-core-server.sh if [[ -n "$CORE_FAILED" ]]; then exit 1; fi - source tool/test/start-cloud-servers.sh && # use source to receive export vars + source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars bazel test //python/tests/integration:test_connection --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && # TODO currently broken test # bazel test //python/tests/integration:test_cloud_failover --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && @@ -318,7 +318,7 @@ build: tool/test/stop-core-server.sh if [[ -n "$CORE_FAILED" ]]; then exit 1; fi - source tool/test/start-cloud-servers.sh && # use source to receive export vars + source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars node nodejs/test/integration/test-connection-cloud.js && node nodejs/test/integration/test-cloud-failover.js && export CLOUD_FAILED= || export CLOUD_FAILED=1 @@ -382,7 +382,7 @@ build: tool/test/stop-core-server.sh if [[ -n "$CORE_FAILED" ]]; then exit 1; fi - source tool/test/start-cloud-servers.sh && # use source to receive export vars + source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars bazel test //cpp/test/integration:test-cpp-driver-cloud --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && export CLOUD_FAILED= || export CLOUD_FAILED=1 tool/test/stop-cloud-servers.sh @@ -452,7 +452,7 @@ build: tool/test/stop-core-server.sh if [[ -n "$CORE_FAILED" ]]; then exit 1; fi - source tool/test/start-cloud-servers.sh && # use source to receive export vars + source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars bazel test //csharp/Test/Integration/Network/... --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && .factory/test-cloud.sh //csharp/Test/Integration/Examples/... --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && export CLOUD_FAILED= || export CLOUD_FAILED=1 From 69762f74b7a923c3ebfe45d5c1fb6a32d73e994f Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:42:29 +0100 Subject: [PATCH 48/57] Fix C# test --- csharp/Test/Integration/Network/AddressTranslationTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/Test/Integration/Network/AddressTranslationTest.cs b/csharp/Test/Integration/Network/AddressTranslationTest.cs index da73708a09..12ec449ba1 100644 --- a/csharp/Test/Integration/Network/AddressTranslationTest.cs +++ b/csharp/Test/Integration/Network/AddressTranslationTest.cs @@ -41,7 +41,7 @@ public void TestCloudConnectionWithTranslation() IDictionary addressTranslation = new Dictionary() { {"localhost:11729", "localhost:11729"}, {"localhost:21729", "localhost:21729"}, - {"localhost:1729", "localhost:31729"}, + {"localhost:31729", "localhost:31729"}, }; TypeDBCredential connectCredential = new TypeDBCredential( From 3cdac7aab1eb84142c3616ad1d487bdcd127bb81 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:01:38 +0100 Subject: [PATCH 49/57] note --- rust/src/connection/network/proto/database.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/src/connection/network/proto/database.rs b/rust/src/connection/network/proto/database.rs index dc19fe3ae2..c9d9f0734a 100644 --- a/rust/src/connection/network/proto/database.rs +++ b/rust/src/connection/network/proto/database.rs @@ -30,6 +30,11 @@ impl FromProto for DatabaseInfo { impl FromProto for ReplicaInfo { fn from_proto(proto: ReplicaProto) -> Self { - Self { server: proto.address, is_primary: proto.primary, is_preferred: proto.preferred, term: proto.term } + Self { + server: proto.address, // TODO should be eventually replaced by "server_id" or "server_name" in protocol + is_primary: proto.primary, + is_preferred: proto.preferred, + term: proto.term, + } } } From 118f1c9bff411b92aff2f222bb48800dfe7416e9 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:05:52 +0100 Subject: [PATCH 50/57] fix override docs --- cpp/include/typedb/connection/driver.hpp | 17 ++--------------- csharp/Drivers.cs | 16 ++-------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/cpp/include/typedb/connection/driver.hpp b/cpp/include/typedb/connection/driver.hpp index 7b5dce3022..fdf84df209 100644 --- a/cpp/include/typedb/connection/driver.hpp +++ b/cpp/include/typedb/connection/driver.hpp @@ -85,24 +85,11 @@ class Driver { * Driver::cloudDriver(addresses, credential); * * - * @param addresses The address(es) of the TypeDB server(s) + * @param addresses The address(es) of the TypeDB server(s) or translation map from addresses + * received from the TypeDB server(s) to addresses to be used by the driver for connection * @param credential The Credential to connect with */ static Driver cloudDriver(const std::vector& addresses, const Credential& credential); - - /** - * Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with - * the provided credential. - * - *

Examples

- *
-     * TypeDB.cloudDriver(addresses, credential);
-     * 
- * - * @param addressTranslation Translation map from addresses received from the TypeDB server(s) - * to addresses to be used by the driver for connection - * @param credential The credential to connect with - */ static Driver cloudDriver(const std::unordered_map& addressTranslation, const Credential& credential); Driver(const Driver&) = delete; diff --git a/csharp/Drivers.cs b/csharp/Drivers.cs index 5dc38d8782..0a6b415035 100644 --- a/csharp/Drivers.cs +++ b/csharp/Drivers.cs @@ -70,7 +70,8 @@ public static ITypeDBDriver CloudDriver(string address, TypeDBCredential credent * Drivers.CloudDriver(addresses, credential); * * - * @param addresses The address(es) of the TypeDB server(s) + * @param addresses The address(es) of the TypeDB server(s) or translation map from addresses + * received from the TypeDB server(s) to addresses to be used by the driver for connection * @param credential The credential to connect with */ public static ITypeDBDriver CloudDriver(ICollection addresses, TypeDBCredential credential) @@ -78,19 +79,6 @@ public static ITypeDBDriver CloudDriver(ICollection addresses, TypeDBCre return new TypeDBDriver(addresses, credential); } - /** - * Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with - * the provided credential. - * - *

Examples

- *
-         * TypeDB.cloudDriver(addressTranslation, credential);
-         * 
- * - * @param addressTranslation Translation map from addresses received from the TypeDB server(s) - * to addresses to be used by the driver for connection - * @param credential The credential to connect with - */ public static ITypeDBDriver CloudDriver(IDictionary addressTranslation, TypeDBCredential credential) { return new TypeDBDriver(addressTranslation, credential); From 47fa63a014b505aac6605f5ecaac35031b8a87cd Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:12:08 +0100 Subject: [PATCH 51/57] NodeJS: replica address => server --- nodejs/api/connection/database/Database.ts | 4 ++-- nodejs/connection/TypeDBDatabaseImpl.ts | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/nodejs/api/connection/database/Database.ts b/nodejs/api/connection/database/Database.ts index d063d1aa58..035d62f045 100644 --- a/nodejs/api/connection/database/Database.ts +++ b/nodejs/api/connection/database/Database.ts @@ -69,7 +69,7 @@ export namespace Database { * If true, Operations which can be run on any replica will prefer to use this replica. */ readonly preferred: boolean; - /** The address of the server hosting this replica */ - readonly address: string; + /** The server hosting this replica */ + readonly server: string; } } diff --git a/nodejs/connection/TypeDBDatabaseImpl.ts b/nodejs/connection/TypeDBDatabaseImpl.ts index e3d3df21e8..afa17a4548 100644 --- a/nodejs/connection/TypeDBDatabaseImpl.ts +++ b/nodejs/connection/TypeDBDatabaseImpl.ts @@ -128,7 +128,7 @@ export class TypeDBDatabaseImpl implements Database { async runOnAnyReplica(task: (serverDriver: ServerDriver, serverDatabase: ServerDatabase) => Promise): Promise { for (const replica of this.replicas) { try { - return await task(this._driver.serverDrivers.get(replica.address), replica.database); + return await task(this._driver.serverDrivers.get(replica.server), replica.database); } catch (e) { if (e instanceof TypeDBDriverError && UNABLE_TO_CONNECT === e.messageTemplate) { // TODO log @@ -144,7 +144,7 @@ export class TypeDBDatabaseImpl implements Database { } for (const _ of Array(PRIMARY_REPLICA_TASK_MAX_RETRIES)) { try { - return await task(this._driver.serverDrivers.get(this.primaryReplica.address), this.primaryReplica.database); + return await task(this._driver.serverDrivers.get(this.primaryReplica.server), this.primaryReplica.database); } catch (e) { if (e instanceof TypeDBDriverError && (UNABLE_TO_CONNECT === e.messageTemplate || CLOUD_REPLICA_NOT_PRIMARY === e.messageTemplate) @@ -176,14 +176,14 @@ export class TypeDBDatabaseImpl implements Database { } export class Replica implements Database.Replica { - private readonly _address: string; + private readonly _server: string; private readonly _database: ServerDatabase; private readonly _term: number; private readonly _isPrimary: boolean; private readonly _isPreferred: boolean; - private constructor(database: ServerDatabase, address: string, term: number, isPrimary: boolean, isPreferred: boolean) { - this._address = address; + private constructor(database: ServerDatabase, server: string, term: number, isPrimary: boolean, isPreferred: boolean) { + this._server = server; this._database = database; this._term = term; this._isPrimary = isPrimary; @@ -198,8 +198,8 @@ export class Replica implements Database.Replica { return this._database; } - get address(): string { - return this._address; + get server(): string { + return this._server; } get databaseName(): string { @@ -219,7 +219,7 @@ export class Replica implements Database.Replica { } toString(): string { - return `${this._address}/${this.databaseName}:${this._isPrimary ? "P" : "S"}:${this._term}`; + return `${this._server}/${this.databaseName}:${this._isPrimary ? "P" : "S"}:${this._term}`; } } From 0862dd98416b6bba7a2f242942a80dd611f336d8 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:19:01 +0100 Subject: [PATCH 52/57] docs --- c/docs/connection/connection.adoc | 28 +++++++++++ c/docs/connection/replica.adoc | 8 ++-- c/src/database.rs | 2 +- cpp/docs/connection/Driver.adoc | 12 ++--- cpp/docs/connection/ReplicaInfo.adoc | 28 +++++------ cpp/include/typedb/database/database.hpp | 2 +- csharp/Api/Database/IDatabase.cs | 2 +- csharp/docs/connection/Drivers.adoc | 2 +- csharp/docs/connection/IReplica.adoc | 28 +++++------ java/api/database/Database.java | 2 +- java/docs/connection/Database.Replica.adoc | 28 +++++------ java/docs/connection/TypeDB.adoc | 33 +++++++++++++ nodejs/docs/connection/Replica.adoc | 2 +- nodejs/docs/connection/TypeDB.adoc | 4 +- python/docs/connection/Replica.adoc | 28 +++++------ python/docs/connection/TypeDB.adoc | 6 +-- python/typedb/api/connection/database.py | 2 +- rust/docs/connection/Connection.adoc | 55 ++++++++++++++++++++++ rust/docs/connection/ReplicaInfo.adoc | 2 +- rust/docs/errors/ConnectionError.adoc | 1 + rust/docs/errors/InternalError.adoc | 2 +- 21 files changed, 197 insertions(+), 80 deletions(-) diff --git a/c/docs/connection/connection.adoc b/c/docs/connection/connection.adoc index 4536233ae9..d98b5ea33c 100644 --- a/c/docs/connection/connection.adoc +++ b/c/docs/connection/connection.adoc @@ -102,6 +102,34 @@ a| `credential` a| The ``Credential`` to connect with a| `const struct Credentia .Returns `struct Connection*` +[#_connection_open_cloud_translated] +==== connection_open_cloud_translated + +[source,cpp] +---- +struct Connection* connection_open_cloud_translated(const char*const* advertised_addresses, const char*const* translated_addresses, const struct Credential* credential) +---- + + + +Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with the provided credential. + + +[caption=""] +.Input parameters +[cols=",,"] +[options="header"] +|=== +|Name |Description |Type +a| `advertised_addresses` a| a null-terminated array holding the address(es) the TypeDB server(s) are configured to advertise a| `const char*const*` +a| `translated_addresses` a| a null-terminated array holding the address(es) of the TypeDB server(s) the driver will connect to a| `const char*const*` +a| `credential` a| The ``Credential`` to connect with a| `const struct Credential*` +|=== + +[caption=""] +.Returns +`struct Connection*` + [#_connection_open_core] ==== connection_open_core diff --git a/c/docs/connection/replica.adoc b/c/docs/connection/replica.adoc index 77cedf593f..f93370f3a4 100644 --- a/c/docs/connection/replica.adoc +++ b/c/docs/connection/replica.adoc @@ -31,17 +31,17 @@ Frees the native rust ``ReplicaInfo`` object .Returns `void` -[#_replica_info_get_address] -==== replica_info_get_address +[#_replica_info_get_server] +==== replica_info_get_server [source,cpp] ---- -char* replica_info_get_address(const struct ReplicaInfo* replica_info) +char* replica_info_get_server(const struct ReplicaInfo* replica_info) ---- -Retrieves the address of the server hosting this replica +The server hosting this replica [caption=""] .Returns diff --git a/c/src/database.rs b/c/src/database.rs index 6bed95854c..4e3c77c0ce 100644 --- a/c/src/database.rs +++ b/c/src/database.rs @@ -107,7 +107,7 @@ pub extern "C" fn replica_info_drop(replica_info: *mut ReplicaInfo) { free(replica_info); } -/// Retrieves the ID of the server hosting this replica +/// The server hosting this replica #[no_mangle] pub extern "C" fn replica_info_get_server(replica_info: *const ReplicaInfo) -> *mut c_char { release_string(borrow(replica_info).server.to_string()) diff --git a/cpp/docs/connection/Driver.adoc b/cpp/docs/connection/Driver.adoc index 18324459e7..c67f564232 100644 --- a/cpp/docs/connection/Driver.adoc +++ b/cpp/docs/connection/Driver.adoc @@ -44,12 +44,12 @@ Closes the driver. Before instantiating a new driver, the driver that’s curren driver.close() ---- -[#_static_Driver_TypeDBDrivercloudDriver___const_stdvector__stdstring____cloudAddresses__const_Credential__credential_] +[#_static_Driver_TypeDBDrivercloudDriver___const_stdvector__stdstring____addresses__const_Credential__credential_] ==== cloudDriver [source,cpp] ---- -static Driver TypeDB::Driver::cloudDriver(const std::vector< std::string >& cloudAddresses, const Credential& credential) +static Driver TypeDB::Driver::cloudDriver(const std::vector< std::string >& addresses, const Credential& credential) ---- @@ -63,7 +63,7 @@ Open a TypeDB Driver to TypeDB Cloud server(s) available at the provided address [options="header"] |=== |Name |Description |Type -a| `addresses` a| The address(es) of the TypeDB server(s) a| +a| `addresses` a| The address(es) of the TypeDB server(s) or translation map from addresses received from the TypeDB server(s) to addresses to be used by the driver for connection a| `const std::vector< std::string >&` a| `credential` a| The Credential to connect with a| `const Credential&` |=== @@ -78,12 +78,12 @@ a| `credential` a| The Credential to connect with a| `const Credential&` Driver::cloudDriver(addresses, credential); ---- -[#_static_Driver_TypeDBDrivercoreDriver___const_stdstring__coreAddress_] +[#_static_Driver_TypeDBDrivercoreDriver___const_stdstring__address_] ==== coreDriver [source,cpp] ---- -static Driver TypeDB::Driver::coreDriver(const std::string& coreAddress) +static Driver TypeDB::Driver::coreDriver(const std::string& address) ---- @@ -97,7 +97,7 @@ Open a TypeDB Driver to a TypeDB Core server available at the provided address. [options="header"] |=== |Name |Description |Type -a| `address` a| The address of the TypeDB server a| +a| `address` a| The address of the TypeDB server a| `const std::string&` |=== [caption=""] diff --git a/cpp/docs/connection/ReplicaInfo.adoc b/cpp/docs/connection/ReplicaInfo.adoc index 6cfb6ee7f9..28294967d0 100644 --- a/cpp/docs/connection/ReplicaInfo.adoc +++ b/cpp/docs/connection/ReplicaInfo.adoc @@ -8,53 +8,53 @@ The metadata and state of an individual raft replica of a database. // tag::methods[] -[#_stdstring_TypeDBReplicaInfoaddress___] -==== address +[#_bool_TypeDBReplicaInfoisPreferred___] +==== isPreferred [source,cpp] ---- -std::string TypeDB::ReplicaInfo::address() +bool TypeDB::ReplicaInfo::isPreferred() ---- -Retrieves the address of the server hosting this replica +Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. [caption=""] .Returns -`std::string` +`bool` -[#_bool_TypeDBReplicaInfoisPreferred___] -==== isPreferred +[#_bool_TypeDBReplicaInfoisPrimary___] +==== isPrimary [source,cpp] ---- -bool TypeDB::ReplicaInfo::isPreferred() +bool TypeDB::ReplicaInfo::isPrimary() ---- -Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. +Checks whether this is the primary replica of the raft cluster. [caption=""] .Returns `bool` -[#_bool_TypeDBReplicaInfoisPrimary___] -==== isPrimary +[#_stdstring_TypeDBReplicaInfoserver___] +==== server [source,cpp] ---- -bool TypeDB::ReplicaInfo::isPrimary() +std::string TypeDB::ReplicaInfo::server() ---- -Checks whether this is the primary replica of the raft cluster. +The server hosting this replica [caption=""] .Returns -`bool` +`std::string` [#_int64_t_TypeDBReplicaInfoterm___] ==== term diff --git a/cpp/include/typedb/database/database.hpp b/cpp/include/typedb/database/database.hpp index b50d3a7c8e..2c785311ac 100644 --- a/cpp/include/typedb/database/database.hpp +++ b/cpp/include/typedb/database/database.hpp @@ -39,7 +39,7 @@ class ReplicaInfo { ReplicaInfo& operator=(ReplicaInfo&&) = default; /** - * Retrieves the server hosting this replica + * The server hosting this replica */ std::string server(); diff --git a/csharp/Api/Database/IDatabase.cs b/csharp/Api/Database/IDatabase.cs index 6483872b49..a3fca934bf 100644 --- a/csharp/Api/Database/IDatabase.cs +++ b/csharp/Api/Database/IDatabase.cs @@ -112,7 +112,7 @@ public interface IDatabase public interface IReplica { /** - * Retrieves the address of the server hosting this replica. + * The server hosting this replica. */ public string Server { get; } diff --git a/csharp/docs/connection/Drivers.adoc b/csharp/docs/connection/Drivers.adoc index 954b714579..c647fe15f2 100644 --- a/csharp/docs/connection/Drivers.adoc +++ b/csharp/docs/connection/Drivers.adoc @@ -57,7 +57,7 @@ Open a TypeDB Driver to TypeDB Cloud server(s) available at the provided address [options="header"] |=== |Name |Description |Type -a| `addresses` a| The address(es) of the TypeDB server(s) a| `ICollection< string >` +a| `addresses` a| The address(es) of the TypeDB server(s) or translation map from addresses received from the TypeDB server(s) to addresses to be used by the driver for connection a| `ICollection< string >` a| `credential` a| The credential to connect with a| `TypeDBCredential` |=== diff --git a/csharp/docs/connection/IReplica.adoc b/csharp/docs/connection/IReplica.adoc index edf44d3b13..2210d40e13 100644 --- a/csharp/docs/connection/IReplica.adoc +++ b/csharp/docs/connection/IReplica.adoc @@ -8,53 +8,53 @@ The metadata and state of an individual raft replica of a database. // tag::methods[] -[#_string_TypeDB_Driver_Api_IDatabase_IReplica_Address] -==== Address +[#_bool_TypeDB_Driver_Api_IDatabase_IReplica_IsPreferred___] +==== IsPreferred [source,cs] ---- -string TypeDB.Driver.Api.IDatabase.IReplica.Address +bool IsPreferred() ---- -Retrieves the address of the server hosting this replica. +Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. [caption=""] .Returns -`string` +`bool` -[#_bool_TypeDB_Driver_Api_IDatabase_IReplica_IsPreferred___] -==== IsPreferred +[#_bool_TypeDB_Driver_Api_IDatabase_IReplica_IsPrimary___] +==== IsPrimary [source,cs] ---- -bool IsPreferred() +bool IsPrimary() ---- -Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. +Checks whether this is the primary replica of the raft cluster. [caption=""] .Returns `bool` -[#_bool_TypeDB_Driver_Api_IDatabase_IReplica_IsPrimary___] -==== IsPrimary +[#_string_TypeDB_Driver_Api_IDatabase_IReplica_Server] +==== Server [source,cs] ---- -bool IsPrimary() +string TypeDB.Driver.Api.IDatabase.IReplica.Server ---- -Checks whether this is the primary replica of the raft cluster. +The server hosting this replica. [caption=""] .Returns -`bool` +`string` [#_long_TypeDB_Driver_Api_IDatabase_IReplica_Term] ==== Term diff --git a/java/api/database/Database.java b/java/api/database/Database.java index 43756717b7..a193a20b63 100644 --- a/java/api/database/Database.java +++ b/java/api/database/Database.java @@ -116,7 +116,7 @@ public interface Database { interface Replica { /** - * Retrieves the server hosting this replica + * The server hosting this replica */ @CheckReturnValue String serverID(); diff --git a/java/docs/connection/Database.Replica.adoc b/java/docs/connection/Database.Replica.adoc index 115a27b252..969d4a68a0 100644 --- a/java/docs/connection/Database.Replica.adoc +++ b/java/docs/connection/Database.Replica.adoc @@ -6,50 +6,50 @@ The metadata and state of an individual raft replica of a database. // tag::methods[] -[#_Database_Replica_address__] -==== address +[#_Database_Replica_isPreferred__] +==== isPreferred [source,java] ---- @CheckReturnValue -java.lang.String address() +boolean isPreferred() ---- -Retrieves the address of the server hosting this replica +Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. [caption=""] .Returns -`java.lang.String` +`boolean` -[#_Database_Replica_isPreferred__] -==== isPreferred +[#_Database_Replica_isPrimary__] +==== isPrimary [source,java] ---- @CheckReturnValue -boolean isPreferred() +boolean isPrimary() ---- -Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. +Checks whether this is the primary replica of the raft cluster. [caption=""] .Returns `boolean` -[#_Database_Replica_isPrimary__] -==== isPrimary +[#_Database_Replica_serverID__] +==== serverID [source,java] ---- @CheckReturnValue -boolean isPrimary() +java.lang.String serverID() ---- -Checks whether this is the primary replica of the raft cluster. +The server hosting this replica [caption=""] .Returns -`boolean` +`java.lang.String` [#_Database_Replica_term__] ==== term diff --git a/java/docs/connection/TypeDB.adoc b/java/docs/connection/TypeDB.adoc index 3608520d07..6411f63d1d 100644 --- a/java/docs/connection/TypeDB.adoc +++ b/java/docs/connection/TypeDB.adoc @@ -95,6 +95,39 @@ a| `credential` a| The credential to connect with a| `TypeDBCredential` TypeDB.cloudDriver(addresses, credential); ---- +[#_TypeDB_cloudDriver__java_util_Map_java_lang_String_​java_lang_String___TypeDBCredential] +==== cloudDriver + +[source,java] +---- +public static TypeDBDriver cloudDriver​(java.util.Map addressTranslation, + TypeDBCredential credential) +---- + +Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with the provided credential. + + +[caption=""] +.Input parameters +[cols=",,"] +[options="header"] +|=== +|Name |Description |Type +a| `addressTranslation` a| Translation map from addresses received from the TypeDB server(s) to addresses to be used by the driver for connection a| `java.util.Map` +a| `credential` a| The credential to connect with a| `TypeDBCredential` +|=== + +[caption=""] +.Returns +`public static TypeDBDriver` + +[caption=""] +.Code examples +[source,java] +---- +TypeDB.cloudDriver(addressTranslation, credential); +---- + [#_TypeDB_coreDriver__java_lang_String] ==== coreDriver diff --git a/nodejs/docs/connection/Replica.adoc b/nodejs/docs/connection/Replica.adoc index 3464de5cd9..e42d26308b 100644 --- a/nodejs/docs/connection/Replica.adoc +++ b/nodejs/docs/connection/Replica.adoc @@ -10,10 +10,10 @@ The metadata and state of an individual raft replica of a database. [options="header"] |=== |Name |Type |Description -a| `address` a| `string` a| The address of the server hosting this replica a| `databaseName` a| `string` a| The database for which this is a replica. a| `preferred` a| `boolean` a| Checks whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. a| `primary` a| `boolean` a| Checks whether this is the primary replica of the raft cluster. +a| `server` a| `string` a| The server hosting this replica a| `term` a| `number` a| The raft protocol ‘term’ of this replica. |=== // end::properties[] diff --git a/nodejs/docs/connection/TypeDB.adoc b/nodejs/docs/connection/TypeDB.adoc index 705cc9ad41..23e1f06596 100644 --- a/nodejs/docs/connection/TypeDB.adoc +++ b/nodejs/docs/connection/TypeDB.adoc @@ -13,7 +13,7 @@ a| `DEFAULT_ADDRESS` // end::enum_constants[] // tag::methods[] -[#_TypeDB_cloudDriver__addresses_string__string____credential_TypeDBCredential] +[#_TypeDB_cloudDriver__addresses_string__string____Record_string__string___credential_TypeDBCredential] ==== cloudDriver [source,nodejs] @@ -29,7 +29,7 @@ Creates a connection to TypeDB Cloud, authenticating with the provided credentia [options="header"] |=== |Name |Description |Type -a| `addresses` a| List of addresses of the individual TypeDB Cloud servers. As long one specified address is provided, the driver will discover the other addresses from that server. a| `string \| string[]` +a| `addresses` a| List of addresses of the individual TypeDB Cloud servers. As long one specified address is provided, the driver will discover the other addresses from that server. a| `string \| string[] \| Record` a| `credential` a| The credentials to log in, and encryption settings. See ``TypeDBCredential`` Examples ``const driver = TypeDB.cloudDriver(["127.0.0.1:11729"], new TypeDBCredential(username, password)); diff --git a/python/docs/connection/Replica.adoc b/python/docs/connection/Replica.adoc index 72e8c9568d..d686bd4f8a 100644 --- a/python/docs/connection/Replica.adoc +++ b/python/docs/connection/Replica.adoc @@ -4,20 +4,6 @@ The metadata and state of an individual raft replica of a database. // tag::methods[] -[#_Replica_address__] -==== address - -[source,python] ----- -address() -> str ----- - -Retrieves address of the server hosting this replica - -[caption=""] -.Returns -`str` - [#_Replica_database__] ==== database @@ -60,6 +46,20 @@ Checks whether this is the primary replica of the raft cluster. .Returns `bool` +[#_Replica_server__] +==== server + +[source,python] +---- +server() -> str +---- + +The server hosting this replica + +[caption=""] +.Returns +`str` + [#_Replica_term__] ==== term diff --git a/python/docs/connection/TypeDB.adoc b/python/docs/connection/TypeDB.adoc index 2c6a39f781..49c8a01fb4 100644 --- a/python/docs/connection/TypeDB.adoc +++ b/python/docs/connection/TypeDB.adoc @@ -2,12 +2,12 @@ === TypeDB // tag::methods[] -[#_TypeDB_cloud_driver__addresses_Iterable_str___str__credential_TypeDBCredential] +[#_TypeDB_cloud_driver__addresses_Mapping_str__str___Iterable_str___str__credential_TypeDBCredential] ==== cloud_driver [source,python] ---- -static cloud_driver(addresses: Iterable[str] | str, credential: TypeDBCredential) -> TypeDBDriver +static cloud_driver(addresses: Mapping[str, str] | Iterable[str] | str, credential: TypeDBCredential) -> TypeDBDriver ---- Creates a connection to TypeDB Cloud, authenticating with the provided credentials. @@ -18,7 +18,7 @@ Creates a connection to TypeDB Cloud, authenticating with the provided credentia [options="header"] |=== |Name |Description |Type |Default Value -a| `addresses` a| – a| `Iterable[str] \| str` a| +a| `addresses` a| – a| `Mapping[str, str] \| Iterable[str] \| str` a| a| `credential` a| – a| `TypeDBCredential` a| |=== diff --git a/python/typedb/api/connection/database.py b/python/typedb/api/connection/database.py index ab7e93d065..923edaae81 100644 --- a/python/typedb/api/connection/database.py +++ b/python/typedb/api/connection/database.py @@ -156,7 +156,7 @@ def database(self) -> Database: @abstractmethod def server(self) -> str: """ - Retrieves the server hosting this replica + The server hosting this replica :return: """ diff --git a/rust/docs/connection/Connection.adoc b/rust/docs/connection/Connection.adoc index 4a63a18e27..5ed9fc23f1 100644 --- a/rust/docs/connection/Connection.adoc +++ b/rust/docs/connection/Connection.adoc @@ -128,6 +128,61 @@ Connection::new_cloud( ) ---- +[#_struct_Connection_new_cloud_with_translation__address_translation_HashMap_T__credential_Credential] +==== new_cloud_with_translation + +[source,rust] +---- +pub fn new_cloud_with_translation( + address_translation: HashMap, + credential: Credential +) -> Resultwhere + T: AsRef + Sync, + U: AsRef + Sync, +---- + +Creates a new TypeDB Cloud connection. + +[caption=""] +.Input parameters +[cols=",,"] +[options="header"] +|=== +|Name |Description |Type +a| `address_translation` a| Translation map from addresses received from the TypeDB server(s) to addresses to be used by the driver for connection a| `HashMapwhere + T: AsRef + Sync, + U: AsRef + Sync, +---- + +[caption=""] +.Code examples +[source,rust] +---- +Connection::new_cloud_with_translation( + [ + ("typedb-cloud.ext:11729", "localhost:11729"), + ("typedb-cloud.ext:21729", "localhost:21729"), + ("typedb-cloud.ext:31729", "localhost:31729"), + ].into(), + Credential::with_tls( + "admin", + "password", + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cloud tests to run"), + )), + )?, +) +---- + [#_struct_Connection_new_core__address_impl_AsRef_str_] ==== new_core diff --git a/rust/docs/connection/ReplicaInfo.adoc b/rust/docs/connection/ReplicaInfo.adoc index 4afca870f3..f84cf2439f 100644 --- a/rust/docs/connection/ReplicaInfo.adoc +++ b/rust/docs/connection/ReplicaInfo.adoc @@ -14,9 +14,9 @@ The metadata and state of an individual raft replica of a database. [options="header"] |=== |Name |Type |Description -a| `address` a| `Address` a| The address of the server hosting this replica a| `is_preferred` a| `bool` a| Whether this is the preferred replica of the raft cluster. If true, Operations which can be run on any replica will prefer to use this replica. a| `is_primary` a| `bool` a| Whether this is the primary replica of the raft cluster. +a| `server` a| `String` a| The server hosting this replica a| `term` a| `i64` a| The raft protocol ‘term’ of this replica. |=== // end::properties[] diff --git a/rust/docs/errors/ConnectionError.adoc b/rust/docs/errors/ConnectionError.adoc index a054d17d61..4d243d8d39 100644 --- a/rust/docs/errors/ConnectionError.adoc +++ b/rust/docs/errors/ConnectionError.adoc @@ -8,6 +8,7 @@ [options="header"] |=== |Variant +a| `AddressTranslationMismatch` a| `BrokenPipe` a| `CloudAllNodesFailed` a| `CloudEndpointEncrypted` diff --git a/rust/docs/errors/InternalError.adoc b/rust/docs/errors/InternalError.adoc index a59d2cc829..78f7c480b5 100644 --- a/rust/docs/errors/InternalError.adoc +++ b/rust/docs/errors/InternalError.adoc @@ -13,7 +13,7 @@ a| `RecvError` a| `SendError` a| `UnexpectedRequestType` a| `UnexpectedResponseType` -a| `UnknownConnectionAddress` +a| `UnknownServer` |=== // end::enum_constants[] From 9e519897bda2f2190450239abf4b6bd14e92133f Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:22:27 +0100 Subject: [PATCH 53/57] emph equal array sizes in C driver --- c/docs/connection/connection.adoc | 4 ++-- c/src/connection.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/c/docs/connection/connection.adoc b/c/docs/connection/connection.adoc index d98b5ea33c..058c5e9323 100644 --- a/c/docs/connection/connection.adoc +++ b/c/docs/connection/connection.adoc @@ -121,8 +121,8 @@ Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translati [options="header"] |=== |Name |Description |Type -a| `advertised_addresses` a| a null-terminated array holding the address(es) the TypeDB server(s) are configured to advertise a| `const char*const*` -a| `translated_addresses` a| a null-terminated array holding the address(es) of the TypeDB server(s) the driver will connect to a| `const char*const*` +a| `advertised_addresses` a| A null-terminated array holding the address(es) the TypeDB server(s) are configured to advertise a| `const char*const*` +a| `translated_addresses` a| A null-terminated array holding the address(es) of the TypeDB server(s) the driver will connect to. This array _must_ have the same length as ``advertised_addresses`` a| `const char*const*` a| `credential` a| The ``Credential`` to connect with a| `const struct Credential*` |=== diff --git a/c/src/connection.rs b/c/src/connection.rs index cacef15a4c..f1b5babcf8 100644 --- a/c/src/connection.rs +++ b/c/src/connection.rs @@ -52,10 +52,10 @@ pub extern "C" fn connection_open_cloud( /// Open a TypeDB Driver to TypeDB Cloud server(s), using provided address translation, with /// the provided credential. /// -/// @param advertised_addresses a null-terminated array holding the address(es) the TypeDB server(s) +/// @param advertised_addresses A null-terminated array holding the address(es) the TypeDB server(s) /// are configured to advertise -/// @param translated_addresses a null-terminated array holding the address(es) of the TypeDB server(s) -/// the driver will connect to +/// @param translated_addresses A null-terminated array holding the address(es) of the TypeDB server(s) +/// the driver will connect to. This array must have the same length as advertised_addresses /// @param credential The Credential to connect with #[no_mangle] pub extern "C" fn connection_open_cloud_translated( From 71a271dd6cb8714b5a532adf1ca06348ee3d86a6 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:29:31 +0100 Subject: [PATCH 54/57] Java: serverID => server --- java/api/database/Database.java | 2 +- java/connection/TypeDBDatabaseImpl.java | 2 +- java/docs/connection/Database.Replica.adoc | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/api/database/Database.java b/java/api/database/Database.java index a193a20b63..a8b5c81fae 100644 --- a/java/api/database/Database.java +++ b/java/api/database/Database.java @@ -119,7 +119,7 @@ interface Replica { * The server hosting this replica */ @CheckReturnValue - String serverID(); + String server(); /** * Checks whether this is the primary replica of the raft cluster. diff --git a/java/connection/TypeDBDatabaseImpl.java b/java/connection/TypeDBDatabaseImpl.java index 73afdf6264..5b7cf56d8e 100644 --- a/java/connection/TypeDBDatabaseImpl.java +++ b/java/connection/TypeDBDatabaseImpl.java @@ -127,7 +127,7 @@ public static class Replica extends NativeObject Date: Thu, 11 Apr 2024 12:35:56 +0100 Subject: [PATCH 55/57] Rust: server_id => server --- rust/src/common/error.rs | 4 ++-- rust/src/database/database.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index 498015e8c0..359fab7ac0 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -86,8 +86,8 @@ error_messages! { InternalError 3: "Unexpected request type for remote procedure call: {request_type}.", UnexpectedResponseType { response_type: String } = 4: "Unexpected response type for remote procedure call: {response_type}.", - UnknownServer { server_id: String } = - 5: "Received replica at unrecognized server: {server_id}.", + UnknownServer { server: String } = + 5: "Received replica at unrecognized server: {server}.", EnumOutOfBounds { value: i32, enum_name: &'static str } = 6: "Value '{value}' is out of bounds for enum '{enum_name}'.", } diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index 408bb9a6fa..8cca837e89 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -182,7 +182,7 @@ impl Database { Err(Error::Connection( ConnectionError::ServerConnectionFailedStatusError { .. } | ConnectionError::ConnectionFailed, )) => { - debug!("Unable to connect to {}. Attempting next server.", replica.server_id); + debug!("Unable to connect to {}. Attempting next server.", replica.server); } res => return res, } @@ -253,8 +253,8 @@ impl fmt::Debug for Database { /// The metadata and state of an individual raft replica of a database. #[derive(Clone)] pub(super) struct Replica { - /// Retrieves id of the server hosting this replica - server_id: String, + /// The server hosting this replica + server: String, /// Retrieves the database name for which this is a replica database_name: String, /// Checks whether this is the primary replica of the raft cluster. @@ -270,7 +270,7 @@ pub(super) struct Replica { impl Replica { fn new(name: String, metadata: ReplicaInfo, server_connection: ServerConnection) -> Self { Self { - server_id: metadata.server, + server: metadata.server, database_name: name.clone(), is_primary: metadata.is_primary, term: metadata.term, @@ -286,7 +286,7 @@ impl Replica { .map(|replica| { let server_connection = connection .connection(&replica.server) - .ok_or_else(|| InternalError::UnknownServer { server_id: replica.server.clone() })?; + .ok_or_else(|| InternalError::UnknownServer { server: replica.server.clone() })?; Ok(Self::new(database_info.name.clone(), replica, server_connection.clone())) }) .collect() @@ -294,7 +294,7 @@ impl Replica { fn to_info(&self) -> ReplicaInfo { ReplicaInfo { - server: self.server_id.clone(), + server: self.server.clone(), is_primary: self.is_primary, is_preferred: self.is_preferred, term: self.term, @@ -303,7 +303,7 @@ impl Replica { #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] async fn fetch_all(name: String, connection: Connection) -> Result> { - for (server_id, server_connection) in connection.connections() { + for (server, server_connection) in connection.connections() { let res = server_connection.get_database_replicas(name.clone()).await; match res { Ok(info) => { @@ -316,7 +316,7 @@ impl Replica { )) => { error!( "Failed to fetch replica info for database '{}' from {}. Attempting next server.", - name, server_id + name, server ); } Err(err) => return Err(err), @@ -329,7 +329,7 @@ impl Replica { impl fmt::Debug for Replica { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Replica") - .field("server_id", &self.server_id) + .field("server", &self.server) .field("database_name", &self.database_name) .field("is_primary", &self.is_primary) .field("term", &self.term) From 5649de24247c96bf1667318e4c713794d63b6a5a Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:45:32 +0100 Subject: [PATCH 56/57] Clean up rust doc --- rust/docs/connection/Connection.adoc | 9 +-------- rust/src/connection/connection.rs | 9 +-------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/rust/docs/connection/Connection.adoc b/rust/docs/connection/Connection.adoc index 5ed9fc23f1..639d3222a1 100644 --- a/rust/docs/connection/Connection.adoc +++ b/rust/docs/connection/Connection.adoc @@ -172,14 +172,7 @@ Connection::new_cloud_with_translation( ("typedb-cloud.ext:21729", "localhost:21729"), ("typedb-cloud.ext:31729", "localhost:31729"), ].into(), - Credential::with_tls( - "admin", - "password", - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cloud tests to run"), - )), - )?, + credential, ) ---- diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index 0ead819d68..d664e98aef 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -154,14 +154,7 @@ impl Connection { /// ("typedb-cloud.ext:21729", "localhost:21729"), /// ("typedb-cloud.ext:31729", "localhost:31729"), /// ].into(), - /// Credential::with_tls( - /// "admin", - /// "password", - /// Some(&PathBuf::from( - /// std::env::var("ROOT_CA") - /// .expect("ROOT_CA environment variable needs to be set for cloud tests to run"), - /// )), - /// )?, + /// credential, /// ) /// ``` pub fn new_cloud_with_translation(address_translation: HashMap, credential: Credential) -> Result From 1840c617f2803d1766a32ec9d053833bfca2d333 Mon Sep 17 00:00:00 2001 From: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:52:24 +0100 Subject: [PATCH 57/57] fix node failover test --- nodejs/test/integration/test-cloud-failover.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nodejs/test/integration/test-cloud-failover.js b/nodejs/test/integration/test-cloud-failover.js index 320d6a18a1..d766f7bb8a 100644 --- a/nodejs/test/integration/test-cloud-failover.js +++ b/nodejs/test/integration/test-cloud-failover.js @@ -115,7 +115,7 @@ async function run() { ); primaryReplica = await seekPrimaryReplica(driver.databases); console.info(`Stopping primary replica (test ${iteration}/10)...`); - const port = primaryReplica.address.substring(10,15); + const port = primaryReplica.server.substring(10,15); const primaryReplicaServerPID = getServerPID(port); console.info(`Primary replica is hosted by server with PID ${primaryReplicaServerPID}`); spawnSync("kill", ["-9", primaryReplicaServerPID]); @@ -129,7 +129,7 @@ async function run() { console.info(`Retrieved entity type with label '${person.label.scopedName}' from new primary replica`); assert(person.label.scopedName === "person"); await session.close(); - const idx = primaryReplica.address[10]; + const idx = primaryReplica.server[10]; serverStart(idx); await new Promise(resolve => setTimeout(resolve, 20000)); const spawned = getServerPID(`${idx}1729`);