В настоящее время я пишу веб-сканер / паук на C ++ для Linux, и у меня возникли некоторые проблемы с обновлением базы данных. Я довольно новичок в C / C ++, просто к вашему сведению.
Обновления базы данных выполняются отдельным потоком (с использованием pthreads), но такая же проблема существует, если выполняется в main (), поэтому я, возможно, наивно, отбросил потоки как причину чего-либо.
Я использую libmysqlcppconn для API базы данных.
Я компилирую с gcc версии 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1) с -O2 -Wall -pedantic, и он компилируется чисто.
Тем не менее, когда ниже вызывается функция commitChangesToDatabase (), она в основном выбирает элементы из std :: map (url_queue), выбрасывает их в std :: vector (updates) и удаляет указанный элемент из исходного std: : map, затем переходит к итерации по std :: vector, выполняя подготовленный MySQL оператор для каждого элемента в векторе. Вот где это терпит неудачу.
Случайно либо:
- Сбои без вывода ошибок (без segfault, без трассировки стека, без ничего)
- Сбои с обнаруженным повреждением памяти glibc (см. Вывод здесь: http://pastie.org/private/wlkuorivq5tptlcr7ojg)
- Сообщает, что сервер MySQL пропал (перехватил исключение), но продолжает попытки (не вылетает)
Я пытался переключить подготовленный оператор на простой executeUpdate (), но безрезультатно.
Я попытался исключить этот шаг, выбрав элементы и просто выполняя обновления всякий раз, когда я нахожу элемент для обновления, в первом цикле над url_queue.
Другие функции в этом приложении также используют подготовленные операторы (еще одно ОБНОВЛЕНИЕ), и это прекрасно работает. Эти функции также выполняются отдельными потоками.
Я бы запустил приложение через valgrind, но, честно говоря, я не понимаю большую часть выходных данных, так что это не сильно мне поможет - но если кто-то захочет получить от него выходные данные, дайте мне знать, какие варианты запустить с, и я предоставлю это.
Понятия не имею, как поступить отсюда. Кто-нибудь знает, что не так?
struct queue_item_t {
int id;
int sites_id;
int priority;
int depth;
int handler;
int state; // 0 = Pending, 1 = Working, 2 = Completed, 3 = Checked
double time_allowed_crawl;
bool status;
bool was_redirected;
double time;
double time_end;
double time_curl;
double size;
std::string hash;
std::string url;
std::string file;
std::string host;
};
void commitChangesToDatabase()
{
map< string, queue_item_t >::iterator it, end;
sql::PreparedStatement *pstmt;
int i = 0;
if (!url_queue.size()) {
return;
}
pthread_mutex_lock(&dbCommitMutex);
pthread_mutex_lock(&itemMutex);
cout << "commitChangesToDatabase()" << endl;
pstmt = dbPrepareStatement("UPDATE crawler_queue SET process_hash = NULL, date_crawled = NOW(), url = ?, hash = ? WHERE id = ?");
for (it = url_queue.begin(); it != url_queue.end();)
{
if (it->second.state == 2)
{
pstmt->setString(1, it->second.url);
pstmt->setString(2, it->second.hash);
pstmt->setInt(3, it->second.id);
try {
pstmt->executeUpdate();
++i;
} catch (sql::SQLException &e) {
cerr << "# ERR: SQLException in " << __FILE__;
cerr << "(" << __FUNCTION__ << ") on line " << __LINE__ << endl;
cerr << "# ERR: " << e.what();
cerr << " (MySQL error code: " << e.getErrorCode();
cerr << ", SQLState: " << e.getSQLState() << " )" << endl;
}
url_queue.erase(it++);
}
else {
++it;
}
}
delete pstmt;
cout << "~commitChangesToDatabase()" << endl;
pthread_mutex_unlock(&itemMutex);
pthread_mutex_unlock(&dbCommitMutex);
}
// this function is defined in another file but is written here just to show the contents of it
sql::PreparedStatement *dbPrepareStatement(const std::string &query)
{
return con->prepareStatement(query);
}
Edit:
Некоторые, похоже, считают, что проблема в итерации по коллекции url_queue, однако я исключил это, закомментировав все, что работает с базой данных, но не итерацию. Кроме того, итерация здесь представляет собой упрощенную (но работающую) версию оригинала, которая выбирает элементы с карты, добавляет вектор и стирает с карты, как показано ниже, и эта часть программы работает нормально - это only падает при использовании базы данных.
for (it = url_queue.begin(); it != url_queue.end();)
{
if (it->second.state == 2)
{
update_item.type = (!it->second.was_redirected ? 1 : 2);
update_item.item = it->second;
updates.push_back(update_item);
url_queue.erase(it++);
}
else {
++it;
}
}
Редактировать 2:
Вывод из valgrind --leak-check=yes
: http://pastie.org/private/2ypk0bmawwsqva3ikfazw