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.
This commit is contained in:
Reto Brunner 2022-11-26 13:52:27 +01:00
parent deeea274da
commit 2d4143b779
2 changed files with 37 additions and 6 deletions

View file

@ -38,17 +38,30 @@ const schema = [
"CREATE INDEX IF NOT EXISTS time ON messages (time)",
];
class Deferred {
resolve!: () => void;
promise: Promise<void>;
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<Message[]> {
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<SearchResponse> {
await this.initDone.promise;
if (!this.isEnabled) {
// this should never be hit as messageProvider is checked in client.search()
throw new Error(

View file

@ -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'",