mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Bug #608032 - MySQL timeout and no attempt reconnect, second version
This version builds on Phil's implementation of the dbi error callback functions to test for a timeout and to do the reconnect. The same error handling is equally implemented for postgres and sqlite. Unlike MySQL these two database types don't actually generate timeouts, but the functionality can be used later on for other error types as well. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18706 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
@@ -149,8 +149,38 @@ typedef struct
|
||||
dbi_conn conn;
|
||||
/*@ observer @*/
|
||||
provider_functions_t* provider;
|
||||
gint last_error; // Code of the last error that occurred. This is set in the error callback function
|
||||
gint error_repeat; // Used in case of transient errors. After such error, another attempt at the
|
||||
// original call is allowed. error_repeat tracks the number of attempts and can
|
||||
// be used to prevent infinite loops.
|
||||
gboolean retry; // Signals the calling function that it should retry (the error handler detected
|
||||
// transient error and managed to resolve it, but it can't run the original query)
|
||||
} GncDbiSqlConnection;
|
||||
|
||||
#define DBI_MAX_CONN_ATTEMPTS 5
|
||||
|
||||
/* ================================================================= */
|
||||
|
||||
static void
|
||||
gnc_dbi_set_error( GncDbiSqlConnection* dbi_conn, gint last_error,
|
||||
gint error_repeat, gboolean retry )
|
||||
{
|
||||
g_return_if_fail( dbi_conn != NULL );
|
||||
|
||||
dbi_conn->last_error = last_error;
|
||||
if ( error_repeat > 0 )
|
||||
dbi_conn->error_repeat = dbi_conn->error_repeat + error_repeat;
|
||||
else
|
||||
dbi_conn->error_repeat = 0;
|
||||
dbi_conn->retry = retry;
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_dbi_init_error( GncDbiSqlConnection* dbi_conn )
|
||||
{
|
||||
gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_ERR, 0, FALSE );
|
||||
}
|
||||
|
||||
/* ================================================================= */
|
||||
|
||||
static void
|
||||
@@ -171,11 +201,13 @@ create_tables_cb( const gchar* type, gpointer data_p, gpointer be_p )
|
||||
static void
|
||||
sqlite3_error_fn( dbi_conn conn, /*@ unused @*/ void* user_data )
|
||||
{
|
||||
//GncDbiBackend *be = (GncDbiBackend*)user_data;
|
||||
GncDbiBackend *be = (GncDbiBackend*)user_data;
|
||||
GncDbiSqlConnection *dbi_conn = (GncDbiSqlConnection*)be->sql_be.conn;
|
||||
const gchar* msg;
|
||||
|
||||
(void)dbi_conn_error( conn, &msg );
|
||||
PERR( "DBI error: %s\n", msg );
|
||||
gnc_dbi_set_error( dbi_conn, ERR_BACKEND_MISC, 0, FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -278,17 +310,35 @@ static void
|
||||
mysql_error_fn( dbi_conn conn, void* user_data )
|
||||
{
|
||||
GncDbiBackend *be = (GncDbiBackend*)user_data;
|
||||
GncDbiSqlConnection *dbi_conn = (GncDbiSqlConnection*)be->sql_be.conn;
|
||||
const gchar* msg;
|
||||
gint err_num;
|
||||
|
||||
(void)dbi_conn_error( conn, &msg );
|
||||
if ( g_str_has_prefix( msg, "1049: Unknown database" ) )
|
||||
err_num = dbi_conn_error( conn, &msg );
|
||||
if ( err_num == 1049 ) // Database doesn't exist
|
||||
{
|
||||
PINFO( "DBI error: %s\n", msg );
|
||||
be->exists = FALSE;
|
||||
gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_SUCH_DB, 0, FALSE );
|
||||
}
|
||||
else
|
||||
else if ( err_num == 2006 ) // Server has gone away
|
||||
{
|
||||
if (dbi_conn->error_repeat > DBI_MAX_CONN_ATTEMPTS )
|
||||
{
|
||||
PERR( "DBI error: %s - Failed to reconnect after %d attempts.\n", msg, DBI_MAX_CONN_ATTEMPTS );
|
||||
gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 0, FALSE );
|
||||
} else
|
||||
{
|
||||
PINFO( "DBI error: %s - Reconnecting...\n", msg );
|
||||
gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CONN_LOST, 1, TRUE );
|
||||
|
||||
(void)dbi_conn_connect( conn );
|
||||
}
|
||||
}
|
||||
else // Any other error
|
||||
{
|
||||
PERR( "DBI error: %s\n", msg );
|
||||
gnc_dbi_set_error( dbi_conn, ERR_BACKEND_MISC, 0, FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,6 +561,7 @@ static void
|
||||
pgsql_error_fn( dbi_conn conn, void* user_data )
|
||||
{
|
||||
GncDbiBackend *be = (GncDbiBackend*)user_data;
|
||||
GncDbiSqlConnection *dbi_conn = (GncDbiSqlConnection*)be->sql_be.conn;
|
||||
const gchar* msg;
|
||||
|
||||
(void)dbi_conn_error( conn, &msg );
|
||||
@@ -519,10 +570,12 @@ pgsql_error_fn( dbi_conn conn, void* user_data )
|
||||
{
|
||||
PINFO( "DBI error: %s\n", msg );
|
||||
be->exists = FALSE;
|
||||
gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_SUCH_DB, 0, FALSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
PERR( "DBI error: %s\n", msg );
|
||||
gnc_dbi_set_error( dbi_conn, ERR_BACKEND_MISC, 0, FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,7 +855,10 @@ gnc_dbi_sync_all( QofBackend* qbe, /*@ dependent @*/ QofBook *book )
|
||||
const gchar* table_name = (const gchar*)node->data;
|
||||
dbi_result result;
|
||||
|
||||
result = dbi_conn_queryf( be->conn, "DROP TABLE %s", table_name );
|
||||
do {
|
||||
gnc_dbi_init_error( ((GncDbiSqlConnection*)(be->sql_be.conn)) );
|
||||
result = dbi_conn_queryf( be->conn, "DROP TABLE %s", table_name );
|
||||
} while ( ((GncDbiSqlConnection*)(be->sql_be.conn))->retry );
|
||||
if ( result != NULL )
|
||||
{
|
||||
status = dbi_result_free( result );
|
||||
@@ -1416,7 +1472,10 @@ conn_execute_select_statement( GncSqlConnection* conn, GncSqlStatement* stmt )
|
||||
dbi_result result;
|
||||
|
||||
DEBUG( "SQL: %s\n", dbi_stmt->sql->str );
|
||||
result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str );
|
||||
do {
|
||||
gnc_dbi_init_error( dbi_conn );
|
||||
result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str );
|
||||
} while ( dbi_conn->retry );
|
||||
if ( result == NULL )
|
||||
{
|
||||
PERR( "Error executing SQL %s\n", dbi_stmt->sql->str );
|
||||
@@ -1435,7 +1494,10 @@ conn_execute_nonselect_statement( GncSqlConnection* conn, GncSqlStatement* stmt
|
||||
gint status;
|
||||
|
||||
DEBUG( "SQL: %s\n", dbi_stmt->sql->str );
|
||||
result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str );
|
||||
do {
|
||||
gnc_dbi_init_error( dbi_conn );
|
||||
result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str );
|
||||
} while( dbi_conn->retry );
|
||||
if ( result == NULL )
|
||||
{
|
||||
PERR( "Error executing SQL %s\n", dbi_stmt->sql->str );
|
||||
@@ -1513,15 +1575,11 @@ conn_begin_transaction( /*@ unused @*/ GncSqlConnection* conn )
|
||||
gint status;
|
||||
|
||||
DEBUG( "BEGIN\n" );
|
||||
result = dbi_conn_queryf( dbi_conn->conn, "BEGIN" );
|
||||
|
||||
/* Handle MySQL connection timeouts with reconnect */
|
||||
if (result == NULL && dbi_conn_error( dbi_conn->conn, NULL ) == 2006 )
|
||||
{
|
||||
DEBUG( "MySQL server has gone away, reconnecting and retrying...\n" );
|
||||
(void)dbi_conn_connect( dbi_conn->conn );
|
||||
do {
|
||||
gnc_dbi_init_error( dbi_conn );
|
||||
result = dbi_conn_queryf( dbi_conn->conn, "BEGIN" );
|
||||
}
|
||||
} while( dbi_conn->retry );
|
||||
|
||||
status = dbi_result_free( result );
|
||||
if ( status < 0 )
|
||||
@@ -2017,6 +2075,8 @@ create_dbi_connection( /*@ observer @*/ provider_functions_t* provider,
|
||||
dbi_conn->conn = conn;
|
||||
dbi_conn->provider = provider;
|
||||
|
||||
gnc_dbi_init_error(dbi_conn);
|
||||
|
||||
return (GncSqlConnection*)dbi_conn;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user