Question

In an XMLRPC server that I'm working on (based off xmlrpc-c) the threads may want to make a MySQL connection to retrieve some data, using the following function:

Distribution getEntitySetFromMysql( int id ) {

    Distribution result;

    try {
        sql::Driver *driver = get_driver_instance();
        sql::Connection *con = driver->connect( (std::string)DBHOST, (std::string)USER, (std::string)PASSWORD);
        con->setSchema( (std::string)DATABASE );

        sql::Statement *stmt = con->createStatement();
        std::stringstream query;
        query << "SELECT concept_id, weight FROM entity_set_lines WHERE entity_set_id = " << id;
        sql::ResultSet *res = stmt->executeQuery ( query.str() );

        while (res->next()) {
            result[ res->getInt("concept_id") ] = res->getDouble("weight");
        }

        delete res;
        delete stmt;
        con->close();
        delete con;

    } catch (sql::SQLException &e) {
        std::cout << "ERROR: SQLException in " << __FILE__;
        std::cout << " (" << __func__<< ") on line " << __LINE__ << std::endl;
        std::cout << "ERROR: " << e.what();
        std::cout << " (MySQL error code: " << e.getErrorCode();
        std::cout << ", SQLState: " << e.getSQLState() << ")" << std::endl;

        if (e.getErrorCode() == 1047) {
            std::cout << "\nYour server does not seem to support Prepared Statements at all. ";
            std::cout << "Perhaps MYSQL < 4.1?" << std::endl;
        }

    } catch (std::runtime_error &e) {

        std::cout << "ERROR: runtime_error in " << __FILE__;
        std::cout << " (" << __func__ << ") on line " << __LINE__ << std::endl;
        std::cout << "ERROR: " << e.what() << std::endl;

    }

    return result;
}

All works fine, but after a thread runs this code and successfully returns its result, the thread remains hanging and does not exit. What is wrong with this approach? How fundamentaly wrong is this? Is the MySQL connector thread safe?

Was it helpful?

Solution

While Googling around for a solutions, I came across mentions of sql::Driver::threadInit() and sql::Driver::threadEnd(). However, as I was on version 1.0.5 of the C++ Connector, these functions were not available to me. Adding a driver->threadInit(); after getting a driver instance and driver->threadEnd(); at the end of my function, this problem was resolved.

The following is the mention of this thread init and end functionality in MySQL's 1.1.0 change history:

Added Driver::threadInit() and Driver::threadEnd() methods. Every thread of a threaded client must call Driver::threadInit() at the very start of the thread before it does anything else with Connector/C++ and every thread must call Driver::threadEnd() when it finishes. You can find an example demonstrating the use in examples/pthreads.cpp. It is strongly discouraged to share connections between threads. It is theoretically possible, if you set certain (undocumented) mutexes, but it is not supported at all. Use one connection per thread. Do not have two threads using the same connection at the same time. Please check the C API notes on threading on the MySQL manual. Connector/C++ wraps the C API. (Lawrin, Andrey, Ulf)

TL;DR: If you come across this problem, make sure that your version of the C++ MySQL Connector is >= 1.1.0 and use the sql::Driver::threadInit() and sql::Driver::threadEnd() methods to surround your connection code.

OTHER TIPS

Two thoughts:

  1. libmysql isn't fully thread safe.
  2. the way your code is structured you will leak memory if an exception occurs. you might be better served by either declaring your variables outside the try/catch and using a finally (or local equivalent) to ensure proper cleanup or using smart pointers (if available).

Since you don't show any of the calling or surrounding code it's hard to tell what's actually going on. Do you check the exit code of the thread when it's supposedly done? Can you attach it in a debugger to see what it's doing instead of closing?

Actually :

DO NOT USE : sql::Driver::threadInit() nor sql::Driver::threadEnd()

BECAUSE : you are already using try()

YOU FORGOT :

res->close();
stmt->close();
con->close();

delete res;
delete stmt;
delete con;

EXAMPLE :

int connection_and_query_func()
{
     /*connection and query variables*/
     sql::Driver *driver;
     sql::Connection *con;
     sql::Statement *stmt;
     sql::ResultSet *res;
     int err_exception_getErrorCode=0;

     /*results variables*/
     int my_int_from_column_1 = 0;
     double my_double_from_column_2 = 0;
     ....
     std:string my_string_from_column_p = "";

 try
    {
        /* Create a connection */
        driver = get_driver_instance();
        con = driver->connect("address_name", "user_name", "password");

        /* Connect to the MySQL database */
        con->setSchema("schema_name");

        /* Execute MySQL Query*/
        stmt = con->createStatement();
        res = stmt->executeQuery("your query statement here");

        /* Read MySQL Query results per column*/
        my_int_from_column_1 = res->getInt(1);
        my_double_from_column_2 = res->getDouble(2);
        ....
        my_string_from_column_p = res->getString(p);

        /* Close MySQL Connection*/
        res->close();
        stmt->close();
        con->close();

        delete res;
        delete stmt;
        delete con;
    };

 /* Get last error*/
 catch (sql::SQLException &exception)
    {
        err_exception_getErrorCode = exception.getErrorCode();
    };

 return(0);
};

CONCLUSION : this can be executed as many times as you want. The function example (connection_and_query_func()) will close MySQL connection properly after it is done - without adding up processes to your MySQL server!!!

FURTHERMORE : read the official manual https://docs.oracle.com/cd/E17952_01/connector-cpp-en/connector-cpp-en.pdf

ALTERNATIVE : if you cannot close properly your connection and query from your program/function side (thus adding up processes to your MySQL server) consider the 2 following options:

1/ set all MySQL timeout parameters to 10 sec. or less (for instance); 2/ write a script that SHOW PROCESSLIST and delete processes that are in SLEEP for too long.

Cheers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top