Russian Qt Forum

Qt => Базы данных => Тема начата: ksergey85 от Сентябрь 16, 2015, 16:39



Название: Проверка состояния подключения к БД.
Отправлено: ksergey85 от Сентябрь 16, 2015, 16:39
Пользователи драйвера QODBC, подскажите пожалуйста как вы проверяете состояние коннекта к БД на предмет умерло/не умерло? Не дело ведь это выполнять "SELECT 1" после каждого неудачно выполненного запроса только для того, чтобы проверить не отвалился ли коннект. Или как-то по коду ошибки QSqlError все-таки можно догадаться, что запрос обломался именно по причине отпавшего коннекта.  ???
Погуглив, нашел только вот что https://bugreports.qt.io/browse/QTBUG-223 (https://bugreports.qt.io/browse/QTBUG-223). Закрыто в 2013. Никаких вразумительных комментариев.


Название: Re: Проверка состояния подключения к БД.
Отправлено: Old от Сентябрь 16, 2015, 17:58
Не знаю что там есть для ODBC, а для postgresql и mysql я в отдельном потоке с заданным интервалом проверяю валидность соединения и при его разрыве посылаю сигнал об этом.


Название: Re: Проверка состояния подключения к БД.
Отправлено: ksergey85 от Сентябрь 16, 2015, 18:05
Не знаю что там есть для ODBC, а для postgresql и mysql я в отдельном потоке с заданным интервалом проверяю валидность соединения и при его разрыве посылаю сигнал об этом.

Как проверяете? Можно кода?


Название: Re: Проверка состояния подключения к БД.
Отправлено: Old от Сентябрь 16, 2015, 18:14
Как проверяете? Можно кода?
Кода можно будет только завтра, сейчас смогу только на словах.
Создаем отдельное тестовое подключение, запускаем поток, в котором по таймеру выполняется слот.
В слоте проверяем валидность подключения проверяя открыто ли соединение и пытаясь выполнить запрос "SELECT TRUE", если все нормально - выходим из слота. Иначе посылаем сигнал об отключении базы и пытаемся переподключиться.


Название: Re: Проверка состояния подключения к БД.
Отправлено: Nidxogg от Сентябрь 17, 2015, 20:05
Цитировать
В слоте проверяем валидность подключения проверяя открыто ли соединение
Если речь про isOpen, то оно true выдает даже после разрыва связи с БД. Разве нет?
Цитировать
и пытаясь выполнить запрос "SELECT TRUE", если все нормально - выходим из слота.
Результат запроса (false) у вас сразу приходит или с таймаутом каким-то?


Название: Re: Проверка состояния подключения к БД.
Отправлено: Old от Сентябрь 17, 2015, 20:14
Если речь про isOpen, то оно true выдает даже после разрыва связи с БД. Разве нет?
Да, поэтому и нужен второй запрос.

Результат запроса (false) у вас сразу приходит или с таймаутом каким-то?
Сразу. Отдельная нитка нужна, что бы попытка переподключения не блокировала основную нить и не морозила UI. Открытие соединения с не отвечающим сервером БД заметно тормозит нить.


Название: Re: Проверка состояния подключения к БД.
Отправлено: Nidxogg от Сентябрь 17, 2015, 20:20
Понятно, тогда тоже +1 к посмотреть код
(интересует как сама нитка создается и подключение в ней)


Название: Re: Проверка состояния подключения к БД.
Отправлено: Old от Сентябрь 17, 2015, 20:24
Понятно, тогда тоже +1 к посмотреть код
(интересует как сама нитка создается и подключение в ней)
Если я утром забуду, напишите сообщение в этой теме еще раз (или в личку) - я выложу.


