use std::sync::mpsc;
use std::time::Duration;
use aerospike_sync::{Bins, Client, ClientPolicy, Statement};
const CLOSED_PORT: &str = "127.0.0.1:1";
const PATIENCE: Duration = Duration::from_secs(15);
fn unreachable_policy() -> ClientPolicy {
ClientPolicy {
timeout: 500,
fail_if_not_connected: true,
..ClientPolicy::default()
}
}
fn within_patience<F>(what: &str, f: F)
where
F: FnOnce() + Send + 'static,
{
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || {
f();
let _ = tx.send(());
});
rx.recv_timeout(PATIENCE)
.unwrap_or_else(|_| panic!("{0} did not return (panicked or hung)", what));
}
#[test]
fn client_new_from_a_plain_thread_needs_no_ambient_runtime() {
within_patience("Client::new on a plain thread", || {
let result = Client::new(&unreachable_policy(), &CLOSED_PORT.to_string());
assert!(
result.is_err(),
"expected a connection error against a closed port"
);
});
}
#[test]
fn blocking_call_inside_a_current_thread_runtime_does_not_deadlock() {
within_patience("Client::new inside a current-thread runtime", || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let result = rt.block_on(async { Client::new(&unreachable_policy(), &CLOSED_PORT.to_string()) });
assert!(result.is_err(), "expected a connection error");
});
}
#[test]
fn blocking_call_inside_a_multi_thread_runtime_works() {
within_patience("Client::new inside a multi-thread runtime", || {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.enable_all()
.build()
.unwrap();
let result = rt.block_on(async { Client::new(&unreachable_policy(), &CLOSED_PORT.to_string()) });
assert!(result.is_err(), "expected a connection error");
});
}
#[test]
fn an_entered_runtime_guard_still_works() {
within_patience("Client::new under an entered runtime guard", || {
let rt = tokio::runtime::Runtime::new().unwrap();
let _guard = rt.enter();
let result = Client::new(&unreachable_policy(), &CLOSED_PORT.to_string());
assert!(result.is_err(), "expected a connection error");
});
}
#[test]
fn query_with_the_blocking_iterator_terminates() {
use aerospike_sync::{
as_bin, as_key, AdminPolicy, PartitionFilter, QueryPolicy, WritePolicy,
};
let hosts = std::env::var("AEROSPIKE_HOSTS").unwrap_or_else(|_| "127.0.0.1:3000".to_string());
let namespace = std::env::var("AEROSPIKE_NAMESPACE").unwrap_or_else(|_| "test".to_string());
let set_name = "sync_blocking_iter";
let policy = ClientPolicy {
use_services_alternate: std::env::var("AEROSPIKE_USE_SERVICES_ALTERNATE")
.map(|v| {
let v = v.trim().to_string();
v.eq_ignore_ascii_case("true") || v == "1"
})
.unwrap_or(false),
..ClientPolicy::default()
};
let client = Client::new(&policy, &hosts).expect("connect");
client
.truncate(&AdminPolicy::default(), &namespace, set_name, 0)
.expect("truncate");
std::thread::sleep(Duration::from_millis(500));
let wpolicy = WritePolicy::default();
for i in 0..10 {
let key = as_key!(namespace.clone(), set_name.to_string(), i);
client
.put(&wpolicy, &key, &[as_bin!("i", i)])
.expect("put");
}
let (tx, rx) = mpsc::channel();
let iterating = std::thread::spawn(move || {
let statement = Statement::new(&namespace, set_name, Bins::All);
let recordset = client
.query(&QueryPolicy::default(), PartitionFilter::all(), statement)
.expect("query");
let mut count = 0;
for record in &*recordset {
record.expect("record");
count += 1;
}
let _ = tx.send(count);
client.close().expect("close");
});
let count = rx
.recv_timeout(PATIENCE)
.expect("blocking iteration never ended");
iterating.join().unwrap();
assert_eq!(count, 10, "expected every record, and then the end");
}