core: Store session state in database, migrate old
authorJanne Koschinski <janne@kuschku.de>
Sat, 3 Mar 2018 07:45:31 +0000 (08:45 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Wed, 23 May 2018 22:33:28 +0000 (00:33 +0200)
Store core session state in database rather than configuration file.
This improves containerization as the configuration file doesn't need
to be preserved, and also makes database restoration handle restoring
active sessions.

Migrate existing legacy sessions to database storage, ensuring that
the core state fallback is handled correctly, too.

Part of GH-341.

21 files changed:
src/core/SQL/PostgreSQL/insert_core_state.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/migrate_write_corestate.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/select_core_state.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/setup_150_corestate.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/update_core_state.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/version/28/upgrade_000_create_corestate.sql [new file with mode: 0644]
src/core/SQL/SQLite/insert_core_state.sql [new file with mode: 0644]
src/core/SQL/SQLite/migrate_read_corestate.sql [new file with mode: 0644]
src/core/SQL/SQLite/select_core_state.sql [new file with mode: 0644]
src/core/SQL/SQLite/setup_160_corestate.sql [new file with mode: 0644]
src/core/SQL/SQLite/update_core_state.sql [new file with mode: 0644]
src/core/SQL/SQLite/version/30/upgrade_000_create_corestate.sql [new file with mode: 0644]
src/core/abstractsqlstorage.cpp
src/core/abstractsqlstorage.h
src/core/core.cpp
src/core/postgresqlstorage.cpp
src/core/postgresqlstorage.h
src/core/sql.qrc
src/core/sqlitestorage.cpp
src/core/sqlitestorage.h
src/core/storage.h

diff --git a/src/core/SQL/PostgreSQL/insert_core_state.sql b/src/core/SQL/PostgreSQL/insert_core_state.sql
new file mode 100644 (file)
index 0000000..899fc04
--- /dev/null
@@ -0,0 +1,2 @@
+INSERT INTO core_state (key, value)
+VALUES (:key, :value)
diff --git a/src/core/SQL/PostgreSQL/migrate_write_corestate.sql b/src/core/SQL/PostgreSQL/migrate_write_corestate.sql
new file mode 100644 (file)
index 0000000..877822d
--- /dev/null
@@ -0,0 +1,2 @@
+INSERT INTO core_state (key, value)
+VALUES (?, ?)
diff --git a/src/core/SQL/PostgreSQL/select_core_state.sql b/src/core/SQL/PostgreSQL/select_core_state.sql
new file mode 100644 (file)
index 0000000..37565ce
--- /dev/null
@@ -0,0 +1,3 @@
+SELECT value
+FROM core_state
+WHERE key = :key
diff --git a/src/core/SQL/PostgreSQL/setup_150_corestate.sql b/src/core/SQL/PostgreSQL/setup_150_corestate.sql
new file mode 100644 (file)
index 0000000..a26d852
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE TABLE core_state (
+    key TEXT NOT NULL,
+    value bytea,
+    PRIMARY KEY (key)
+)
diff --git a/src/core/SQL/PostgreSQL/update_core_state.sql b/src/core/SQL/PostgreSQL/update_core_state.sql
new file mode 100644 (file)
index 0000000..922dc95
--- /dev/null
@@ -0,0 +1,3 @@
+UPDATE core_state
+SET value = :value
+WHERE key = :key
diff --git a/src/core/SQL/PostgreSQL/version/28/upgrade_000_create_corestate.sql b/src/core/SQL/PostgreSQL/version/28/upgrade_000_create_corestate.sql
new file mode 100644 (file)
index 0000000..a26d852
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE TABLE core_state (
+    key TEXT NOT NULL,
+    value bytea,
+    PRIMARY KEY (key)
+)
diff --git a/src/core/SQL/SQLite/insert_core_state.sql b/src/core/SQL/SQLite/insert_core_state.sql
new file mode 100644 (file)
index 0000000..899fc04
--- /dev/null
@@ -0,0 +1,2 @@
+INSERT INTO core_state (key, value)
+VALUES (:key, :value)
diff --git a/src/core/SQL/SQLite/migrate_read_corestate.sql b/src/core/SQL/SQLite/migrate_read_corestate.sql
new file mode 100644 (file)
index 0000000..0bc5366
--- /dev/null
@@ -0,0 +1,3 @@
+SELECT key, value
+FROM core_state
+
diff --git a/src/core/SQL/SQLite/select_core_state.sql b/src/core/SQL/SQLite/select_core_state.sql
new file mode 100644 (file)
index 0000000..37565ce
--- /dev/null
@@ -0,0 +1,3 @@
+SELECT value
+FROM core_state
+WHERE key = :key
diff --git a/src/core/SQL/SQLite/setup_160_corestate.sql b/src/core/SQL/SQLite/setup_160_corestate.sql
new file mode 100644 (file)
index 0000000..a26d852
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE TABLE core_state (
+    key TEXT NOT NULL,
+    value bytea,
+    PRIMARY KEY (key)
+)
diff --git a/src/core/SQL/SQLite/update_core_state.sql b/src/core/SQL/SQLite/update_core_state.sql
new file mode 100644 (file)
index 0000000..922dc95
--- /dev/null
@@ -0,0 +1,3 @@
+UPDATE core_state
+SET value = :value
+WHERE key = :key
diff --git a/src/core/SQL/SQLite/version/30/upgrade_000_create_corestate.sql b/src/core/SQL/SQLite/version/30/upgrade_000_create_corestate.sql
new file mode 100644 (file)
index 0000000..a26d852
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE TABLE core_state (
+    key TEXT NOT NULL,
+    value bytea,
+    PRIMARY KEY (key)
+)
index 4ecf623..cb8fbd1 100644 (file)
@@ -409,6 +409,8 @@ QString AbstractSqlMigrator::migrationObject(MigrationObject moType)
         return "IrcServer";
     case UserSetting:
         return "UserSetting";
+    case CoreState:
+        return "CoreState";
     };
     return QString();
 }