Название: Re: Проверка состояния подключения к БД.
Отправлено: Old от Сентябрь 18, 2015, 10:23
Код
C++ (Qt)
class DbWatcher : public QThread
{
Q_OBJECT
public:
explicit DbWatcher();
explicit DbWatcher( int interval, QSqlDatabase db );
 
int interval() const;
void setInterval( int val );
 
bool isConnected() const;
QSqlDatabase connection() const;
void setConnection( QSqlDatabase db );
 
signals:
void connected();
void disconnected();
 
protected slots:
void reconnect();
 
protected:
virtual void run();
 
int m_interval;
QSqlDatabase m_db;
QTimer *m_timer;
bool m_connOk;
mutable QMutex m_mutex;
};
 

Код
C++ (Qt)
DbWatcher::DbWatcher() :
QThread( 0 ),
m_interval( 0 ),
m_db(),
m_timer( 0 ),
m_connOk( false )
{
moveToThread( this );
}
 
DbWatcher::DbWatcher( int interval, QSqlDatabase db ) :
QThread( 0 ),
m_interval( interval ),
m_db( db ),
m_timer( 0 ),
m_connOk( false )
{
moveToThread( this );
}
 
int DbWatcher::interval() const
{
QMutexLocker lock( &m_mutex );
return m_interval;
}
 
void DbWatcher::setInterval( int val )
{
QMutexLocker lock( &m_mutex );
m_interval = val;
}
 
bool DbWatcher::isConnected() const
{
QMutexLocker lock( &m_mutex );
return m_db.isOpen() && m_db.exec( "SELECT TRUE" ).isActive();
}
 
QSqlDatabase DbWatcher::connection() const
{
QMutexLocker lock( &m_mutex );
return m_db;
}
 
void DbWatcher::setConnection( QSqlDatabase db )
{
QMutexLocker lock( &m_mutex );
m_db = db;
}
 
void DbWatcher::reconnect()
{
Q_ASSERT( m_timer );
 
if( isConnected() )
return;
 
m_timer->stop();
 
if( m_connOk )
{
m_connOk = false;
emit disconnected();
}
 
m_db.open();
 
if( isConnected() )
{
m_connOk = true;
emit connected();
}
 
m_timer->start();
}
 
void DbWatcher::run()
{
m_timer = new QTimer;
connect( m_timer, SIGNAL(timeout()), SLOT(reconnect()) );
m_timer->setSingleShot( false );
m_timer->setInterval( m_interval * 1000 );
m_timer->start();
 
QMetaObject::invokeMethod( this, "reconnect", Qt::QueuedConnection );
 
exec();
 
m_timer->stop();
delete m_timer;
}
 


Название: Re: Проверка состояния подключения к БД.
Отправлено: Nidxogg от Сентябрь 18, 2015, 19:09
Цитировать
Создаем отдельное тестовое подключение
Имеется ввиду addDatabase?
Судя по вашему коду используется defaultConnection?


Название: Re: Проверка состояния подключения к БД.
Отправлено: Old от Сентябрь 18, 2015, 19:30
Цитировать
Создаем отдельное тестовое подключение
Имеется ввиду addDatabase?
Судя по вашему коду используется defaultConnection?
Нет. Мы передаем соединение, которое будет использоваться для тестирования.
Я создаю рабочее соединение, клонирую его и клон передаю ватчеру для проверки. Для каждого потока нужно отдельное соединение.


Название: Re: Проверка состояния подключения к БД.
Отправлено: Nidxogg от Сентябрь 18, 2015, 19:40
А если в процессе работы требуется отключиться от БД
Цитировать
void QSqlDatabase::removeDatabase ( const QString & connectionName )
и подключиться с другими параметрами - потребуется ли пересоздать ватчер?


Название: Re: Проверка состояния подключения к БД.
Отправлено: Old от Сентябрь 18, 2015, 21:00
А если в процессе работы требуется отключиться от БД
Цитировать
void QSqlDatabase::removeDatabase ( const QString & connectionName )
и подключиться с другими параметрами - потребуется ли пересоздать ватчер?
Нет. Достаточно будет сделать setConnection с клоном нового подключения.


