Skip to content

Instantly share code, notes, and snippets.

@dertin
Last active February 19, 2025 17:33
Show Gist options
  • Save dertin/d5e6b031cb86fbe71c6d2272c764dedd to your computer and use it in GitHub Desktop.
Save dertin/d5e6b031cb86fbe71c6d2272c764dedd to your computer and use it in GitHub Desktop.

Revisions

  1. dertin revised this gist Jul 23, 2023. 1 changed file with 4 additions and 6 deletions.
    10 changes: 4 additions & 6 deletions test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -80,12 +80,10 @@ fn odbc_mssql() -> Result<(), Box<dyn std::error::Error>> {
    }

    // Connection string for MSSQL
    let connection_string = "
    Driver={ODBC_Driver_18_for_SQL_Server_Pool};\
    Server=0.0.0.0;\
    UID=SA;\
    PWD=Pepe1234;TrustServerCertificate=Yes;Database=mydatabase;
    ";
    let connection_string = "Driver={ODBC_Driver_18_for_SQL_Server_Pool};\
    Server=0.0.0.0;\
    UID=SA;\
    PWD=Pepe1234;TrustServerCertificate=Yes;Database=mydatabase;";

    let conn: Connection<'_> = connection_pooling(connection_string);
    // let conn = unsafe { conn.promote_to_send() };
  2. dertin revised this gist Jul 16, 2023. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -6,9 +6,14 @@ const NUM_THREADS: u32 = 10; // number of threads to spawn
    const CONDVAR_FLAG: bool = false; // wait for all threads to start then connect to ODBC all at the same time.
    const CONN_IDLE_PAUSE: u64 = 30; // seconds to stay alive connection within each thread

    // Behaviors:

    // It's not a sure thing, just a rough reference.
    // I have to keep investigating

    // if CONDVAR_FLAG == true :
    // PoolMaxSize >= NUM_THREADS ==> OK
    // PoolMaxSize < NUM_THREADS ==> [[unixODBC][Driver Manager]Connection pool at capacity and the wait has timed out]
    // PoolMaxSize < NUM_THREADS ==> OK (if the difference is not much) || [[unixODBC][Driver Manager]Connection pool at capacity and the wait has timed out]

    // if CONDVAR_FLAG == false and (CONN_IDLE_PAUSE + CPTimeout) > PoolWaitTimeout :
    // PoolMaxSize >= NUM_THREADS ==> OK
  3. dertin revised this gist Jul 16, 2023. 1 changed file with 80 additions and 50 deletions.
    130 changes: 80 additions & 50 deletions test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -1,86 +1,116 @@
    use odbc_sys::{AttrConnectionPooling, AttrCpMatch};
    use odbc_api::{Connection, ConnectionOptions, Environment, IntoParameter};
    use std::{sync::OnceLock};
    use odbc_api::{Connection, ConnectionOptions, Environment, IntoParameter, sys::{AttrConnectionPooling, AttrCpMatch}};
    use std::{sync::OnceLock, time::Duration};
    use std::sync::{Arc, Condvar, Mutex};

    // Q: To work with threads do I need to share a single Environment ?
    fn environment_pooling_mssql<'a>() -> &'a Environment {
    pub static ENV: OnceLock<Environment> = OnceLock::new();
    const NUM_THREADS: u32 = 10; // number of threads to spawn
    const CONDVAR_FLAG: bool = false; // wait for all threads to start then connect to ODBC all at the same time.
    const CONN_IDLE_PAUSE: u64 = 30; // seconds to stay alive connection within each thread

    // if CONDVAR_FLAG == true :
    // PoolMaxSize >= NUM_THREADS ==> OK
    // PoolMaxSize < NUM_THREADS ==> [[unixODBC][Driver Manager]Connection pool at capacity and the wait has timed out]

    // if CONDVAR_FLAG == false and (CONN_IDLE_PAUSE + CPTimeout) > PoolWaitTimeout :
    // PoolMaxSize >= NUM_THREADS ==> OK
    // PoolMaxSize < NUM_THREADS ==> [[unixODBC][Driver Manager]Connection pool at capacity and the wait has timed out]

    fn connection_pooling<'a>(connection_string: &str) -> Connection<'a> {

    // Environment initialized only once and safe to share between threads
    let env = {
    pub static ENV: OnceLock<Environment> = OnceLock::new();

    ENV.get_or_init(|| unsafe {

    println!("Environment for connection pooling");

    fn init_env() -> Environment {
    unsafe {
    println!("Environment for MSSQL pool");
    Environment::set_connection_pooling(AttrConnectionPooling::DriverAware).unwrap();

    let mut env = Environment::new().unwrap();

    env.set_connection_pooling_matching(AttrCpMatch::Strict)
    .unwrap();
    env
    }
    }

    ENV.get_or_init(init_env)
    }
    // Q: If I connect multiple times from different threads, are the connections reused? According to ODBC and Driver ?
    fn connection_pooling_mssql<'a>() -> Connection<'a> {
    let env = environment_pooling_mssql();

    let connection_string = "
    Driver={ODBC_Driver_18_for_SQL_Server_Pool};\
    Server=0.0.0.0;\
    UID=SA;\
    PWD=12345678;TrustServerCertificate=Yes;Database=mydatabase;\
    ";
    env
    })
    };

    println!("Connection MSSQL pool");

    let conn = env
    .connect_with_connection_string(connection_string, ConnectionOptions::default())

    let conn: Connection<'a> = env
    .connect_with_connection_string(
    connection_string,
    ConnectionOptions {
    login_timeout_sec: Some(15),
    },
    )
    .unwrap();

    conn
    }

    #[tokio::test]
    async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    let mut handles = vec![];
    fn odbc_mssql() -> Result<(), Box<dyn std::error::Error>> {

    for _ in 0..10 {

    let conn: Connection<'_> = connection_pooling_mssql();

    // Q: Is this safe to send to a thread? Do I need to add Mutex?
    let conn = unsafe { conn.promote_to_send() };

    let counter = Arc::new((Mutex::new(0), Condvar::new()));
    let mut handles = Vec::new();

    //
    // Q: The connections are being reused, how can I verify it?
    //
    for _ in 0..NUM_THREADS {
    let counter_clone = Arc::clone(&counter);
    let handle = std::thread::spawn(move || {

    // If I create a high number of threads => 10000 and connect in each thread, an error is thrown.
    // all connections will fail due to timeout login.
    // see full log => https://gist.github.com/dertin/da9e195e4cf65a021b1e431fc21e5b97
    // Q: Where would it be correct to connect, inside or outside the thread?
    // let conn: Connection<'_> = connection_pooling_mssql();

    let mut prepared = conn.prepare("SELECT * FROM mytable WHERE id=?;").unwrap();
    if CONDVAR_FLAG {
    // Wait for all threads to start then connect to ODBC all at the same time.
    println!("Thread {:?} waits for all threads", std::thread::current().id());

    let (lock, cvar) = &*counter_clone;
    let mut count = lock.lock().unwrap();
    *count += 1;

    if *count < NUM_THREADS {
    count = cvar.wait(count).unwrap();
    } else {
    cvar.notify_all(); // Notify all threads to start
    }
    }

    // Connection string for MSSQL
    let connection_string = "
    Driver={ODBC_Driver_18_for_SQL_Server_Pool};\
    Server=0.0.0.0;\
    UID=SA;\
    PWD=Pepe1234;TrustServerCertificate=Yes;Database=mydatabase;
    ";

    let conn: Connection<'_> = connection_pooling(connection_string);
    // let conn = unsafe { conn.promote_to_send() };

    let title = 1;
    let mut prepared = conn.prepare("SELECT * FROM mytable WHERE id=?;").unwrap();

    match prepared.execute(&IntoParameter::into_parameter(title)) {
    match prepared.execute(&IntoParameter::into_parameter(1)) {
    Err(e) => println!("{}", e),
    Ok(None) => println!("No result set generated."),
    Ok(Some(_cursor)) => {}
    };

    // Stay alive for 10 seconds
    std::thread::sleep(Duration::from_secs(CONN_IDLE_PAUSE));

    // Check if connection is dead
    assert!(!conn.is_dead().unwrap());
    });

    handles.push(handle);
    }

    for handle in handles {
    println!("Thread {:?} started", handle.thread().id());
    handle.join().unwrap();
    }

    Ok(())
    }

    fn main() {}
    fn main() {
    odbc_mssql().unwrap();
    }
  4. dertin revised this gist Jul 15, 2023. 2 changed files with 16 additions and 5 deletions.
    19 changes: 15 additions & 4 deletions odbcinst.ini
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,18 @@
    [ODBC Driver 18 for SQL Server]
    Description=Microsoft ODBC Driver 18 for SQL Server
    [ODBC]
    Trace=Yes
    Trace File=/tmp/sql.log
    Pooling=Yes
    PoolMaxSize=15
    PoolWaitTimeout=30

    [ODBC_Driver_18_for_SQL_Server_Pool]
    Description=Microsoft ODBC Driver 18 for SQL Server - POOL
    Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.2.so.2.1
    UsageCount=1
    Pooling=Yes
    CPTimeout=180
    PoolMaxSize=15

    [ODBC_Driver_18_for_SQL_Server]
    Description=Microsoft ODBC Driver 18 for SQL Server
    Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.2.so.2.1
    UsageCount=1
    CPTimeout=0
    2 changes: 1 addition & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -24,7 +24,7 @@ fn connection_pooling_mssql<'a>() -> Connection<'a> {
    let env = environment_pooling_mssql();

    let connection_string = "
    Driver={ODBC Driver 18 for SQL Server};\
    Driver={ODBC_Driver_18_for_SQL_Server_Pool};\
    Server=0.0.0.0;\
    UID=SA;\
    PWD=12345678;TrustServerCertificate=Yes;Database=mydatabase;\
  5. dertin revised this gist Jul 12, 2023. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions odbcinst.ini
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,6 @@
    Description=Microsoft ODBC Driver 18 for SQL Server
    Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.2.so.2.1
    UsageCount=1
    Pooling = Yes
    CPTimeout = 180
    PoolMaxSize = 15
    Pooling=Yes
    CPTimeout=180
    PoolMaxSize=15
  6. dertin revised this gist Jul 12, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -52,7 +52,7 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    //
    // Q: The connections are being reused, how can I verify it?
    //
    let handle = thread::spawn(move || {
    let handle = std::thread::spawn(move || {

    // If I create a high number of threads => 10000 and connect in each thread, an error is thrown.
    // all connections will fail due to timeout login.
  7. dertin revised this gist Jul 12, 2023. No changes.
  8. dertin revised this gist Jul 11, 2023. 1 changed file with 9 additions and 0 deletions.
    9 changes: 9 additions & 0 deletions Cargo.toml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    [package]
    name = "rust-pooldb"
    version = "0.1.0"
    edition = "2021"

    [dependencies]
    tokio = { version = "1.29.1", features = ["full"] }
    odbc-sys = "0.21.4"
    odbc-api = "0.57.0"
  9. dertin revised this gist Jul 10, 2023. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@ use odbc_sys::{AttrConnectionPooling, AttrCpMatch};
    use odbc_api::{Connection, ConnectionOptions, Environment, IntoParameter};
    use std::{sync::OnceLock};

    // To work with threads do I need to share a single Environment ?
    // Q: To work with threads do I need to share a single Environment ?
    fn environment_pooling_mssql<'a>() -> &'a Environment {
    pub static ENV: OnceLock<Environment> = OnceLock::new();

    @@ -19,7 +19,7 @@ fn environment_pooling_mssql<'a>() -> &'a Environment {

    ENV.get_or_init(init_env)
    }
    // If I connect multiple times from different threads, are the connections reused? According to ODBC and Driver ?
    // Q: If I connect multiple times from different threads, are the connections reused? According to ODBC and Driver ?
    fn connection_pooling_mssql<'a>() -> Connection<'a> {
    let env = environment_pooling_mssql();

  10. dertin revised this gist Jul 10, 2023. 1 changed file with 7 additions and 2 deletions.
    9 changes: 7 additions & 2 deletions test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -45,14 +45,19 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    for _ in 0..10 {

    let conn: Connection<'_> = connection_pooling_mssql();

    // Q: Is this safe to send to a thread? Do I need to add Mutex?
    let conn = unsafe { conn.promote_to_send() };

    //
    // The connections are being reused, how can I verify it?
    // Q: The connections are being reused, how can I verify it?
    //
    let handle = thread::spawn(move || {

    // If I create a high number of threads => 10000 and connect in each thread, an error is thrown.
    // after a number of threads establishing ODBC connections, all connections will fail due to timeout login.
    // all connections will fail due to timeout login.
    // see full log => https://gist.github.com/dertin/da9e195e4cf65a021b1e431fc21e5b97
    // Q: Where would it be correct to connect, inside or outside the thread?
    // let conn: Connection<'_> = connection_pooling_mssql();

    let mut prepared = conn.prepare("SELECT * FROM mytable WHERE id=?;").unwrap();
  11. dertin revised this gist Jul 10, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -51,7 +51,7 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    //
    let handle = thread::spawn(move || {
    // If I create a high number of threads => 10000 and connect in each thread, an error is thrown.
    // after a number of threads establishing ODBC connections, all connections will fail.
    // after a number of threads establishing ODBC connections, all connections will fail due to timeout login.
    // see full log => https://gist.github.com/dertin/da9e195e4cf65a021b1e431fc21e5b97
    // let conn: Connection<'_> = connection_pooling_mssql();

  12. dertin revised this gist Jul 10, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -51,7 +51,7 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    //
    let handle = thread::spawn(move || {
    // If I create a high number of threads => 10000 and connect in each thread, an error is thrown.
    // after a number of iterations establishing ODBC connections, all connections will fail.
    // after a number of threads establishing ODBC connections, all connections will fail.
    // see full log => https://gist.github.com/dertin/da9e195e4cf65a021b1e431fc21e5b97
    // let conn: Connection<'_> = connection_pooling_mssql();

  13. dertin revised this gist Jul 10, 2023. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -50,8 +50,8 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    // The connections are being reused, how can I verify it?
    //
    let handle = thread::spawn(move || {
    // If I connect to each thread, an error is thrown.
    // After a number of iterations establishing ODBC connections, all connections will fail.
    // If I create a high number of threads => 10000 and connect in each thread, an error is thrown.
    // after a number of iterations establishing ODBC connections, all connections will fail.
    // see full log => https://gist.github.com/dertin/da9e195e4cf65a021b1e431fc21e5b97
    // let conn: Connection<'_> = connection_pooling_mssql();

  14. dertin revised this gist Jul 10, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -52,7 +52,7 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    let handle = thread::spawn(move || {
    // If I connect to each thread, an error is thrown.
    // After a number of iterations establishing ODBC connections, all connections will fail.
    // see file: panic_conn_in_thread.log
    // see full log => https://gist.github.com/dertin/da9e195e4cf65a021b1e431fc21e5b97
    // let conn: Connection<'_> = connection_pooling_mssql();

    let mut prepared = conn.prepare("SELECT * FROM mytable WHERE id=?;").unwrap();
  15. dertin revised this gist Jul 10, 2023. 1 changed file with 0 additions and 80391 deletions.
    80,391 changes: 0 additions & 80,391 deletions panic_conn_in_thread.log
    0 additions, 80,391 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  16. dertin revised this gist Jul 10, 2023. 2 changed files with 80396 additions and 0 deletions.
    80,391 changes: 80,391 additions & 0 deletions panic_conn_in_thread.log
    80,391 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
    5 changes: 5 additions & 0 deletions test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -50,6 +50,11 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    // The connections are being reused, how can I verify it?
    //
    let handle = thread::spawn(move || {
    // If I connect to each thread, an error is thrown.
    // After a number of iterations establishing ODBC connections, all connections will fail.
    // see file: panic_conn_in_thread.log
    // let conn: Connection<'_> = connection_pooling_mssql();

    let mut prepared = conn.prepare("SELECT * FROM mytable WHERE id=?;").unwrap();

    let title = 1;
  17. dertin revised this gist Jul 10, 2023. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,7 @@ use odbc_sys::{AttrConnectionPooling, AttrCpMatch};
    use odbc_api::{Connection, ConnectionOptions, Environment, IntoParameter};
    use std::{sync::OnceLock};

    // To work with threads do I need to share a single Environment ?
    fn environment_pooling_mssql<'a>() -> &'a Environment {
    pub static ENV: OnceLock<Environment> = OnceLock::new();

    @@ -18,7 +19,7 @@ fn environment_pooling_mssql<'a>() -> &'a Environment {

    ENV.get_or_init(init_env)
    }

    // If I connect multiple times from different threads, are the connections reused? According to ODBC and Driver ?
    fn connection_pooling_mssql<'a>() -> Connection<'a> {
    let env = environment_pooling_mssql();

    @@ -42,6 +43,7 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    let mut handles = vec![];

    for _ in 0..10 {

    let conn: Connection<'_> = connection_pooling_mssql();
    let conn = unsafe { conn.promote_to_send() };
    //
  18. dertin revised this gist Jul 9, 2023. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -44,7 +44,9 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    for _ in 0..10 {
    let conn: Connection<'_> = connection_pooling_mssql();
    let conn = unsafe { conn.promote_to_send() };

    //
    // The connections are being reused, how can I verify it?
    //
    let handle = thread::spawn(move || {
    let mut prepared = conn.prepare("SELECT * FROM mytable WHERE id=?;").unwrap();

  19. dertin revised this gist Jul 9, 2023. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion odbcinst.ini
    Original file line number Diff line number Diff line change
    @@ -3,4 +3,5 @@ Description=Microsoft ODBC Driver 18 for SQL Server
    Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.2.so.2.1
    UsageCount=1
    Pooling = Yes
    CPTimeout = 180
    CPTimeout = 180
    PoolMaxSize = 15
  20. dertin revised this gist Jul 9, 2023. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion odbcinst.ini
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,6 @@
    [ODBC Driver 18 for SQL Server]
    Description=Microsoft ODBC Driver 18 for SQL Server
    Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.2.so.2.1
    UsageCount=1
    UsageCount=1
    Pooling = Yes
    CPTimeout = 180
  21. dertin revised this gist Jul 9, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -42,7 +42,7 @@ async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    let mut handles = vec![];

    for _ in 0..10 {
    let conn: odbc_api::connection::Connection<'_> = connection_pooling_mssql();
    let conn: Connection<'_> = connection_pooling_mssql();
    let conn = unsafe { conn.promote_to_send() };

    let handle = thread::spawn(move || {
  22. dertin revised this gist Jul 9, 2023. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions odbcinst.ini
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,4 @@
    [ODBC Driver 18 for SQL Server]
    Description=Microsoft ODBC Driver 18 for SQL Server
    Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.2.so.2.1
    UsageCount=1
  23. dertin created this gist Jul 9, 2023.
    72 changes: 72 additions & 0 deletions test_odbc_pool_mssql.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,72 @@
    use odbc_sys::{AttrConnectionPooling, AttrCpMatch};
    use odbc_api::{Connection, ConnectionOptions, Environment, IntoParameter};
    use std::{sync::OnceLock};

    fn environment_pooling_mssql<'a>() -> &'a Environment {
    pub static ENV: OnceLock<Environment> = OnceLock::new();

    fn init_env() -> Environment {
    unsafe {
    println!("Environment for MSSQL pool");
    Environment::set_connection_pooling(AttrConnectionPooling::DriverAware).unwrap();
    let mut env = Environment::new().unwrap();
    env.set_connection_pooling_matching(AttrCpMatch::Strict)
    .unwrap();
    env
    }
    }

    ENV.get_or_init(init_env)
    }

    fn connection_pooling_mssql<'a>() -> Connection<'a> {
    let env = environment_pooling_mssql();

    let connection_string = "
    Driver={ODBC Driver 18 for SQL Server};\
    Server=0.0.0.0;\
    UID=SA;\
    PWD=12345678;TrustServerCertificate=Yes;Database=mydatabase;\
    ";

    println!("Connection MSSQL pool");

    let conn = env
    .connect_with_connection_string(connection_string, ConnectionOptions::default())
    .unwrap();
    conn
    }

    #[tokio::test]
    async fn test_odbc_pool_mssql() -> Result<(), Box<dyn std::error::Error>> {
    let mut handles = vec![];

    for _ in 0..10 {
    let conn: odbc_api::connection::Connection<'_> = connection_pooling_mssql();
    let conn = unsafe { conn.promote_to_send() };

    let handle = thread::spawn(move || {
    let mut prepared = conn.prepare("SELECT * FROM mytable WHERE id=?;").unwrap();

    let title = 1;

    match prepared.execute(&IntoParameter::into_parameter(title)) {
    Err(e) => println!("{}", e),
    Ok(None) => println!("No result set generated."),
    Ok(Some(_cursor)) => {}
    };

    assert!(!conn.is_dead().unwrap());
    });

    handles.push(handle);
    }

    for handle in handles {
    handle.join().unwrap();
    }

    Ok(())
    }

    fn main() {}