@@ -502,6 +504,10 @@ bool AbstractSqlMigrationReader::migrateTo(AbstractSqlMigrationWriter *writer)
     if (!transferMo(UserSetting, userSettingMo))
         return false;
 
+    CoreStateMO coreStateMO;
+    if (!transferMo(CoreState, coreStateMO))
+        return false;
+
     if (!_writer->postProcess())
         abortMigration();
     return finalizeMigration();
index 2e57230..e894555 100644 (file)
@@ -285,6 +285,11 @@ public:
         QByteArray settingvalue;
     };
 
+    struct CoreStateMO {
+        QString key;
+        QByteArray value;
+    };
+
     enum MigrationObject {
         QuasselUser,
         Sender,
@@ -294,7 +299,8 @@ public:
         Buffer,
         Backlog,
         IrcServer,
-        UserSetting
+        UserSetting,
+        CoreState
     };
 
     AbstractSqlMigrator();
@@ -340,6 +346,7 @@ public:
     virtual bool readMo(BacklogMO &backlog) = 0;
     virtual bool readMo(IrcServerMO &ircserver) = 0;
     virtual bool readMo(UserSettingMO &userSetting) = 0;
+    virtual bool readMo(CoreStateMO &coreState) = 0;
 
     bool migrateTo(AbstractSqlMigrationWriter *writer);
 
@@ -365,6 +372,7 @@ public:
     virtual bool writeMo(const BacklogMO &backlog) = 0;
     virtual bool writeMo(const IrcServerMO &ircserver) = 0;
     virtual bool writeMo(const UserSettingMO &userSetting) = 0;
+    virtual bool writeMo(const CoreStateMO &coreState) = 0;
 
     inline bool migrateFrom(AbstractSqlMigrationReader *reader) { return reader->migrateTo(this); }
 
