From 17efc2759a7e7437cf3bb4324e08ea5605d6c363 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 22 Oct 2023 00:26:11 +0200 Subject: [PATCH] Made database optimization faster using pragma analysis_limit --- src/libs/services/database/impl/Db.cpp | 98 ++++--- src/libs/services/database/impl/Migration.cpp | 25 +- src/libs/services/database/impl/Session.cpp | 252 +++++++++--------- .../database/include/services/database/Db.hpp | 65 +++-- .../include/services/database/Session.hpp | 93 ++++--- src/libs/services/database/test/Common.cpp | 2 +- .../services/scanner/impl/ScannerService.cpp | 3 +- src/lms/main.cpp | 5 +- 8 files changed, 263 insertions(+), 280 deletions(-) diff --git a/src/libs/services/database/impl/Db.cpp b/src/libs/services/database/impl/Db.cpp index 7eb7bf7a..117d18c5 100644 --- a/src/libs/services/database/impl/Db.cpp +++ b/src/libs/services/database/impl/Db.cpp @@ -28,70 +28,62 @@ namespace Database { -// Session living class handling the database and the login -Db::Db(const std::filesystem::path& dbPath, std::size_t connectionCount) -{ - LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string(); + // Session living class handling the database and the login + Db::Db(const std::filesystem::path& dbPath, std::size_t connectionCount) + { + LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string(); - std::unique_ptr connection {std::make_unique(dbPath.string())}; -// connection->setProperty("show-queries", "true"); - connection->executeSql("pragma journal_mode=WAL"); - connection->executeSql("pragma synchronous=normal"); + auto connection{ std::make_unique(dbPath.string()) }; + // connection->setProperty("show-queries", "true"); + connection->executeSql("pragma journal_mode=WAL"); + connection->executeSql("pragma synchronous=normal"); + connection->executeSql("pragma analysis_limit=1000"); // to help make analyze command faster - auto connectionPool = std::make_unique(std::move(connection), connectionCount); - connectionPool->setTimeout(std::chrono::seconds(10)); + auto connectionPool{ std::make_unique(std::move(connection), connectionCount) }; + connectionPool->setTimeout(std::chrono::seconds{ 10 }); - _connectionPool = std::move(connectionPool); -} + _connectionPool = std::move(connectionPool); + } -Db::~Db() -{ - 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 }; + connection->executeSql(sql); + } -void -Db::executeSql(const std::string& sql) -{ - ScopedConnection connection {*_connectionPool}; - connection->executeSql(sql); -} + Session& Db::getTLSSession() + { + static thread_local Session* tlsSession{}; -Session& -Db::getTLSSession() -{ - static thread_local Session* tlsSession {}; + if (!tlsSession) + { + auto newSession{ std::make_unique(*this) }; + tlsSession = newSession.get(); - if (!tlsSession) - { - auto newSession {std::make_unique(*this)}; - tlsSession = newSession.get(); + { + std::scoped_lock lock{ _tlsSessionsMutex }; + _tlsSessions.push_back(std::move(newSession)); + } + } - { - std::scoped_lock lock {_tlsSessionsMutex}; - _tlsSessions.push_back(std::move(newSession)); - } - } + return *tlsSession; + } - return *tlsSession; -} + Db::ScopedConnection::ScopedConnection(Wt::Dbo::SqlConnectionPool& pool) + : _connectionPool{ pool } + , _connection{ _connectionPool.getConnection() } + { + } -Db::ScopedConnection::ScopedConnection(Wt::Dbo::SqlConnectionPool& pool) -: _connectionPool {pool} -, _connection {_connectionPool.getConnection()} -{ -} + Db::ScopedConnection::~ScopedConnection() + { + _connectionPool.returnConnection(std::move(_connection)); + } -Db::ScopedConnection::~ScopedConnection() -{ - _connectionPool.returnConnection(std::move(_connection)); -} - -Wt::Dbo::SqlConnection* Db::ScopedConnection::operator->() const -{ - return _connection.get(); -} + Wt::Dbo::SqlConnection* Db::ScopedConnection::operator->() const + { + return _connection.get(); + } } // namespace Database diff --git a/src/libs/services/database/impl/Migration.cpp b/src/libs/services/database/impl/Migration.cpp index 5785cce6..6c11b887 100644 --- a/src/libs/services/database/impl/Migration.cpp +++ b/src/libs/services/database/impl/Migration.cpp @@ -654,7 +654,6 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( {41, migrateFromV41}, }; - while (1) { auto uniqueTransaction{ session.createUniqueTransaction() }; @@ -670,28 +669,24 @@ CREATE TABLE IF NOT EXISTS "track_backup" ( throw LmsException{ outdatedMsg }; } - 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) - { + if (version > LMS_DATABASE_VERSION) throw LmsException{ "Server binary outdated, please upgrade it to handle this database" }; - } if (version < migrationFunctions.begin()->first) throw LmsException{ outdatedMsg }; - LMS_LOG(DB, INFO) << "Migrating database from version " << version << " to " << version + 1 << "..."; + while (version < LMS_DATABASE_VERSION) + { + LMS_LOG(DB, INFO) << "Migrating database from version " << version << " to " << version + 1 << "..."; - auto itMigrationFunc{ migrationFunctions.find(version) }; - assert(itMigrationFunc != std::cend(migrationFunctions)); - itMigrationFunc->second(session); + auto itMigrationFunc{ migrationFunctions.find(version) }; + assert(itMigrationFunc != std::cend(migrationFunctions)); + itMigrationFunc->second(session); - VersionInfo::get(session).modify()->setVersion(++version); + VersionInfo::get(session).modify()->setVersion(++version); - LMS_LOG(DB, INFO) << "Migration complete to version " << version; + LMS_LOG(DB, INFO) << "Migration complete to version " << version; + } } } } diff --git a/src/libs/services/database/impl/Session.cpp b/src/libs/services/database/impl/Session.cpp index 0aa5d551..90387aaa 100644 --- a/src/libs/services/database/impl/Session.cpp +++ b/src/libs/services/database/impl/Session.cpp @@ -46,145 +46,145 @@ namespace Database { -Session::Session(Db& db) -: _db {db} -{ - _session.setConnectionPool(_db.getConnectionPool()); + Session::Session(Db& db) + : _db{ db } + { + _session.setConnectionPool(_db.getConnectionPool()); - _session.mapClass("version_info"); - _session.mapClass("artist"); - _session.mapClass("auth_token"); - _session.mapClass("cluster"); - _session.mapClass("cluster_type"); - _session.mapClass("listen"); - _session.mapClass("release"); - _session.mapClass("scan_settings"); - _session.mapClass("starred_artist"); - _session.mapClass("starred_release"); - _session.mapClass("starred_track"); - _session.mapClass("track"); - _session.mapClass("track_bookmark"); - _session.mapClass("track_artist_link"); - _session.mapClass("track_features"); - _session.mapClass("tracklist"); - _session.mapClass("tracklist_entry"); - _session.mapClass("user"); -} + _session.mapClass("version_info"); + _session.mapClass("artist"); + _session.mapClass("auth_token"); + _session.mapClass("cluster"); + _session.mapClass("cluster_type"); + _session.mapClass("listen"); + _session.mapClass("release"); + _session.mapClass("scan_settings"); + _session.mapClass("starred_artist"); + _session.mapClass("starred_release"); + _session.mapClass("starred_track"); + _session.mapClass("track"); + _session.mapClass("track_bookmark"); + _session.mapClass("track_artist_link"); + _session.mapClass("track_features"); + _session.mapClass("tracklist"); + _session.mapClass("tracklist_entry"); + _session.mapClass("user"); + } -UniqueTransaction::UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) -: _lock {mutex}, - _transaction {session} -{ -} + UniqueTransaction::UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) + : _lock{ mutex }, + _transaction{ session } + { + } -SharedTransaction::SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) -: _lock {mutex}, - _transaction {session} -{ -} + SharedTransaction::SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) + : _lock{ mutex }, + _transaction{ session } + { + } -void -Session::checkUniqueLocked() -{ - assert(_db.getMutex().isUniqueLocked()); -} + void Session::checkUniqueLocked() + { + assert(_db.getMutex().isUniqueLocked()); + } -void -Session::checkSharedLocked() -{ - assert(_db.getMutex().isSharedLocked()); -} + void Session::checkSharedLocked() + { + assert(_db.getMutex().isSharedLocked()); + } -UniqueTransaction -Session::createUniqueTransaction() -{ - return UniqueTransaction {_db.getMutex(), _session}; -} + UniqueTransaction Session::createUniqueTransaction() + { + return UniqueTransaction{ _db.getMutex(), _session }; + } -SharedTransaction -Session::createSharedTransaction() -{ - return SharedTransaction {_db.getMutex(), _session}; -} + SharedTransaction Session::createSharedTransaction() + { + return SharedTransaction{ _db.getMutex(), _session }; + } -void -Session::prepareTables() -{ - // Creation case - try - { - _session.createTables(); + void Session::prepareTables() + { + LMS_LOG(DB, INFO) << "Preparing tables..."; - LMS_LOG(DB, INFO) << "Tables created"; - } - catch (Wt::Dbo::Exception& e) - { - LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what(); - } + // Initial creation case + try + { + _session.createTables(); + LMS_LOG(DB, INFO) << "Tables created"; + } + 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(); + throw e; + } + } - Migration::doDbMigration(*this); + Migration::doDbMigration(*this); - // Indexes - { - auto uniqueTransaction {createUniqueTransaction()}; - _session.execute("CREATE INDEX IF NOT EXISTS artist_name_idx ON artist(name)"); - _session.execute("CREATE INDEX IF NOT EXISTS artist_sort_name_nocase_idx ON artist(sort_name COLLATE NOCASE)"); - _session.execute("CREATE INDEX IF NOT EXISTS artist_mbid_idx ON artist(mbid)"); - _session.execute("CREATE INDEX IF NOT EXISTS auth_token_user_idx ON auth_token(user_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS auth_token_expiry_idx ON auth_token(expiry)"); - _session.execute("CREATE INDEX IF NOT EXISTS auth_token_value_idx ON auth_token(value)"); - _session.execute("CREATE INDEX IF NOT EXISTS cluster_name_idx ON cluster(name)"); - _session.execute("CREATE INDEX IF NOT EXISTS cluster_cluster_type_idx ON cluster(cluster_type_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS cluster_type_name_idx ON cluster_type(name)"); - _session.execute("CREATE INDEX IF NOT EXISTS release_name_idx ON release(name)"); - _session.execute("CREATE INDEX IF NOT EXISTS release_name_nocase_idx ON release(name COLLATE NOCASE)"); - _session.execute("CREATE INDEX IF NOT EXISTS release_mbid_idx ON release(mbid)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_file_last_write_idx ON track(file_last_write)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_path_idx ON track(file_path)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_name_idx ON track(name)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_name_nocase_idx ON track(name COLLATE NOCASE)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_release_idx ON track(release_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_date_idx ON track(date)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)"); - _session.execute("CREATE INDEX IF NOT EXISTS tracklist_name_idx ON tracklist(name)"); - _session.execute("CREATE INDEX IF NOT EXISTS tracklist_user_idx ON tracklist(user_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_features_track_idx ON track_features(track_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_artist_idx ON track_artist_link(artist_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_track_idx ON track_artist_link(track_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_type_idx ON track_artist_link(type)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_artist_type_idx ON track_artist_link(artist_id,type)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_bookmark_user_idx ON track_bookmark(user_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS track_bookmark_user_track_idx ON track_bookmark(user_id,track_id)"); - _session.execute("CREATE INDEX IF NOT EXISTS listen_scrobbler_idx ON listen(scrobbler)"); - _session.execute("CREATE INDEX IF NOT EXISTS listen_user_scrobbler_idx ON listen(user_id,scrobbler)"); - _session.execute("CREATE INDEX IF NOT EXISTS listen_user_track_scrobbler_date_time_idx ON listen(user_id,track_id,scrobbler,date_time)"); - _session.execute("CREATE INDEX IF NOT EXISTS starred_artist_user_scrobbler_idx ON starred_artist(user_id,scrobbler)"); - _session.execute("CREATE INDEX IF NOT EXISTS starred_artist_artist_user_scrobbler_idx ON starred_artist(artist_id,user_id,scrobbler)"); - _session.execute("CREATE INDEX IF NOT EXISTS starred_release_user_scrobbler_idx ON starred_release(user_id,scrobbler)"); - _session.execute("CREATE INDEX IF NOT EXISTS starred_release_release_user_scrobbler_idx ON starred_release(release_id,user_id,scrobbler)"); - _session.execute("CREATE INDEX IF NOT EXISTS starred_track_user_scrobbler_idx ON starred_track(user_id,scrobbler)"); - _session.execute("CREATE INDEX IF NOT EXISTS starred_track_track_user_scrobbler_idx ON starred_track(track_id,user_id,scrobbler)"); - } + // Indexes + { + auto uniqueTransaction{ createUniqueTransaction() }; + _session.execute("CREATE INDEX IF NOT EXISTS artist_name_idx ON artist(name)"); + _session.execute("CREATE INDEX IF NOT EXISTS artist_sort_name_nocase_idx ON artist(sort_name COLLATE NOCASE)"); + _session.execute("CREATE INDEX IF NOT EXISTS artist_mbid_idx ON artist(mbid)"); + _session.execute("CREATE INDEX IF NOT EXISTS auth_token_user_idx ON auth_token(user_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS auth_token_expiry_idx ON auth_token(expiry)"); + _session.execute("CREATE INDEX IF NOT EXISTS auth_token_value_idx ON auth_token(value)"); + _session.execute("CREATE INDEX IF NOT EXISTS cluster_name_idx ON cluster(name)"); + _session.execute("CREATE INDEX IF NOT EXISTS cluster_cluster_type_idx ON cluster(cluster_type_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS cluster_type_name_idx ON cluster_type(name)"); + _session.execute("CREATE INDEX IF NOT EXISTS release_name_idx ON release(name)"); + _session.execute("CREATE INDEX IF NOT EXISTS release_name_nocase_idx ON release(name COLLATE NOCASE)"); + _session.execute("CREATE INDEX IF NOT EXISTS release_mbid_idx ON release(mbid)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_file_last_write_idx ON track(file_last_write)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_path_idx ON track(file_path)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_name_idx ON track(name)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_name_nocase_idx ON track(name COLLATE NOCASE)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_release_idx ON track(release_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_date_idx ON track(date)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)"); + _session.execute("CREATE INDEX IF NOT EXISTS tracklist_name_idx ON tracklist(name)"); + _session.execute("CREATE INDEX IF NOT EXISTS tracklist_user_idx ON tracklist(user_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_features_track_idx ON track_features(track_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_artist_idx ON track_artist_link(artist_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_track_idx ON track_artist_link(track_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_type_idx ON track_artist_link(type)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_artist_type_idx ON track_artist_link(artist_id,type)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_bookmark_user_idx ON track_bookmark(user_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS track_bookmark_user_track_idx ON track_bookmark(user_id,track_id)"); + _session.execute("CREATE INDEX IF NOT EXISTS listen_scrobbler_idx ON listen(scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS listen_user_scrobbler_idx ON listen(user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS listen_user_track_scrobbler_date_time_idx ON listen(user_id,track_id,scrobbler,date_time)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_artist_user_scrobbler_idx ON starred_artist(user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_artist_artist_user_scrobbler_idx ON starred_artist(artist_id,user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_release_user_scrobbler_idx ON starred_release(user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_release_release_user_scrobbler_idx ON starred_release(release_id,user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_track_user_scrobbler_idx ON starred_track(user_id,scrobbler)"); + _session.execute("CREATE INDEX IF NOT EXISTS starred_track_track_user_scrobbler_idx ON starred_track(track_id,user_id,scrobbler)"); + } - // Initial settings tables - { - auto uniqueTransaction {createUniqueTransaction()}; + // Initial settings tables + { + auto uniqueTransaction{ createUniqueTransaction() }; - ScanSettings::init(*this); - } -} + ScanSettings::init(*this); + } + } -void -Session::optimize() -{ - LMS_LOG(DB, DEBUG) << "Optimizing db..."; - { - auto uniqueTransaction {createUniqueTransaction()}; - _session.execute("ANALYZE"); - } - LMS_LOG(DB, DEBUG) << "Optimized db!"; -} + void Session::analyze() + { + LMS_LOG(DB, INFO) << "Analyzing database..."; + { + auto uniqueTransaction{ createUniqueTransaction() }; + _session.execute("ANALYZE"); + } + LMS_LOG(DB, INFO) << "Database Analyze complete"; + } } // namespace Database diff --git a/src/libs/services/database/include/services/database/Db.hpp b/src/libs/services/database/include/services/database/Db.hpp index b58077e8..a0298eb1 100644 --- a/src/libs/services/database/include/services/database/Db.hpp +++ b/src/libs/services/database/include/services/database/Db.hpp @@ -27,52 +27,47 @@ namespace Database { -class Session; -class Db -{ - public: - Db(const std::filesystem::path& dbPath, std::size_t connectionCount = 10); - ~Db(); + class Session; + class Db + { + public: + Db(const std::filesystem::path& dbPath, std::size_t connectionCount = 10); - 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: + Db(const Db&) = delete; + Db& operator=(const Db&) = delete; - private: - friend class Session; + friend class Session; - RecursiveSharedMutex& getMutex() { return _sharedMutex; } - Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; } + RecursiveSharedMutex& getMutex() { return _sharedMutex; } + Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; } - class ScopedConnection - { - public: - ScopedConnection(Wt::Dbo::SqlConnectionPool& pool); - ~ScopedConnection(); + class ScopedConnection + { + public: + ScopedConnection(Wt::Dbo::SqlConnectionPool& pool); + ~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: + ScopedConnection(const ScopedConnection&) = delete; + ScopedConnection& operator=(const ScopedConnection&) = delete; - private: - Wt::Dbo::SqlConnectionPool& _connectionPool; - std::unique_ptr _connection; - }; + Wt::Dbo::SqlConnectionPool& _connectionPool; + std::unique_ptr _connection; + }; - RecursiveSharedMutex _sharedMutex; - std::unique_ptr _connectionPool; + RecursiveSharedMutex _sharedMutex; + std::unique_ptr _connectionPool; - std::mutex _tlsSessionsMutex; - std::vector> _tlsSessions; -}; + std::mutex _tlsSessionsMutex; + std::vector> _tlsSessions; + }; } // namespace Database diff --git a/src/libs/services/database/include/services/database/Session.hpp b/src/libs/services/database/include/services/database/Session.hpp index d12b6cfb..0edba936 100644 --- a/src/libs/services/database/include/services/database/Session.hpp +++ b/src/libs/services/database/include/services/database/Session.hpp @@ -28,68 +28,67 @@ namespace Database { - class UniqueTransaction - { - private: - friend class Session; - UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); + class UniqueTransaction + { + private: + friend class Session; + UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); - std::unique_lock _lock; - Wt::Dbo::Transaction _transaction; - }; + std::unique_lock _lock; + Wt::Dbo::Transaction _transaction; + }; - class SharedTransaction - { - private: - friend class Session; - SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); + class SharedTransaction + { + private: + friend class Session; + SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); - std::shared_lock _lock; - Wt::Dbo::Transaction _transaction; - }; + std::shared_lock _lock; + Wt::Dbo::Transaction _transaction; + }; - class Db; - class Session - { - public: - Session (Db& database); + class Db; + class Session + { + public: + Session(Db& database); + ~Session(); - Session(const Session&) = delete; - Session(Session&&) = delete; - Session& operator=(const Session&) = delete; - Session& operator=(Session&&) = delete; + [[nodiscard]] UniqueTransaction createUniqueTransaction(); + [[nodiscard]] SharedTransaction createSharedTransaction(); - [[nodiscard]] UniqueTransaction createUniqueTransaction(); - [[nodiscard]] SharedTransaction createSharedTransaction(); + void checkUniqueLocked(); + void checkSharedLocked(); - void checkUniqueLocked(); - void checkSharedLocked(); + void analyze(); - void optimize(); + void prepareTables(); // need to run only once at startup - void prepareTables(); // need to run only once at startup + Wt::Dbo::Session& getDboSession() { return _session; } + Db& getDb() { return _db; } - Wt::Dbo::Session& getDboSession() { return _session; } - Db& getDb() { return _db; } + template + typename Object::pointer create(Args&&... args) + { + checkUniqueLocked(); - template - typename Object::pointer create(Args&&... args) - { - checkUniqueLocked(); + typename Object::pointer res{ Object::create(*this, std::forward(args)...) }; + getDboSession().flush(); - typename Object::pointer res {Object::create(*this, std::forward(args)...)}; - getDboSession().flush(); + if (res->hasOnPostCreated()) + res.modify()->onPostCreated(); - if (res->hasOnPostCreated()) - res.modify()->onPostCreated(); + return res; + } - return res; - } + private: + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; - private: - Db& _db; - Wt::Dbo::Session _session; - }; + Db& _db; + Wt::Dbo::Session _session; + }; } // namespace Database diff --git a/src/libs/services/database/test/Common.cpp b/src/libs/services/database/test/Common.cpp index 89a43da8..c32d8932 100644 --- a/src/libs/services/database/test/Common.cpp +++ b/src/libs/services/database/test/Common.cpp @@ -60,7 +60,7 @@ DatabaseFixture::SetUpTestCase() { Database::Session s {_tmpDb->getDb()}; s.prepareTables(); - s.optimize(); + s.analyze(); // remove default created entries { diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index e9b4cffb..4c4c1b95 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -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(); - // TODO make it a scan step - _dbSession.optimize(); + _dbSession.analyze(); if (!_abortScan) { diff --git a/src/lms/main.cpp b/src/lms/main.cpp index 4cb86c98..0dd1fe41 100644 --- a/src/lms/main.cpp +++ b/src/lms/main.cpp @@ -238,7 +238,10 @@ int main(int argc, char* argv[]) { Database::Session session {database}; 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;