From 2d4143b7798c9cf0600280a5a79cb9061585be0e Mon Sep 17 00:00:00 2001 From: Reto Brunner Date: Sat, 26 Nov 2022 13:52:27 +0100 Subject: [PATCH] sqlite: synchronize enable() internally TL is stupid and doesn't wait for message{Provider,Storage} to settle before it starts using the store. While this should be fixed globally, we can hack around the problem by pushing everything onto the call stack and hope that we'll eventually finish the setup before we blow the stack. --- server/plugins/messageStorage/sqlite.ts | 31 ++++++++++++++++++++++++- test/plugins/sqlite.ts | 12 ++++++---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/server/plugins/messageStorage/sqlite.ts b/server/plugins/messageStorage/sqlite.ts index 3adea7a4..fb7d9c3e 100644 --- a/server/plugins/messageStorage/sqlite.ts +++ b/server/plugins/messageStorage/sqlite.ts @@ -38,17 +38,30 @@ const schema = [ "CREATE INDEX IF NOT EXISTS time ON messages (time)", ]; +class Deferred { + resolve!: () => void; + promise: Promise; + + constructor() { + this.promise = new Promise((resolve) => { + this.resolve = resolve; + }); + } +} + class SqliteMessageStorage implements ISqliteMessageStorage { client: Client; isEnabled: boolean; database!: Database; + initDone: Deferred; constructor(client: Client) { this.client = client; this.isEnabled = false; + this.initDone = new Deferred(); } - async enable() { + async _enable() { const logsPath = Config.getUserLogsPath(); const sqlitePath = path.join(logsPath, `${this.client.name}.sqlite3`); @@ -70,6 +83,14 @@ class SqliteMessageStorage implements ISqliteMessageStorage { } } + async enable() { + try { + await this._enable(); + } finally { + this.initDone.resolve(); // unblock the instance methods + } + } + async run_migrations() { for (const stmt of schema) { await this.serialize_run(stmt, []); @@ -127,6 +148,8 @@ class SqliteMessageStorage implements ISqliteMessageStorage { } async index(network: Network, channel: Chan, msg: Msg) { + await this.initDone.promise; + if (!this.isEnabled) { return; } @@ -155,6 +178,8 @@ class SqliteMessageStorage implements ISqliteMessageStorage { } async deleteChannel(network: Network, channel: Channel) { + await this.initDone.promise; + if (!this.isEnabled) { return; } @@ -172,6 +197,8 @@ class SqliteMessageStorage implements ISqliteMessageStorage { * @param channel Channel - Channel object for which to load messages for */ async getMessages(network: Network, channel: Channel): Promise { + await this.initDone.promise; + if (!this.isEnabled || Config.values.maxHistory === 0) { return []; } @@ -199,6 +226,8 @@ class SqliteMessageStorage implements ISqliteMessageStorage { } async search(query: SearchQuery): Promise { + await this.initDone.promise; + if (!this.isEnabled) { // this should never be hit as messageProvider is checked in client.search() throw new Error( diff --git a/test/plugins/sqlite.ts b/test/plugins/sqlite.ts index f2ad6750..4476faf0 100644 --- a/test/plugins/sqlite.ts +++ b/test/plugins/sqlite.ts @@ -37,11 +37,6 @@ describe("SQLite Message Storage", function () { fs.rmdir(path.join(Config.getHomePath(), "logs"), done); }); - it("should resolve an empty array when disabled", async function () { - const messages = await store.getMessages(null as any, null as any); - expect(messages).to.be.empty; - }); - it("should create database file", async function () { expect(store.isEnabled).to.be.false; expect(fs.existsSync(expectedPath)).to.be.false; @@ -50,6 +45,13 @@ describe("SQLite Message Storage", function () { expect(store.isEnabled).to.be.true; }); + it("should resolve an empty array when disabled", async function () { + store.isEnabled = false; + const messages = await store.getMessages(null as any, null as any); + expect(messages).to.be.empty; + store.isEnabled = true; + }); + it("should create tables", function (done) { store.database.all( "SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'table'",