Название: Re: Проверка состояния подключения к БД.
Отправлено: Nidxogg от Сентябрь 18, 2015, 22:37
Спасибо за информацию


Название: Re: Проверка состояния подключения к БД.
Отправлено: Nidxogg от Декабрь 03, 2015, 20:42
del


Название: Re: Проверка состояния подключения к БД.
Отправлено: titan83 от Март 11, 2016, 18:15
Коллеги, чтобы не плодить темы спрошу здесь.
У меня тоже ODBC соединение, все подключается, все здорово.
Есть функционал проверки доступности подключения и переподключения при разрыве соединения.
И вот здесь у меня проблема: принципиально все работает, но память течет(
После каждого вызова open() для QSqlDatabase я теряю ~70кб, а так как проверка доступности сервера выполняется довольно часто (раз в 30 секунд), то память течет со скоростью 1Мб в час, мне это крайне не нравится. Но как это исправить пока ума не приложу.
QWshDatabase::open() - это слот, который вызывается при каждой попытке переподключения. И после каждого _db.open() я теряю память.
Кто как решает эту проблему?
Спасибо.

Код:
QWshDatabase::QWshDatabase()
{
    _db = QSqlDatabase::addDatabase("QODBC");
    _query = new QSqlQuery(_db);
    connect(this, SIGNAL(queryFail(QString)), this, SLOT(close()));
}

QWshDatabase::~QWshDatabase()
{
    delete _query;
}

bool QWshDatabase::open()
{
    if (!(_db.isOpen())) {
        _db.setDatabaseName(connectionName());
        _db.setUserName(username());
        _db.setPassword(password());

        if (_db.open()) {
            emit openSuccess();
            _prevConnetionWasSuccess = true;
            return true;
        } else {
            qDebug() << "db open fail";
            if (_prevConnetionWasSuccess)
                emit openFail("Database: open failed with error - " + _db.lastError().text());
            else
                emit openFail("Database: try to restore connection");

            _prevConnetionWasSuccess = false;
            return false;
        }
    }

    return true;
}


Название: Re: Проверка состояния подключения к БД.
Отправлено: Old от Март 11, 2016, 18:30
Ну так загляните в исходники драйвера для ODBC, посмотрите что именно выделяется (какая структура данных?), кто ее должен освобождать, когда...
Может нужно просто делать close соединению, при обнаружении отключения.


Название: Re: Проверка состояния подключения к БД.
Отправлено: titan83 от Март 11, 2016, 20:06
Ну так загляните в исходники драйвера для ODBC, посмотрите что именно выделяется (какая структура данных?), кто ее должен освобождать, когда...
Может нужно просто делать close соединению, при обнаружении отключения.
Old, спасибо за комментарий.
В код глядел, ничего гиперстрашного не увидел, все переменные локальные, ничего там не создается динамически.
Код:
    QVarLengthArray<SQLTCHAR> connOut(1024);
    memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
    r = SQLDriverConnect(d->hDbc,
                          NULL,
                          toSQLTCHAR(connQStr).data(),
                          (SQLSMALLINT)connQStr.length(),
                          connOut.data(),
                          1024,
                          &cb,
                          /*SQL_DRIVER_NOPROMPT*/0);

    if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
        setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
        setOpenError(true);
        return false;
    }

По вызову close() делается и cleanup(), в котором есть пара SQLFreeHandle.
Но моя проблема в том, что я имею утечку памяти еще ДО ТОГО, как я установил соединение. Т.е. программа запускается, пробует соединиться, не получается, через 30 секунд пробует снова и так далее.
Хотя я делал close() при неудачной попытке соединения - это не влияло на ситуацию.


Название: Re: Проверка состояния подключения к БД.
Отправлено: titan83 от Март 11, 2016, 20:22
Похоже, что шляпа герцога на нижнем слое...
http://www.qtcentre.org/threads/39978-Experiencing-Memory-Leak-using-QODBC