Тестирование клиента MySQL
Один из наших сервисов использует самописный клиент MySQL,
который осуществляет периодический сброс статистики в базу.
Его задача очень простая:
1. проверить состояние соединения с СУБД.
2. если всё Ok, выполнить необходимые запросы.
3. ждать в течение заданного таймаута и повторить п.1,2.
Клиент и сервер MySQL находятся на разных хостах.
Функционал клиента выглядит примерно так:
План тестирования.
1. Ограничения со стороны СУБД.
1.1. Отобрать привилегии у пользователя СУБД и убедиться, что база становится недоступной клиенту.
1.2. Назначить все необходимые привилегии пользователю посредством и убедиться, что соединение с базой восстановлено.
2. Ограничение со стороны FW.
В качестве FW использовался iptables.
Необходимо закрывать/открывать порт соединения с базой и исследовать результаты.
2.1. Порт закрывается до работы клиента.
2.2. Порт закрывается/открывается во время работы клиента.
Основные проблемы обнаружились в MySQL-API.
Смотрим ещё раз на check_db();
Проблема первая: mysql_stat возвращает результат на момент соединения и далее не меняет своего значения.
Замена mysql_stat на mysql_ping привела к зависанию из-за большого таймаута соединения (mysql_ping, как известно, пытается восстанавливать соединение самостоятельно) на условии проверки.
Очевидным решением казалось применение опции таймаута соединения:
Но увы, это породило другую проблему:
В случае, когда порт закрывался до запуска клиента таймаут соединения действительно был равен заданному. Но при проверке п.2.2. клиент опять зависал на строке проверки mysql_ping.
В результате пришлось оторвать проверку напрочь, а функция flash_stat() стала выглядеть следующим образом:
Поскольку периодичность запуска клиента большая, подключение к базе на каждом срабатывании вполне допустимо.
Результаты тестирования.
1. Тестирование доступа к базе на стороне MySQL Server:
1.1.REVOKE ALL PRIVILEGES ON database FROM 'user'@'host';
Записи в логе клиента:
[28.05.2010 19:42:55] flush stat
[28.05.2010 19:42:55] DBconnection closed
[28.05.2010 19:43:00] Connect MySQL failed: Can't connect to MySQL server on 'host'
1.2.GRANT ALL PRIVILEGES ON reklama TO 'user'@'host'
Записи в логе клиента:
[28.05.2010 19:43:55] flush stat
[28.05.2010 19:43:55] DB (user@database) connect OK
[28.05.2010 19:43:55] flush stat OK
2. Тестирование через FW
2.1.
Закрываем порт соединения с базой и запускаем клиент:
/etc/init.d/iptables restart
iptables -A OUTPUT -p TCP --dport 3306 -j DROP
Проверяем, что получилось командой iptables -L -n -v
Записи в логе клиента:
[28.05.2010 19:42:55] flush stat
[28.05.2010 19:42:55] DBconnection closed
[28.05.2010 19:43:00] Connect MySQL failed: Can't connect to MySQL server on 'host'
Открываем порт соединения с базой
iptables -D OUTPUT -p TCP --dport 3306 -j DROP
Записи в логе клиента:
[28.05.2010 19:43:55] flush stat
[28.05.2010 19:43:55] DB (user@database) connect OK
[28.05.2010 19:43:55] flush stat OK
2.2 Повторяем п.2.1., но при работающем клиенте. Результат аналогичен предыдущему.
Мораль этой басни не нова: be careful, при использовании MySQL-API.
который осуществляет периодический сброс статистики в базу.
Его задача очень простая:
1. проверить состояние соединения с СУБД.
2. если всё Ok, выполнить необходимые запросы.
3. ждать в течение заданного таймаута и повторить п.1,2.
Клиент и сервер MySQL находятся на разных хостах.
Функционал клиента выглядит примерно так:
static MYSQL *db;
static void db_disconnect() {
if (db == NULL) return;
mysql_close(db);
db = NULL;
}
static int db_connect() {
if (db)
return 1;
db = mysql_init(NULL);
if (db == NULL) {
log_w("Could not initialize db");
return 0;
}
if (mysql_real_connect(db, ..., NULL, 0) == NULL){
log_w("Connect MySQL failed: %s\n", mysql_error(db));
db_disconnect();
return 0;
}
log_w("DB (%s@%s) connect OK", ssrv_config.db_user, ssrv_config.db_name);
return 1;
}
static int check_db() {
if (db && (mysql_stat(db) == NULL)){
log_w("DBconnection closed");
db_disconnect();
}
if (!db)
db_connect();
if (!db)return 0;
return 1;
}
void flush_stat() {
if (!check_db())
return;
for (...){
// SQL queries
...
}
}
План тестирования.
1. Ограничения со стороны СУБД.
1.1. Отобрать привилегии у пользователя СУБД и убедиться, что база становится недоступной клиенту.
1.2. Назначить все необходимые привилегии пользователю посредством и убедиться, что соединение с базой восстановлено.
2. Ограничение со стороны FW.
В качестве FW использовался iptables.
Необходимо закрывать/открывать порт соединения с базой и исследовать результаты.
2.1. Порт закрывается до работы клиента.
2.2. Порт закрывается/открывается во время работы клиента.
Основные проблемы обнаружились в MySQL-API.
Смотрим ещё раз на check_db();
static int check_db() {
if (db && (mysql_stat(db) == NULL)){
log_w("DBconnection closed");
db_disconnect();
}
if (!db)
db_connect();
if (!db)
return 0;
return 1;
}
Проблема первая: mysql_stat возвращает результат на момент соединения и далее не меняет своего значения.
Замена mysql_stat на mysql_ping привела к зависанию из-за большого таймаута соединения (mysql_ping, как известно, пытается восстанавливать соединение самостоятельно) на условии проверки.
Очевидным решением казалось применение опции таймаута соединения:
#define DBCONNECTION_TIMEOUT 5
...
static int db_connect() {
unsigned int timeout = DBCONNECTION_TIMEOUT;
...
if(mysql_options(db, MYSQL_OPT_CONNECT_TIMEOUT, &timeout)){
log_w("Set options MySQL failed: %s\n", mysql_error(db));
}
...
}Но увы, это породило другую проблему:
В случае, когда порт закрывался до запуска клиента таймаут соединения действительно был равен заданному. Но при проверке п.2.2. клиент опять зависал на строке проверки mysql_ping.
В результате пришлось оторвать проверку напрочь, а функция flash_stat() стала выглядеть следующим образом:
void flush_stat() {
if (!db_connect())
return;
for (...) {
// SQL queries
...
}
db_disconnect();
}
Поскольку периодичность запуска клиента большая, подключение к базе на каждом срабатывании вполне допустимо.
Результаты тестирования.
1. Тестирование доступа к базе на стороне MySQL Server:
1.1.REVOKE ALL PRIVILEGES ON database FROM 'user'@'host';
Записи в логе клиента:
[28.05.2010 19:42:55] flush stat
[28.05.2010 19:42:55] DBconnection closed
[28.05.2010 19:43:00] Connect MySQL failed: Can't connect to MySQL server on 'host'
1.2.GRANT ALL PRIVILEGES ON reklama TO 'user'@'host'
Записи в логе клиента:
[28.05.2010 19:43:55] flush stat
[28.05.2010 19:43:55] DB (user@database) connect OK
[28.05.2010 19:43:55] flush stat OK
2. Тестирование через FW
2.1.
Закрываем порт соединения с базой и запускаем клиент:
/etc/init.d/iptables restart
iptables -A OUTPUT -p TCP --dport 3306 -j DROP
Проверяем, что получилось командой iptables -L -n -v
Записи в логе клиента:
[28.05.2010 19:42:55] flush stat
[28.05.2010 19:42:55] DBconnection closed
[28.05.2010 19:43:00] Connect MySQL failed: Can't connect to MySQL server on 'host'
Открываем порт соединения с базой
iptables -D OUTPUT -p TCP --dport 3306 -j DROP
Записи в логе клиента:
[28.05.2010 19:43:55] flush stat
[28.05.2010 19:43:55] DB (user@database) connect OK
[28.05.2010 19:43:55] flush stat OK
2.2 Повторяем п.2.1., но при работающем клиенте. Результат аналогичен предыдущему.
Мораль этой басни не нова: be careful, при использовании MySQL-API.