index aecb06e..7fbd4e7 100644 (file)
@@ -259,14 +259,10 @@ Core::~Core()
 
 void Core::saveState()
 {
-    CoreSettings s;
-    QVariantMap state;
     QVariantList activeSessions;
     foreach(UserId user, instance()->_sessions.keys())
         activeSessions << QVariant::fromValue<UserId>(user);
-    state["CoreStateVersion"] = 1;
-    state["ActiveSessions"] = activeSessions;
-    s.setCoreState(state);
+    instance()->_storage->setCoreState(activeSessions);
 }
 
 
@@ -289,7 +285,9 @@ void Core::restoreState()
     }
     */
 
-    QVariantList activeSessions = s.coreState().toMap()["ActiveSessions"].toList();
+    const QList<QVariant> &activeSessionsFallback = s.coreState().toMap()["ActiveSessions"].toList();
+    QVariantList activeSessions = instance()->_storage->getCoreState(activeSessionsFallback);
+
     if (activeSessions.count() > 0) {
         quInfo() << "Restoring previous core state...";
         foreach(QVariant v, activeSessions) {
index 5b52afd..a6f2a72 100644 (file)
@@ -397,6 +397,58 @@ QVariant PostgreSqlStorage::getUserSetting(UserId userId, const QString &setting
 }
 
 
+void PostgreSqlStorage::setCoreState(const QVariantList &data)
+{
+    QByteArray rawData;
+    QDataStream out(&rawData, QIODevice::WriteOnly);
+    out.setVersion(QDataStream::Qt_4_2);
+    out << data;
+
+    QSqlDatabase db = logDb();
+    QSqlQuery selectQuery(db);
+    selectQuery.prepare(queryString("select_core_state"));
+    selectQuery.bindValue(":key", "active_sessions");
+    safeExec(selectQuery);
+    watchQuery(selectQuery);
+
+    QString setQueryString;
+    if (!selectQuery.first()) {
+        setQueryString = queryString("insert_core_state");
+    }
+    else {
+        setQueryString = queryString("update_core_state");
+    }
+
+    QSqlQuery setQuery(db);
+    setQuery.prepare(setQueryString);
+    setQuery.bindValue(":key", "active_sessions");
+    setQuery.bindValue(":value", rawData);
+    safeExec(setQuery);
+    watchQuery(setQuery);
+}
+
+
+QVariantList PostgreSqlStorage::getCoreState(const QVariantList &defaultData)
+{
+    QSqlQuery query(logDb());
+    query.prepare(queryString("select_core_state"));
+    query.bindValue(":key", "active_sessions");
+    safeExec(query);
+    watchQuery(query);
+
+    if (query.first()) {
+        QVariantList data;
+        QByteArray rawData = query.value(0).toByteArray();
+        QDataStream in(&rawData, QIODevice::ReadOnly);
+        in.setVersion(QDataStream::Qt_4_2);
+        in >> data;
+        return data;
+    } else {
+        return defaultData;
+    }
+}
+
+
 IdentityId PostgreSqlStorage::createIdentity(UserId user, CoreIdentity &identity)
 {
     IdentityId identityId;
@@ -2176,6 +2228,9 @@ bool PostgreSqlMigrationWriter::prepareQuery(MigrationObject mo)
     case UserSetting:
         query = queryString("migrate_write_usersetting");
         break;
+    case CoreState:
+        query = queryString("migrate_write_corestate");
+        break;
     }
     newQuery(query, logDb());
     return true;
@@ -2352,6 +2407,13 @@ bool PostgreSqlMigrationWriter::writeMo(const UserSettingMO &userSetting)
     return exec();
 }
 
+bool PostgreSqlMigrationWriter::writeMo(const CoreStateMO &coreState)
+{
+    bindValue(0, coreState.key);
+    bindValue(1, coreState.value);
+    return exec();
+}
+
 
 bool PostgreSqlMigrationWriter::postProcess()
 {
index e9fc874..1c6afba 100644 (file)
@@ -57,6 +57,8 @@ public slots:
     void delUser(UserId user) override;
     void setUserSetting(UserId userId, const QString &settingName, const QVariant &data) override;
     QVariant getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData = QVariant()) override;
+    void setCoreState(const QVariantList &data) override;
+    QVariantList getCoreState(const QVariantList &data) override;
 
     /* Identity handling */
     IdentityId createIdentity(UserId user, CoreIdentity &identity) override;
@@ -178,6 +180,7 @@ public:
     bool writeMo(const BacklogMO &backlog) override;
     bool writeMo(const IrcServerMO &ircserver) override;
     bool writeMo(const UserSettingMO &userSetting) override;
+    bool writeMo(const CoreStateMO &coreState) override;
 
     bool prepareQuery(MigrationObject mo) override;
 
index c90d472..d9578c0 100644 (file)
@@ -13,6 +13,7 @@
     <file>./SQL/PostgreSQL/delete_nicks.sql</file>
     <file>./SQL/PostgreSQL/delete_quasseluser.sql</file>
     <file>./SQL/PostgreSQL/insert_buffer.sql</file>
+    <file>./SQL/PostgreSQL/insert_core_state.sql</file>
     <file>./SQL/PostgreSQL/insert_identity.sql</file>
     <file>./SQL/PostgreSQL/insert_message.sql</file>
     <file>./SQL/PostgreSQL/insert_network.sql</file>
@@ -23,6 +24,7 @@
     <file>./SQL/PostgreSQL/insert_user_setting.sql</file>
     <file>./SQL/PostgreSQL/migrate_write_backlog.sql</file>
     <file>./SQL/PostgreSQL/migrate_write_buffer.sql</file>
+    <file>./SQL/PostgreSQL/migrate_write_corestate.sql</file>
     <file>./SQL/PostgreSQL/migrate_write_identity.sql</file>
     <file>./SQL/PostgreSQL/migrate_write_identity_nick.sql</file>
     <file>./SQL/PostgreSQL/migrate_write_ircserver.sql</file>
@@ -48,6 +50,7 @@
     <file>./SQL/PostgreSQL/select_buffers_for_network.sql</file>
     <file>./SQL/PostgreSQL/select_checkidentity.sql</file>
     <file>./SQL/PostgreSQL/select_connected_networks.sql</file>
+    <file>./SQL/PostgreSQL/select_core_state.sql</file>
     <file>./SQL/PostgreSQL/select_identities.sql</file>
     <file>./SQL/PostgreSQL/select_internaluser.sql</file>
     <file>./SQL/PostgreSQL/select_messagesAll.sql</file>
@@ -87,6 +90,7 @@
     <file>./SQL/PostgreSQL/setup_120_alter_messageid_seq.sql</file>
     <file>./SQL/PostgreSQL/setup_130_function_lastmsgid.sql</file>
     <file>./SQL/PostgreSQL/setup_140_sender_idx.sql</file>
+    <file>./SQL/PostgreSQL/setup_150_corestate.sql</file>
     <file>./SQL/PostgreSQL/update_backlog_bufferid.sql</file>
     <file>./SQL/PostgreSQL/update_buffer_bufferactivity.sql</file>
     <file>./SQL/PostgreSQL/update_buffer_cipher.sql</file>
     <file>./SQL/PostgreSQL/update_buffer_name.sql</file>
     <file>./SQL/PostgreSQL/update_buffer_persistent_channel.sql</file>
     <file>./SQL/PostgreSQL/update_buffer_set_channel_key.sql</file>
+    <file>./SQL/PostgreSQL/update_core_state.sql</file>
     <file>./SQL/PostgreSQL/update_identity.sql</file>
     <file>./SQL/PostgreSQL/update_network.sql</file>
     <file>./SQL/PostgreSQL/update_network_connected.sql</file>
     <file>./SQL/PostgreSQL/version/27/upgrade_010_update_sender_add_avatarurl.sql</file>
     <file>./SQL/PostgreSQL/version/27/upgrade_020_update_sender_add_new_constraint.sql</file>
     <file>./SQL/PostgreSQL/version/27/upgrade_030_upgrade_sender_drop_old_constraint.sql</file>
+    <file>./SQL/PostgreSQL/version/28/upgrade_000_create_corestate.sql</file>
     <file>./SQL/SQLite/delete_backlog_by_uid.sql</file>
     <file>./SQL/SQLite/delete_backlog_for_buffer.sql</file>
     <file>./SQL/SQLite/delete_backlog_for_network.sql</file>
     <file>./SQL/SQLite/delete_nicks.sql</file>
     <file>./SQL/SQLite/delete_quasseluser.sql</file>
     <file>./SQL/SQLite/insert_buffer.sql</file>
+    <file>./SQL/SQLite/insert_core_state.sql</file>
     <file>./SQL/SQLite/insert_identity.sql</file>
     <file>./SQL/SQLite/insert_message.sql</file>
     <file>./SQL/SQLite/insert_network.sql</file>
     <file>./SQL/SQLite/insert_user_setting.sql</file>
     <file>./SQL/SQLite/migrate_read_backlog.sql</file>
     <file>./SQL/SQLite/migrate_read_buffer.sql</file>
+    <file>./SQL/SQLite/migrate_read_corestate.sql</file>
     <file>./SQL/SQLite/migrate_read_identity.sql</file>
     <file>./SQL/SQLite/migrate_read_identity_nick.sql</file>
     <file>./SQL/SQLite/migrate_read_ircserver.sql</file>
     <file>./SQL/SQLite/select_buffers_for_network.sql</file>
     <file>./SQL/SQLite/select_checkidentity.sql</file>
     <file>./SQL/SQLite/select_connected_networks.sql</file>
+    <file>./SQL/SQLite/select_core_state.sql</file>
     <file>./SQL/SQLite/select_identities.sql</file>
     <file>./SQL/SQLite/select_internaluser.sql</file>
     <file>./SQL/SQLite/select_messagesAll.sql</file>
     <file>./SQL/SQLite/setup_130_identity.sql</file>
     <file>./SQL/SQLite/setup_140_identity_nick.sql</file>
     <file>./SQL/SQLite/setup_150_sender_idx.sql</file>
+    <file>./SQL/SQLite/setup_160_corestate.sql</file>
     <file>./SQL/SQLite/update_backlog_bufferid.sql</file>
     <file>./SQL/SQLite/update_buffer_bufferactivity.sql</file>
     <file>./SQL/SQLite/update_buffer_cipher.sql</file>
     <file>./SQL/SQLite/update_buffer_name.sql</file>
     <file>./SQL/SQLite/update_buffer_persistent_channel.sql</file>
     <file>./SQL/SQLite/update_buffer_set_channel_key.sql</file>
+    <file>./SQL/SQLite/update_core_state.sql</file>
     <file>./SQL/SQLite/update_identity.sql</file>
     <file>./SQL/SQLite/update_network.sql</file>
     <file>./SQL/SQLite/update_network_connected.sql</file>
     <file>./SQL/SQLite/version/29/upgrade_020_drop_sender.sql</file>
     <file>./SQL/SQLite/version/29/upgrade_030_rename_sender_tmp_sender.sql</file>
     <file>./SQL/SQLite/version/29/upgrade_040_update_sender_add_realname_avatarurl.sql</file>
+    <file>./SQL/SQLite/version/30/upgrade_000_create_corestate.sql</file>
 </qresource>
 </RCC>
index 5139d38..7694021 100644 (file)
@@ -376,6 +376,60 @@ QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName
 }
 
 
