fix some DB race conditions, fix some bulk insert SQL formatting bugs along the way
This commit is contained in:
parent
714d1998ba
commit
1f988b8a60
@ -317,15 +317,15 @@ void FeedDownloader::updateOneFeed(ServiceRoot* acc,
|
||||
}
|
||||
|
||||
if (!msg_original.m_isRead && msg_tweaked_by_filter->m_isRead) {
|
||||
qDebugNN << LOGSEC_FEEDDOWNLOADER << "Message with custom ID: '" << msg_original.m_customId
|
||||
<< "' was marked as read by message scripts.";
|
||||
qDebugNN << LOGSEC_FEEDDOWNLOADER << "Message with custom ID:" << QUOTE_W_SPACE(msg_original.m_customId)
|
||||
<< "was marked as read by message scripts.";
|
||||
|
||||
read_msgs << *msg_tweaked_by_filter;
|
||||
}
|
||||
|
||||
if (!msg_original.m_isImportant && msg_tweaked_by_filter->m_isImportant) {
|
||||
qDebugNN << LOGSEC_FEEDDOWNLOADER << "Message with custom ID: '" << msg_original.m_customId
|
||||
<< "' was marked as important by message scripts.";
|
||||
qDebugNN << LOGSEC_FEEDDOWNLOADER << "Message with custom ID:" << QUOTE_W_SPACE(msg_original.m_customId)
|
||||
<< "was marked as important by message scripts.";
|
||||
|
||||
important_msgs << *msg_tweaked_by_filter;
|
||||
}
|
||||
@ -333,6 +333,8 @@ void FeedDownloader::updateOneFeed(ServiceRoot* acc,
|
||||
// Process changed labels.
|
||||
for (Label* lbl : qAsConst(msg_original.m_assignedLabels)) {
|
||||
if (!msg_tweaked_by_filter->m_assignedLabels.contains(lbl)) {
|
||||
QMutexLocker lck(&m_mutexDb);
|
||||
|
||||
// Label is not there anymore, it was deassigned.
|
||||
lbl->deassignFromMessage(*msg_tweaked_by_filter);
|
||||
|
||||
@ -344,6 +346,8 @@ void FeedDownloader::updateOneFeed(ServiceRoot* acc,
|
||||
|
||||
for (Label* lbl : qAsConst(msg_tweaked_by_filter->m_assignedLabels)) {
|
||||
if (!msg_original.m_assignedLabels.contains(lbl)) {
|
||||
QMutexLocker lck(&m_mutexDb);
|
||||
|
||||
// Label is in new message, but is not in old message, it
|
||||
// was newly assigned.
|
||||
lbl->assignToMessage(*msg_tweaked_by_filter);
|
||||
@ -393,7 +397,7 @@ void FeedDownloader::updateOneFeed(ServiceRoot* acc,
|
||||
removeDuplicateMessages(msgs);
|
||||
|
||||
tmr.restart();
|
||||
auto updated_messages = acc->updateMessages(msgs, feed, false);
|
||||
auto updated_messages = acc->updateMessages(msgs, feed, false, &m_mutexDb);
|
||||
|
||||
qDebugNN << LOGSEC_FEEDDOWNLOADER << "Updating messages in DB took" << NONQUOTE_W_SPACE(tmr.nsecsElapsed() / 1000)
|
||||
<< "microseconds.";
|
||||
|
@ -78,6 +78,7 @@ class FeedDownloader : public QObject {
|
||||
private:
|
||||
bool m_isCacheSynchronizationRunning;
|
||||
bool m_stopCacheSynchronization;
|
||||
QMutex m_mutexDb;
|
||||
QHash<ServiceRoot*, ApplicationException> m_erroredAccounts;
|
||||
QList<FeedUpdateRequest> m_feeds = {};
|
||||
QFutureWatcher<FeedUpdateResult> m_watcherLookup;
|
||||
|
@ -1080,6 +1080,7 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
QList<Message>& messages,
|
||||
Feed* feed,
|
||||
bool force_update,
|
||||
QMutex* db_mutex,
|
||||
bool* ok) {
|
||||
if (messages.isEmpty()) {
|
||||
*ok = true;
|
||||
@ -1096,7 +1097,6 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
QSqlQuery query_select_with_custom_id_for_feed(db);
|
||||
QSqlQuery query_select_with_id(db);
|
||||
QSqlQuery query_update(db);
|
||||
QSqlQuery query_insert(db);
|
||||
|
||||
// Here we have query which will check for existence of the "same" message in given feed.
|
||||
// The two message are the "same" if:
|
||||
@ -1130,14 +1130,6 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
.prepare(QSL("SELECT date_created, is_read, is_important, contents, feed, title, author FROM Messages "
|
||||
"WHERE id = :id AND account_id = :account_id;"));
|
||||
|
||||
// Used to insert new messages.
|
||||
query_insert.setForwardOnly(true);
|
||||
query_insert.prepare(QSL("INSERT INTO Messages "
|
||||
"(feed, title, is_read, is_important, is_deleted, url, author, score, date_created, "
|
||||
"contents, enclosures, custom_id, custom_hash, account_id) "
|
||||
"VALUES (:feed, :title, :is_read, :is_important, :is_deleted, :url, :author, :score, "
|
||||
":date_created, :contents, :enclosures, :custom_id, :custom_hash, :account_id);"));
|
||||
|
||||
// Used to update existing messages.
|
||||
query_update.setForwardOnly(true);
|
||||
query_update.prepare(QSL("UPDATE Messages "
|
||||
@ -1321,6 +1313,8 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
(!ignore_contents_changes && message.m_contents != contents_existing_message);
|
||||
|
||||
if (cond_1 || cond_2 || cond_3 || force_update) {
|
||||
QMutexLocker lck(db_mutex);
|
||||
|
||||
// Message exists and is changed, update it.
|
||||
query_update.bindValue(QSL(":title"), unnulifyString(message.m_title));
|
||||
query_update.bindValue(QSL(":is_read"), int(message.m_isRead));
|
||||
@ -1349,8 +1343,8 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
updated_messages.second++;
|
||||
}
|
||||
else if (query_update.lastError().isValid()) {
|
||||
qWarningNN << LOGSEC_DB
|
||||
<< "Failed to update message in DB:" << QUOTE_W_SPACE_DOT(query_update.lastError().text());
|
||||
qCriticalNN << LOGSEC_DB
|
||||
<< "Failed to update message in DB:" << QUOTE_W_SPACE_DOT(query_update.lastError().text());
|
||||
}
|
||||
|
||||
query_update.finish();
|
||||
@ -1407,7 +1401,7 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
.replace(QSL(":date_created"), QString::number(msg->m_created.toMSecsSinceEpoch()))
|
||||
.replace(QSL(":contents"), DatabaseFactory::escapeQuery(unnulifyString(msg->m_contents)))
|
||||
.replace(QSL(":enclosures"), Enclosures::encodeEnclosuresToString(msg->m_enclosures))
|
||||
.replace(QSL(":custom_id"), unnulifyString(msg->m_customId))
|
||||
.replace(QSL(":custom_id"), DatabaseFactory::escapeQuery(unnulifyString(msg->m_customId)))
|
||||
.replace(QSL(":custom_hash"), unnulifyString(msg->m_customHash))
|
||||
.replace(QSL(":score"), QString::number(msg->m_score))
|
||||
.replace(QSL(":account_id"), QString::number(account_id)));
|
||||
@ -1415,6 +1409,9 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
|
||||
if (!vals.isEmpty()) {
|
||||
QString final_bulk = bulk_insert.arg(vals.join(QSL(", ")));
|
||||
|
||||
QMutexLocker lck(db_mutex);
|
||||
|
||||
auto bulk_query = db.exec(final_bulk);
|
||||
auto bulk_error = bulk_query.lastError();
|
||||
|
||||
@ -1422,6 +1419,8 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
QString txt = bulk_error.text() + bulk_error.databaseText() + bulk_error.driverText();
|
||||
|
||||
qCriticalNN << LOGSEC_DB << "Failed bulk insert of articles:" << QUOTE_W_SPACE_DOT(txt);
|
||||
|
||||
IOFactory::writeFile("aaa.sql", final_bulk.toUtf8());
|
||||
}
|
||||
else {
|
||||
// OK, we bulk-inserted many messages but the thing is that they do not
|
||||
@ -1446,23 +1445,26 @@ QPair<int, int> DatabaseQueries::updateMessages(QSqlDatabase db,
|
||||
for (Message& message : messages) {
|
||||
if (!message.m_assignedLabels.isEmpty()) {
|
||||
if (!message.m_customId.isEmpty() || message.m_id > 0) {
|
||||
QMutexLocker lck(db_mutex);
|
||||
setLabelsForMessage(db, message.m_assignedLabels, message);
|
||||
}
|
||||
else {
|
||||
qWarningNN << LOGSEC_DB << "Cannot set labels for message" << QUOTE_W_SPACE(message.m_title)
|
||||
<< "because we don't have ID or custom ID.";
|
||||
qCriticalNN << LOGSEC_DB << "Cannot set labels for message" << QUOTE_W_SPACE(message.m_title)
|
||||
<< "because we don't have ID or custom ID.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, fixup custom IDS for messages which initially did not have them,
|
||||
// just to keep the data consistent.
|
||||
QMutexLocker lck(db_mutex);
|
||||
|
||||
if (db.exec("UPDATE Messages "
|
||||
"SET custom_id = id "
|
||||
"WHERE custom_id IS NULL OR custom_id = '';")
|
||||
.lastError()
|
||||
.isValid()) {
|
||||
qWarningNN << LOGSEC_DB << "Failed to set custom ID for all messages:" << QUOTE_W_SPACE_DOT(db.lastError().text());
|
||||
qCriticalNN << LOGSEC_DB << "Failed to set custom ID for all messages:" << QUOTE_W_SPACE_DOT(db.lastError().text());
|
||||
}
|
||||
|
||||
if (ok != nullptr) {
|
||||
|
@ -140,6 +140,7 @@ class DatabaseQueries {
|
||||
QList<Message>& messages,
|
||||
Feed* feed,
|
||||
bool force_update,
|
||||
QMutex* db_mutex,
|
||||
bool* ok = nullptr);
|
||||
static bool deleteAccount(const QSqlDatabase& db, ServiceRoot* account);
|
||||
static bool deleteAccountData(const QSqlDatabase& db,
|
||||
|
@ -448,7 +448,7 @@ void FormMessageFiltersManager::processCheckedFeeds() {
|
||||
}
|
||||
|
||||
// Update messages in DB and reload selection.
|
||||
it->getParentServiceRoot()->updateMessages(msgs, it->toFeed(), true);
|
||||
it->getParentServiceRoot()->updateMessages(msgs, it->toFeed(), true, nullptr);
|
||||
displayMessagesOfFeed();
|
||||
}
|
||||
}
|
||||
|
@ -943,7 +943,7 @@ ServiceRoot::LabelOperation operator&(ServiceRoot::LabelOperation lhs, ServiceRo
|
||||
return static_cast<ServiceRoot::LabelOperation>(static_cast<char>(lhs) & static_cast<char>(rhs));
|
||||
}
|
||||
|
||||
QPair<int, int> ServiceRoot::updateMessages(QList<Message>& messages, Feed* feed, bool force_update) {
|
||||
QPair<int, int> ServiceRoot::updateMessages(QList<Message>& messages, Feed* feed, bool force_update, QMutex* db_mutex) {
|
||||
QPair<int, int> updated_messages = {0, 0};
|
||||
|
||||
if (messages.isEmpty()) {
|
||||
@ -956,9 +956,11 @@ QPair<int, int> ServiceRoot::updateMessages(QList<Message>& messages, Feed* feed
|
||||
|
||||
qDebugNN << LOGSEC_CORE << "Updating messages in DB.";
|
||||
|
||||
updated_messages = DatabaseQueries::updateMessages(database, messages, feed, force_update, &ok);
|
||||
updated_messages = DatabaseQueries::updateMessages(database, messages, feed, force_update, db_mutex, &ok);
|
||||
|
||||
if (updated_messages.first > 0 || updated_messages.second > 0) {
|
||||
QMutexLocker lck(db_mutex);
|
||||
|
||||
// Something was added or updated in the DB, update numbers.
|
||||
feed->updateCounts(true);
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <QPair>
|
||||
|
||||
class QAction;
|
||||
class QMutex;
|
||||
class FeedsModel;
|
||||
class RecycleBin;
|
||||
class ImportantNode;
|
||||
@ -197,7 +198,7 @@ class ServiceRoot : public RootItem {
|
||||
void completelyRemoveAllData();
|
||||
|
||||
// Returns counts of updated messages <unread, all>.
|
||||
QPair<int, int> updateMessages(QList<Message>& messages, Feed* feed, bool force_update);
|
||||
QPair<int, int> updateMessages(QList<Message>& messages, Feed* feed, bool force_update, QMutex* db_mutex);
|
||||
|
||||
QIcon feedIconForMessage(const QString& feed_custom_id) const;
|
||||
|
||||
|
@ -248,7 +248,8 @@ void FormStandardImportExport::parseImportFile(const QString& file_name, bool fe
|
||||
QFile input_file(file_name);
|
||||
QByteArray input_data;
|
||||
|
||||
if (input_file.open(QIODevice::Text | QIODevice::Unbuffered | QIODevice::ReadOnly)) {
|
||||
if (input_file.open(QIODevice::OpenModeFlag::Text | QIODevice::OpenModeFlag::Unbuffered |
|
||||
QIODevice::OpenModeFlag::ReadOnly)) {
|
||||
input_data = input_file.readAll();
|
||||
input_file.close();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user