Made database optimization faster using pragma analysis_limit

This commit is contained in:
emeric
2023-10-22 00:26:11 +02:00
parent 7d9b5a7084
commit 17efc2759a
8 changed files with 263 additions and 280 deletions
+6 -14
View File
@@ -33,33 +33,25 @@ Db::Db(const std::filesystem::path& dbPath, std::size_t connectionCount)
{ {
LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string(); LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string();
std::unique_ptr<Wt::Dbo::backend::Sqlite3> connection {std::make_unique<Wt::Dbo::backend::Sqlite3>(dbPath.string())}; auto connection{ std::make_unique<Wt::Dbo::backend::Sqlite3>(dbPath.string()) };
// connection->setProperty("show-queries", "true"); // connection->setProperty("show-queries", "true");
connection->executeSql("pragma journal_mode=WAL"); connection->executeSql("pragma journal_mode=WAL");
connection->executeSql("pragma synchronous=normal"); connection->executeSql("pragma synchronous=normal");
connection->executeSql("pragma analysis_limit=1000"); // to help make analyze command faster
auto connectionPool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), connectionCount); auto connectionPool{ std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), connectionCount) };
connectionPool->setTimeout(std::chrono::seconds(10)); connectionPool->setTimeout(std::chrono::seconds{ 10 });
_connectionPool = std::move(connectionPool); _connectionPool = std::move(connectionPool);
} }
Db::~Db() void Db::executeSql(const std::string& sql)
{
LMS_LOG(DB, DEBUG) << "Optimizing db...";
executeSql("pragma optimize");
LMS_LOG(DB, DEBUG) << "Optimizing db DONE";
}
void
Db::executeSql(const std::string& sql)
{ {
ScopedConnection connection{ *_connectionPool }; ScopedConnection connection{ *_connectionPool };
connection->executeSql(sql); connection->executeSql(sql);
} }
Session& Session& Db::getTLSSession()
Db::getTLSSession()
{ {
static thread_local Session* tlsSession{}; static thread_local Session* tlsSession{};
@@ -654,7 +654,6 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
{41, migrateFromV41}, {41, migrateFromV41},
}; };
while (1)
{ {
auto uniqueTransaction{ session.createUniqueTransaction() }; auto uniqueTransaction{ session.createUniqueTransaction() };
@@ -670,19 +669,14 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
throw LmsException{ outdatedMsg }; throw LmsException{ outdatedMsg };
} }
if (version == LMS_DATABASE_VERSION) if (version > LMS_DATABASE_VERSION)
{
LMS_LOG(DB, INFO) << "Lms database version " << LMS_DATABASE_VERSION << ": up to date!";
return;
}
else if (version > LMS_DATABASE_VERSION)
{
throw LmsException{ "Server binary outdated, please upgrade it to handle this database" }; throw LmsException{ "Server binary outdated, please upgrade it to handle this database" };
}
if (version < migrationFunctions.begin()->first) if (version < migrationFunctions.begin()->first)
throw LmsException{ outdatedMsg }; throw LmsException{ outdatedMsg };
while (version < LMS_DATABASE_VERSION)
{
LMS_LOG(DB, INFO) << "Migrating database from version " << version << " to " << version + 1 << "..."; LMS_LOG(DB, INFO) << "Migrating database from version " << version << " to " << version + 1 << "...";
auto itMigrationFunc{ migrationFunctions.find(version) }; auto itMigrationFunc{ migrationFunctions.find(version) };
@@ -695,3 +689,4 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
} }
} }
} }
}
+16 -16
View File
@@ -83,43 +83,44 @@ SharedTransaction::SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Sessi
{ {
} }
void void Session::checkUniqueLocked()
Session::checkUniqueLocked()
{ {
assert(_db.getMutex().isUniqueLocked()); assert(_db.getMutex().isUniqueLocked());
} }
void void Session::checkSharedLocked()
Session::checkSharedLocked()
{ {
assert(_db.getMutex().isSharedLocked()); assert(_db.getMutex().isSharedLocked());
} }
UniqueTransaction UniqueTransaction Session::createUniqueTransaction()
Session::createUniqueTransaction()
{ {
return UniqueTransaction{ _db.getMutex(), _session }; return UniqueTransaction{ _db.getMutex(), _session };
} }
SharedTransaction SharedTransaction Session::createSharedTransaction()
Session::createSharedTransaction()
{ {
return SharedTransaction{ _db.getMutex(), _session }; return SharedTransaction{ _db.getMutex(), _session };
} }
void void Session::prepareTables()
Session::prepareTables()
{ {
// Creation case LMS_LOG(DB, INFO) << "Preparing tables...";
// Initial creation case
try try
{ {
_session.createTables(); _session.createTables();
LMS_LOG(DB, INFO) << "Tables created"; LMS_LOG(DB, INFO) << "Tables created";
} }
catch (Wt::Dbo::Exception& e) catch (Wt::Dbo::Exception& e)
{
LMS_LOG(DB, DEBUG) << "Cannot create tables: " << e.what();
if (std::string_view{ e.what() }.find("already exists") == std::string_view::npos)
{ {
LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what(); LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what();
throw e;
}
} }
Migration::doDbMigration(*this); Migration::doDbMigration(*this);
@@ -176,15 +177,14 @@ Session::prepareTables()
} }
} }
void void Session::analyze()
Session::optimize()
{ {
LMS_LOG(DB, DEBUG) << "Optimizing db..."; LMS_LOG(DB, INFO) << "Analyzing database...";
{ {
auto uniqueTransaction{ createUniqueTransaction() }; auto uniqueTransaction{ createUniqueTransaction() };
_session.execute("ANALYZE"); _session.execute("ANALYZE");
} }
LMS_LOG(DB, DEBUG) << "Optimized db!"; LMS_LOG(DB, INFO) << "Database Analyze complete";
} }
} // namespace Database } // namespace Database
@@ -32,18 +32,15 @@ class Db
{ {
public: public:
Db(const std::filesystem::path& dbPath, std::size_t connectionCount = 10); Db(const std::filesystem::path& dbPath, std::size_t connectionCount = 10);
~Db();
Db(const Db&) = delete;
Db(Db&&) = delete;
Db& operator=(const Db&) = delete;
Db& operator=(Db&&) = delete;
Session& getTLSSession(); Session& getTLSSession();
void executeSql(const std::string& sql); void executeSql(const std::string& sql);
private: private:
Db(const Db&) = delete;
Db& operator=(const Db&) = delete;
friend class Session; friend class Session;
RecursiveSharedMutex& getMutex() { return _sharedMutex; } RecursiveSharedMutex& getMutex() { return _sharedMutex; }
@@ -55,14 +52,12 @@ class Db
ScopedConnection(Wt::Dbo::SqlConnectionPool& pool); ScopedConnection(Wt::Dbo::SqlConnectionPool& pool);
~ScopedConnection(); ~ScopedConnection();
ScopedConnection(const ScopedConnection& ) = delete;
ScopedConnection(ScopedConnection&& ) = delete;
ScopedConnection& operator=(const ScopedConnection& ) = delete;
ScopedConnection& operator=(ScopedConnection&& ) = delete;
Wt::Dbo::SqlConnection* operator->() const; Wt::Dbo::SqlConnection* operator->() const;
private: private:
ScopedConnection(const ScopedConnection&) = delete;
ScopedConnection& operator=(const ScopedConnection&) = delete;
Wt::Dbo::SqlConnectionPool& _connectionPool; Wt::Dbo::SqlConnectionPool& _connectionPool;
std::unique_ptr<Wt::Dbo::SqlConnection> _connection; std::unique_ptr<Wt::Dbo::SqlConnection> _connection;
}; };
@@ -53,11 +53,7 @@ namespace Database
{ {
public: public:
Session(Db& database); Session(Db& database);
~Session();
Session(const Session&) = delete;
Session(Session&&) = delete;
Session& operator=(const Session&) = delete;
Session& operator=(Session&&) = delete;
[[nodiscard]] UniqueTransaction createUniqueTransaction(); [[nodiscard]] UniqueTransaction createUniqueTransaction();
[[nodiscard]] SharedTransaction createSharedTransaction(); [[nodiscard]] SharedTransaction createSharedTransaction();
@@ -65,7 +61,7 @@ namespace Database
void checkUniqueLocked(); void checkUniqueLocked();
void checkSharedLocked(); void checkSharedLocked();
void optimize(); void analyze();
void prepareTables(); // need to run only once at startup void prepareTables(); // need to run only once at startup
@@ -87,6 +83,9 @@ namespace Database
} }
private: private:
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
Db& _db; Db& _db;
Wt::Dbo::Session _session; Wt::Dbo::Session _session;
}; };
+1 -1
View File
@@ -60,7 +60,7 @@ DatabaseFixture::SetUpTestCase()
{ {
Database::Session s {_tmpDb->getDb()}; Database::Session s {_tmpDb->getDb()};
s.prepareTables(); s.prepareTables();
s.optimize(); s.analyze();
// remove default created entries // remove default created entries
{ {
@@ -299,8 +299,7 @@ ScannerService::scan(bool forceScan)
LMS_LOG(DBUPDATER, INFO) << "Scan " << (_abortScan ? "aborted" : "complete") << ". Changes = " << stats.nbChanges() << " (added = " << stats.additions << ", removed = " << stats.deletions << ", updated = " << stats.updates << "), Not changed = " << stats.skips << ", Scanned = " << stats.scans << " (errors = " << stats.errors.size() << "), features fetched = " << stats.featuresFetched << ", duplicates = " << stats.duplicates.size(); LMS_LOG(DBUPDATER, INFO) << "Scan " << (_abortScan ? "aborted" : "complete") << ". Changes = " << stats.nbChanges() << " (added = " << stats.additions << ", removed = " << stats.deletions << ", updated = " << stats.updates << "), Not changed = " << stats.skips << ", Scanned = " << stats.scans << " (errors = " << stats.errors.size() << "), features fetched = " << stats.featuresFetched << ", duplicates = " << stats.duplicates.size();
// TODO make it a scan step _dbSession.analyze();
_dbSession.optimize();
if (!_abortScan) if (!_abortScan)
{ {
+4 -1
View File
@@ -238,7 +238,10 @@ int main(int argc, char* argv[])
{ {
Database::Session session {database}; Database::Session session {database};
session.prepareTables(); session.prepareTables();
session.optimize();
// force optimize in case scanner aborted during a large import:
// queries may be too slow to even be able to relaunch a scan sing the web interface
session.analyze();
} }
UserInterface::LmsApplicationManager appManager; UserInterface::LmsApplicationManager appManager;