+void SqliteStorage::setCoreState(const QVariantList &data)
+{
+    QByteArray rawData;
+    QDataStream out(&rawData, QIODevice::WriteOnly);
+    out.setVersion(QDataStream::Qt_4_2);
+    out << data;
+
+    QSqlDatabase db = logDb();
+    db.transaction();
+    {
+        QSqlQuery query(db);
+        query.prepare(queryString("insert_core_state"));
+        query.bindValue(":key", "active_sessions");
+        query.bindValue(":value", rawData);
+        lockForWrite();
+        safeExec(query);
+
+        if (query.lastError().isValid()) {
+            QSqlQuery updateQuery(db);
+            updateQuery.prepare(queryString("update_core_state"));
+            updateQuery.bindValue(":key", "active_sessions");
+            updateQuery.bindValue(":value", rawData);
+            safeExec(updateQuery);
+        }
+        db.commit();
+    }
+    unlock();
+}
+
+
+QVariantList SqliteStorage::getCoreState(const QVariantList &defaultData)
+{
+    QVariantList data;
+    {
+        QSqlQuery query(logDb());
+        query.prepare(queryString("select_core_state"));
+        query.bindValue(":key", "active_sessions");
+        lockForRead();
+        safeExec(query);
+
+        if (query.first()) {
+            QByteArray rawData = query.value(0).toByteArray();
+            QDataStream in(&rawData, QIODevice::ReadOnly);
+            in.setVersion(QDataStream::Qt_4_2);
+            in >> data;
+        } else {
+            data = defaultData;
+        }
+    }
+    unlock();
+    return data;
+}
+
+
 IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity)
 {
     IdentityId identityId;
@@ -2220,6 +2274,9 @@ bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
     case UserSetting:
         newQuery(queryString("migrate_read_usersetting"), logDb());
         break;
+    case CoreState:
+        newQuery(queryString("migrate_read_corestate"), logDb());
+        break;
     }
     return exec();
 }
