|
Rex Dieter |
fcd69d |
From 24413dc44b0637d6c64e6b2105c2bcf1b99849a5 Mon Sep 17 00:00:00 2001
|
|
Rex Dieter |
fcd69d |
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
|
|
Rex Dieter |
fcd69d |
Date: Sun, 6 Apr 2014 19:50:38 +0200
|
|
Rex Dieter |
fcd69d |
Subject: [PATCH 13/16] Disable global transaction mutex for QSQLITE3 and
|
|
Rex Dieter |
fcd69d |
enable transaction recording
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
Our QSQLITE3 driver now supports concurrency, so we don't need to serialize
|
|
Rex Dieter |
fcd69d |
transactions in DataStore anymore. It is however still needed for the
|
|
Rex Dieter |
fcd69d |
QSQLITE driver shipped with Qt.
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
Secondary, concurrency support also means possible transactions deadlocks and
|
|
Rex Dieter |
fcd69d |
timeouts, so we also need to enable transaction recording and replaying for
|
|
Rex Dieter |
fcd69d |
the QSQLITE3 backend.
|
|
Rex Dieter |
fcd69d |
---
|
|
Rex Dieter |
fcd69d |
server/src/storage/datastore.cpp | 18 +++++++++++-------
|
|
Rex Dieter |
fcd69d |
server/src/storage/datastore.h | 2 +-
|
|
Rex Dieter |
fcd69d |
server/src/storage/dbtype.cpp | 5 +++++
|
|
Rex Dieter |
fcd69d |
server/src/storage/dbtype.h | 3 +++
|
|
Rex Dieter |
fcd69d |
server/src/storage/querybuilder.cpp | 20 ++++++++++++++++----
|
|
Rex Dieter |
fcd69d |
server/src/storage/querybuilder.h | 2 +-
|
|
Rex Dieter |
fcd69d |
6 files changed, 37 insertions(+), 13 deletions(-)
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
|
|
Rex Dieter |
fcd69d |
index 57d1e4e..0f04fa5 100644
|
|
Rex Dieter |
fcd69d |
--- a/server/src/storage/datastore.cpp
|
|
Rex Dieter |
fcd69d |
+++ b/server/src/storage/datastore.cpp
|
|
Rex Dieter |
fcd69d |
@@ -61,8 +61,8 @@ using namespace Akonadi::Server;
|
|
Rex Dieter |
fcd69d |
static QMutex sTransactionMutex;
|
|
Rex Dieter |
fcd69d |
bool DataStore::s_hasForeignKeyConstraints = false;
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
-#define TRANSACTION_MUTEX_LOCK if ( DbType::type( m_database ) == DbType::Sqlite ) sTransactionMutex.lock()
|
|
Rex Dieter |
fcd69d |
-#define TRANSACTION_MUTEX_UNLOCK if ( DbType::type( m_database ) == DbType::Sqlite ) sTransactionMutex.unlock()
|
|
Rex Dieter |
fcd69d |
+#define TRANSACTION_MUTEX_LOCK if ( DbType::isSystemSQLite( m_database ) ) sTransactionMutex.lock()
|
|
Rex Dieter |
fcd69d |
+#define TRANSACTION_MUTEX_UNLOCK if ( DbType::isSystemSQLite( m_database ) ) sTransactionMutex.unlock()
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
/***************************************************************************
|
|
Rex Dieter |
fcd69d |
* DataStore *
|
|
Rex Dieter |
fcd69d |
@@ -1083,23 +1083,27 @@ QDateTime DataStore::dateTimeToQDateTime( const QByteArray &dateTime )
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
void DataStore::addQueryToTransaction( const QSqlQuery &query, bool isBatch )
|
|
Rex Dieter |
fcd69d |
{
|
|
Rex Dieter |
fcd69d |
- DbType::Type dbType = DbType::type( m_database );
|
|
Rex Dieter |
fcd69d |
// This is used for replaying deadlocked transactions, so only record queries
|
|
Rex Dieter |
fcd69d |
// for backends that support concurrent transactions.
|
|
Rex Dieter |
fcd69d |
- if ( !inTransaction() || ( dbType != DbType::MySQL && dbType != DbType::PostgreSQL ) ) {
|
|
Rex Dieter |
fcd69d |
+ if ( !inTransaction() || DbType::isSystemSQLite( m_database ) ) {
|
|
Rex Dieter |
fcd69d |
return;
|
|
Rex Dieter |
fcd69d |
}
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
m_transactionQueries.append( qMakePair( query, isBatch ) );
|
|
Rex Dieter |
fcd69d |
}
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
-QSqlQuery DataStore::retryLastTransaction()
|
|
Rex Dieter |
fcd69d |
+QSqlQuery DataStore::retryLastTransaction( bool rollbackFirst )
|
|
Rex Dieter |
fcd69d |
{
|
|
Rex Dieter |
fcd69d |
- DbType::Type dbType = DbType::type( m_database );
|
|
Rex Dieter |
fcd69d |
- if ( !inTransaction() || ( dbType != DbType::MySQL && dbType != DbType::PostgreSQL ) ) {
|
|
Rex Dieter |
fcd69d |
+ if ( !inTransaction() || DbType::isSystemSQLite( m_database ) ) {
|
|
Rex Dieter |
fcd69d |
return QSqlQuery();
|
|
Rex Dieter |
fcd69d |
}
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
+ if ( rollbackFirst ) {
|
|
Rex Dieter |
fcd69d |
+ // In some cases the SQL database won't rollback the failed transaction, so
|
|
Rex Dieter |
fcd69d |
+ // we need to do it manually
|
|
Rex Dieter |
fcd69d |
+ m_database.driver()->rollbackTransaction();
|
|
Rex Dieter |
fcd69d |
+ }
|
|
Rex Dieter |
fcd69d |
+
|
|
Rex Dieter |
fcd69d |
// The database has rolled back the actual transaction, so reset the counter
|
|
Rex Dieter |
fcd69d |
// to 0 and start a new one in beginTransaction(). Then restore the level
|
|
Rex Dieter |
fcd69d |
// because this has to be completely transparent to the original caller
|
|
Rex Dieter |
fcd69d |
diff --git a/server/src/storage/datastore.h b/server/src/storage/datastore.h
|
|
Rex Dieter |
fcd69d |
index 8b4a2b7..8a0fe01 100644
|
|
Rex Dieter |
fcd69d |
--- a/server/src/storage/datastore.h
|
|
Rex Dieter |
fcd69d |
+++ b/server/src/storage/datastore.h
|
|
Rex Dieter |
fcd69d |
@@ -317,7 +317,7 @@ protected:
|
|
Rex Dieter |
fcd69d |
* @return Returns an invalid query when error occurs, or the last replayed
|
|
Rex Dieter |
fcd69d |
* query on success.
|
|
Rex Dieter |
fcd69d |
*/
|
|
Rex Dieter |
fcd69d |
- QSqlQuery retryLastTransaction();
|
|
Rex Dieter |
fcd69d |
+ QSqlQuery retryLastTransaction( bool rollbackFirst );
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
private Q_SLOTS:
|
|
Rex Dieter |
fcd69d |
void sendKeepAliveQuery();
|
|
Rex Dieter |
fcd69d |
diff --git a/server/src/storage/dbtype.cpp b/server/src/storage/dbtype.cpp
|
|
Rex Dieter |
fcd69d |
index 495f532..7df2fb1 100644
|
|
Rex Dieter |
fcd69d |
--- a/server/src/storage/dbtype.cpp
|
|
Rex Dieter |
fcd69d |
+++ b/server/src/storage/dbtype.cpp
|
|
Rex Dieter |
fcd69d |
@@ -39,3 +39,8 @@ DbType::Type DbType::typeForDriverName( const QString &driverName )
|
|
Rex Dieter |
fcd69d |
}
|
|
Rex Dieter |
fcd69d |
return Unknown;
|
|
Rex Dieter |
fcd69d |
}
|
|
Rex Dieter |
fcd69d |
+
|
|
Rex Dieter |
fcd69d |
+bool DbType::isSystemSQLite( const QSqlDatabase &db )
|
|
Rex Dieter |
fcd69d |
+{
|
|
Rex Dieter |
fcd69d |
+ return db.driverName() == QLatin1String( "QSQLITE" );
|
|
Rex Dieter |
fcd69d |
+}
|
|
Rex Dieter |
fcd69d |
diff --git a/server/src/storage/dbtype.h b/server/src/storage/dbtype.h
|
|
Rex Dieter |
fcd69d |
index a95a833..3595604 100644
|
|
Rex Dieter |
fcd69d |
--- a/server/src/storage/dbtype.h
|
|
Rex Dieter |
fcd69d |
+++ b/server/src/storage/dbtype.h
|
|
Rex Dieter |
fcd69d |
@@ -42,6 +42,9 @@ namespace DbType
|
|
Rex Dieter |
fcd69d |
/** Returns the type for the given driver name. */
|
|
Rex Dieter |
fcd69d |
Type typeForDriverName( const QString &driverName );
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
+ /** Returns true when using QSQLITE driver shipped with Qt, FALSE otherwise */
|
|
Rex Dieter |
fcd69d |
+ bool isSystemSQLite( const QSqlDatabase &db );
|
|
Rex Dieter |
fcd69d |
+
|
|
Rex Dieter |
fcd69d |
} // namespace DbType
|
|
Rex Dieter |
fcd69d |
} // namespace Server
|
|
Rex Dieter |
fcd69d |
} // namespace Akonadi
|
|
Rex Dieter |
fcd69d |
diff --git a/server/src/storage/querybuilder.cpp b/server/src/storage/querybuilder.cpp
|
|
Rex Dieter |
fcd69d |
index 0abad4a..0530b11 100644
|
|
Rex Dieter |
fcd69d |
--- a/server/src/storage/querybuilder.cpp
|
|
Rex Dieter |
fcd69d |
+++ b/server/src/storage/querybuilder.cpp
|
|
Rex Dieter |
fcd69d |
@@ -320,10 +320,10 @@ QString QueryBuilder::buildQuery()
|
|
Rex Dieter |
fcd69d |
return statement;
|
|
Rex Dieter |
fcd69d |
}
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
-bool QueryBuilder::retryLastTransaction()
|
|
Rex Dieter |
fcd69d |
+bool QueryBuilder::retryLastTransaction( bool rollback )
|
|
Rex Dieter |
fcd69d |
{
|
|
Rex Dieter |
fcd69d |
#ifndef QUERYBUILDER_UNITTEST
|
|
Rex Dieter |
fcd69d |
- mQuery = DataStore::self()->retryLastTransaction();
|
|
Rex Dieter |
fcd69d |
+ mQuery = DataStore::self()->retryLastTransaction( rollback );
|
|
Rex Dieter |
fcd69d |
return !mQuery.lastError().isValid();
|
|
Rex Dieter |
fcd69d |
#else
|
|
Rex Dieter |
fcd69d |
return true;
|
|
Rex Dieter |
fcd69d |
@@ -400,9 +400,21 @@ bool QueryBuilder::exec()
|
|
Rex Dieter |
fcd69d |
akDebug() << mQuery.lastError().text();
|
|
Rex Dieter |
fcd69d |
return retryLastTransaction();
|
|
Rex Dieter |
fcd69d |
}
|
|
Rex Dieter |
fcd69d |
+ } else if ( mDatabaseType == DbType::Sqlite && !DbType::isSystemSQLite( DataStore::self()->database() ) ) {
|
|
Rex Dieter |
fcd69d |
+ const int error = mQuery.lastError().number();
|
|
Rex Dieter |
fcd69d |
+ if ( error == 6 /* SQLITE_LOCKED */ ) {
|
|
Rex Dieter |
fcd69d |
+ akDebug() << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction";
|
|
Rex Dieter |
fcd69d |
+ akDebug() << mQuery.lastError().text();
|
|
Rex Dieter |
fcd69d |
+ return retryLastTransaction();
|
|
Rex Dieter |
fcd69d |
+ } else if ( error == 5 /* SQLITE_BUSY */ ) {
|
|
Rex Dieter |
fcd69d |
+ akDebug() << "QueryBuilder::exec(): database reported transaction timeout, retrying transaction";
|
|
Rex Dieter |
fcd69d |
+ akDebug() << mQuery.lastError().text();
|
|
Rex Dieter |
fcd69d |
+ return retryLastTransaction( true );
|
|
Rex Dieter |
fcd69d |
+ }
|
|
Rex Dieter |
fcd69d |
} else if ( mDatabaseType == DbType::Sqlite ) {
|
|
Rex Dieter |
fcd69d |
- // We can't have a transaction deadlock in SQLite, because it does not support
|
|
Rex Dieter |
fcd69d |
- // concurrent transactions and DataStore serializes them through a global lock.
|
|
Rex Dieter |
fcd69d |
+ // We can't have a transaction deadlock in SQLite when using driver shipped
|
|
Rex Dieter |
fcd69d |
+ // with Qt, because it does not support concurrent transactions and DataStore
|
|
Rex Dieter |
fcd69d |
+ // serializes them through a global lock.
|
|
Rex Dieter |
fcd69d |
}
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
akError() << "DATABASE ERROR:";
|
|
Rex Dieter |
fcd69d |
diff --git a/server/src/storage/querybuilder.h b/server/src/storage/querybuilder.h
|
|
Rex Dieter |
fcd69d |
index 235a099..b380f93 100644
|
|
Rex Dieter |
fcd69d |
--- a/server/src/storage/querybuilder.h
|
|
Rex Dieter |
fcd69d |
+++ b/server/src/storage/querybuilder.h
|
|
Rex Dieter |
fcd69d |
@@ -244,7 +244,7 @@ class QueryBuilder
|
|
Rex Dieter |
fcd69d |
*/
|
|
Rex Dieter |
fcd69d |
void sqliteAdaptUpdateJoin( Query::Condition &cond );
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
- bool retryLastTransaction();
|
|
Rex Dieter |
fcd69d |
+ bool retryLastTransaction( bool rollback = false);
|
|
Rex Dieter |
fcd69d |
|
|
Rex Dieter |
fcd69d |
private:
|
|
Rex Dieter |
fcd69d |
QString mTable;
|
|
Rex Dieter |
fcd69d |
--
|
|
Rex Dieter |
fcd69d |
1.9.0
|
|
Rex Dieter |
fcd69d |
|