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
+45 -53
View File
@@ -28,70 +28,62 @@
namespace Database { namespace Database {
// Session living class handling the database and the login // Session living class handling the database and the login
Db::Db(const std::filesystem::path& dbPath, std::size_t connectionCount) 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..."; ScopedConnection connection{ *_connectionPool };
executeSql("pragma optimize"); connection->executeSql(sql);
LMS_LOG(DB, DEBUG) << "Optimizing db DONE"; }
}
void Session& Db::getTLSSession()
Db::executeSql(const std::string& sql) {
{ static thread_local Session* tlsSession{};
ScopedConnection connection {*_connectionPool};
connection->executeSql(sql);
}
Session& if (!tlsSession)
Db::getTLSSession() {
{ auto newSession{ std::make_unique<Session>(*this) };
static thread_local Session* tlsSession {}; tlsSession = newSession.get();
if (!tlsSession) {
{ std::scoped_lock lock{ _tlsSessionsMutex };
auto newSession {std::make_unique<Session>(*this)}; _tlsSessions.push_back(std::move(newSession));
tlsSession = newSession.get(); }
}
{ return *tlsSession;
std::scoped_lock lock {_tlsSessionsMutex}; }
_tlsSessions.push_back(std::move(newSession));
}
}
return *tlsSession; Db::ScopedConnection::ScopedConnection(Wt::Dbo::SqlConnectionPool& pool)
} : _connectionPool{ pool }
, _connection{ _connectionPool.getConnection() }
{
}
Db::ScopedConnection::ScopedConnection(Wt::Dbo::SqlConnectionPool& pool) Db::ScopedConnection::~ScopedConnection()
: _connectionPool {pool} {
, _connection {_connectionPool.getConnection()} _connectionPool.returnConnection(std::move(_connection));
{ }
}
Db::ScopedConnection::~ScopedConnection() Wt::Dbo::SqlConnection* Db::ScopedConnection::operator->() const
{ {
_connectionPool.returnConnection(std::move(_connection)); return _connection.get();
} }
Wt::Dbo::SqlConnection* Db::ScopedConnection::operator->() const
{
return _connection.get();
}
} // namespace Database } // namespace Database
+10 -15
View File
@@ -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,28 +669,24 @@ 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 };
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) }; auto itMigrationFunc{ migrationFunctions.find(version) };
assert(itMigrationFunc != std::cend(migrationFunctions)); assert(itMigrationFunc != std::cend(migrationFunctions));
itMigrationFunc->second(session); 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;
}
} }
} }
} }
+126 -126
View File
@@ -46,145 +46,145 @@
namespace Database namespace Database
{ {
Session::Session(Db& db) Session::Session(Db& db)
: _db {db} : _db{ db }
{ {
_session.setConnectionPool(_db.getConnectionPool()); _session.setConnectionPool(_db.getConnectionPool());
_session.mapClass<VersionInfo>("version_info"); _session.mapClass<VersionInfo>("version_info");
_session.mapClass<Artist>("artist"); _session.mapClass<Artist>("artist");
_session.mapClass<AuthToken>("auth_token"); _session.mapClass<AuthToken>("auth_token");
_session.mapClass<Cluster>("cluster"); _session.mapClass<Cluster>("cluster");
_session.mapClass<ClusterType>("cluster_type"); _session.mapClass<ClusterType>("cluster_type");
_session.mapClass<Listen>("listen"); _session.mapClass<Listen>("listen");
_session.mapClass<Release>("release"); _session.mapClass<Release>("release");
_session.mapClass<ScanSettings>("scan_settings"); _session.mapClass<ScanSettings>("scan_settings");
_session.mapClass<StarredArtist>("starred_artist"); _session.mapClass<StarredArtist>("starred_artist");
_session.mapClass<StarredRelease>("starred_release"); _session.mapClass<StarredRelease>("starred_release");
_session.mapClass<StarredTrack>("starred_track"); _session.mapClass<StarredTrack>("starred_track");
_session.mapClass<Track>("track"); _session.mapClass<Track>("track");
_session.mapClass<TrackBookmark>("track_bookmark"); _session.mapClass<TrackBookmark>("track_bookmark");
_session.mapClass<TrackArtistLink>("track_artist_link"); _session.mapClass<TrackArtistLink>("track_artist_link");
_session.mapClass<TrackFeatures>("track_features"); _session.mapClass<TrackFeatures>("track_features");
_session.mapClass<TrackList>("tracklist"); _session.mapClass<TrackList>("tracklist");
_session.mapClass<TrackListEntry>("tracklist_entry"); _session.mapClass<TrackListEntry>("tracklist_entry");
_session.mapClass<User>("user"); _session.mapClass<User>("user");
} }
UniqueTransaction::UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) UniqueTransaction::UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session)
: _lock {mutex}, : _lock{ mutex },
_transaction {session} _transaction{ session }
{ {
} }
SharedTransaction::SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session) SharedTransaction::SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session)
: _lock {mutex}, : _lock{ mutex },
_transaction {session} _transaction{ session }
{ {
} }
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() {
{ LMS_LOG(DB, INFO) << "Preparing tables...";
// Creation case
try
{
_session.createTables();
LMS_LOG(DB, INFO) << "Tables created"; // Initial creation case
} try
catch (Wt::Dbo::Exception& e) {
{ _session.createTables();
LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what(); 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 // Indexes
{ {
auto uniqueTransaction {createUniqueTransaction()}; auto uniqueTransaction{ createUniqueTransaction() };
_session.execute("CREATE INDEX IF NOT EXISTS artist_name_idx ON artist(name)"); _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_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 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_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_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 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_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_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 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_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_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 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_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_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_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_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_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_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_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_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 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_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 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_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_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_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_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_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_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 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_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_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 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_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_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_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_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_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)"); _session.execute("CREATE INDEX IF NOT EXISTS starred_track_track_user_scrobbler_idx ON starred_track(track_id,user_id,scrobbler)");
} }
// Initial settings tables // Initial settings tables
{ {
auto uniqueTransaction {createUniqueTransaction()}; auto uniqueTransaction{ createUniqueTransaction() };
ScanSettings::init(*this); ScanSettings::init(*this);
} }
} }
void void Session::analyze()
Session::optimize() {
{ LMS_LOG(DB, INFO) << "Analyzing database...";
LMS_LOG(DB, DEBUG) << "Optimizing db..."; {
{ auto uniqueTransaction{ createUniqueTransaction() };
auto uniqueTransaction {createUniqueTransaction()}; _session.execute("ANALYZE");
_session.execute("ANALYZE"); }
} LMS_LOG(DB, INFO) << "Database Analyze complete";
LMS_LOG(DB, DEBUG) << "Optimized db!"; }
}
} // namespace Database } // namespace Database
@@ -27,52 +27,47 @@
namespace Database { namespace Database {
class Session; class Session;
class Db 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; Session& getTLSSession();
Db(Db&&) = delete;
Db& operator=(const Db&) = delete;
Db& operator=(Db&&) = delete;
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; } RecursiveSharedMutex& getMutex() { return _sharedMutex; }
Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; } Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; }
class ScopedConnection class ScopedConnection
{ {
public: public:
ScopedConnection(Wt::Dbo::SqlConnectionPool& pool); ScopedConnection(Wt::Dbo::SqlConnectionPool& pool);
~ScopedConnection(); ~ScopedConnection();
ScopedConnection(const ScopedConnection& ) = delete; Wt::Dbo::SqlConnection* operator->() const;
ScopedConnection(ScopedConnection&& ) = delete;
ScopedConnection& operator=(const ScopedConnection& ) = delete;
ScopedConnection& operator=(ScopedConnection&& ) = delete;
Wt::Dbo::SqlConnection* operator->() const; private:
ScopedConnection(const ScopedConnection&) = delete;
ScopedConnection& operator=(const ScopedConnection&) = delete;
private: Wt::Dbo::SqlConnectionPool& _connectionPool;
Wt::Dbo::SqlConnectionPool& _connectionPool; std::unique_ptr<Wt::Dbo::SqlConnection> _connection;
std::unique_ptr<Wt::Dbo::SqlConnection> _connection; };
};
RecursiveSharedMutex _sharedMutex; RecursiveSharedMutex _sharedMutex;
std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool; std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool;
std::mutex _tlsSessionsMutex; std::mutex _tlsSessionsMutex;
std::vector<std::unique_ptr<Session>> _tlsSessions; std::vector<std::unique_ptr<Session>> _tlsSessions;
}; };
} // namespace Database } // namespace Database
@@ -28,68 +28,67 @@
namespace Database namespace Database
{ {
class UniqueTransaction class UniqueTransaction
{ {
private: private:
friend class Session; friend class Session;
UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session);
std::unique_lock<RecursiveSharedMutex> _lock; std::unique_lock<RecursiveSharedMutex> _lock;
Wt::Dbo::Transaction _transaction; Wt::Dbo::Transaction _transaction;
}; };
class SharedTransaction class SharedTransaction
{ {
private: private:
friend class Session; friend class Session;
SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session); SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session);
std::shared_lock<RecursiveSharedMutex> _lock; std::shared_lock<RecursiveSharedMutex> _lock;
Wt::Dbo::Transaction _transaction; Wt::Dbo::Transaction _transaction;
}; };
class Db; class Db;
class Session class Session
{ {
public: public:
Session (Db& database); Session(Db& database);
~Session();
Session(const Session&) = delete; [[nodiscard]] UniqueTransaction createUniqueTransaction();
Session(Session&&) = delete; [[nodiscard]] SharedTransaction createSharedTransaction();
Session& operator=(const Session&) = delete;
Session& operator=(Session&&) = delete;
[[nodiscard]] UniqueTransaction createUniqueTransaction(); void checkUniqueLocked();
[[nodiscard]] SharedTransaction createSharedTransaction(); void checkSharedLocked();
void checkUniqueLocked(); void analyze();
void checkSharedLocked();
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; } template <typename Object, typename... Args>
Db& getDb() { return _db; } typename Object::pointer create(Args&&... args)
{
checkUniqueLocked();
template <typename Object, typename... Args> typename Object::pointer res{ Object::create(*this, std::forward<Args>(args)...) };
typename Object::pointer create(Args&&... args) getDboSession().flush();
{
checkUniqueLocked();
typename Object::pointer res {Object::create(*this, std::forward<Args>(args)...)}; if (res->hasOnPostCreated())
getDboSession().flush(); res.modify()->onPostCreated();
if (res->hasOnPostCreated()) return res;
res.modify()->onPostCreated(); }
return res; private:
} Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
private: Db& _db;
Db& _db; Wt::Dbo::Session _session;
Wt::Dbo::Session _session; };
};
} // namespace Database } // namespace Database
+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;