@@ -2431,3 +2488,15 @@ bool SqliteMigrationReader::readMo(UserSettingMO &userSetting)
 
     return true;
 }
+
+
+bool SqliteMigrationReader::readMo(CoreStateMO &coreState)
+{
+    if (!next())
+        return false;
+
+    coreState.key = value(0).toString();
+    coreState.value = value(1).toByteArray();
+
+    return true;
+}
index 41312f1..5d396ad 100644 (file)
@@ -58,6 +58,8 @@ public slots:
     void delUser(UserId user) override;
     void setUserSetting(UserId userId, const QString &settingName, const QVariant &data) override;
     QVariant getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData = QVariant()) override;
+    void setCoreState(const QVariantList &data) override;
+    QVariantList getCoreState(const QVariantList &data) override;
 
     /* Identity handling */
     IdentityId createIdentity(UserId user, CoreIdentity &identity) override;
@@ -162,6 +164,7 @@ public:
     bool readMo(BacklogMO &backlog) override;
     bool readMo(IrcServerMO &ircserver) override;
     bool readMo(UserSettingMO &userSetting) override;
+    bool readMo(CoreStateMO &coreState) override;
 
     bool prepareQuery(MigrationObject mo) override;
 
index 7cbc3e3..5a0a640 100644 (file)
@@ -171,6 +171,19 @@ public slots:
      */
     virtual QVariant getUserSetting(UserId userId, const QString &settingName, const QVariant &data = QVariant()) = 0;
 
+    //! Store core state
+    /**
+     * \param data         Active Sessions
+     */
+    virtual void setCoreState(const QVariantList &data) = 0;
+
+    //! Retrieve core state
+    /**
+     * \param default      Value to return in case it's unset.
+     * \return Active Sessions
+     */
+    virtual QVariantList getCoreState(const QVariantList &data = QVariantList()) = 0;
+
     /* Identity handling */
     virtual IdentityId createIdentity(UserId user, CoreIdentity &identity) = 0;
     virtual bool updateIdentity(UserId user, const CoreIdentity &identity) = 0;