Manually manage full-text search table

This commit is contained in:
Scott Nonnenberg 2021-02-04 12:46:55 -08:00 committed by GitHub
parent 245f8c665d
commit 2f90d6aca9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 223 additions and 84 deletions

View file

@ -21,12 +21,15 @@ const ERASE_SQL_KEY = 'erase-sql-key';
let singleQueue = null; let singleQueue = null;
let multipleQueue = null; let multipleQueue = null;
// Note: we don't want queue timeouts, because delays here are due to in-progress sql
// operations. For example we might try to start a transaction when the prevous isn't
// done, causing that database operation to fail.
function makeNewSingleQueue() { function makeNewSingleQueue() {
singleQueue = new Queue({ concurrency: 1, timeout: 1000 * 60 * 2 }); singleQueue = new Queue({ concurrency: 1 });
return singleQueue; return singleQueue;
} }
function makeNewMultipleQueue() { function makeNewMultipleQueue() {
multipleQueue = new Queue({ concurrency: 10, timeout: 1000 * 60 * 2 }); multipleQueue = new Queue({ concurrency: 10 });
return multipleQueue; return multipleQueue;
} }

View file

@ -54,12 +54,12 @@ export const ConfirmationDialog = React.memo(
const handleAction = React.useCallback( const handleAction = React.useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => { (e: React.MouseEvent<HTMLButtonElement>) => {
onClose();
if (e.currentTarget.dataset.action) { if (e.currentTarget.dataset.action) {
const actionIndex = parseInt(e.currentTarget.dataset.action, 10); const actionIndex = parseInt(e.currentTarget.dataset.action, 10);
const { action } = actions[actionIndex]; const { action } = actions[actionIndex];
action(); action();
} }
onClose();
}, },
[onClose, actions] [onClose, actions]
); );

View file

@ -1621,6 +1621,30 @@ async function updateToSchemaVersion22(
} }
} }
async function updateToSchemaVersion23(
currentVersion: number,
instance: PromisifiedSQLDatabase
) {
if (currentVersion >= 23) {
return;
}
try {
await instance.run('BEGIN TRANSACTION;');
// Remove triggers which keep full-text search up to date
await instance.run('DROP TRIGGER messages_on_insert;');
await instance.run('DROP TRIGGER messages_on_update;');
await instance.run('DROP TRIGGER messages_on_delete;');
await instance.run('PRAGMA user_version = 23;');
await instance.run('COMMIT TRANSACTION;');
console.log('updateToSchemaVersion23: success!');
} catch (error) {
await instance.run('ROLLBACK');
throw error;
}
}
const SCHEMA_VERSIONS = [ const SCHEMA_VERSIONS = [
updateToSchemaVersion1, updateToSchemaVersion1,
updateToSchemaVersion2, updateToSchemaVersion2,
@ -1644,6 +1668,7 @@ const SCHEMA_VERSIONS = [
updateToSchemaVersion20, updateToSchemaVersion20,
updateToSchemaVersion21, updateToSchemaVersion21,
updateToSchemaVersion22, updateToSchemaVersion22,
updateToSchemaVersion23,
]; ];
async function updateSchema(instance: PromisifiedSQLDatabase) { async function updateSchema(instance: PromisifiedSQLDatabase) {
@ -1849,6 +1874,7 @@ async function getIdentityKeyById(id: string) {
async function bulkAddIdentityKeys(array: Array<IdentityKeyType>) { async function bulkAddIdentityKeys(array: Array<IdentityKeyType>) {
return bulkAdd(IDENTITY_KEYS_TABLE, array); return bulkAdd(IDENTITY_KEYS_TABLE, array);
} }
bulkAddIdentityKeys.needsSerial = true;
async function removeIdentityKeyById(id: string) { async function removeIdentityKeyById(id: string) {
return removeById(IDENTITY_KEYS_TABLE, id); return removeById(IDENTITY_KEYS_TABLE, id);
} }
@ -1869,6 +1895,7 @@ async function getPreKeyById(id: number) {
async function bulkAddPreKeys(array: Array<PreKeyType>) { async function bulkAddPreKeys(array: Array<PreKeyType>) {
return bulkAdd(PRE_KEYS_TABLE, array); return bulkAdd(PRE_KEYS_TABLE, array);
} }
bulkAddPreKeys.needsSerial = true;
async function removePreKeyById(id: number) { async function removePreKeyById(id: number) {
return removeById(PRE_KEYS_TABLE, id); return removeById(PRE_KEYS_TABLE, id);
} }
@ -1889,6 +1916,7 @@ async function getSignedPreKeyById(id: number) {
async function bulkAddSignedPreKeys(array: Array<SignedPreKeyType>) { async function bulkAddSignedPreKeys(array: Array<SignedPreKeyType>) {
return bulkAdd(SIGNED_PRE_KEYS_TABLE, array); return bulkAdd(SIGNED_PRE_KEYS_TABLE, array);
} }
bulkAddSignedPreKeys.needsSerial = true;
async function removeSignedPreKeyById(id: number) { async function removeSignedPreKeyById(id: number) {
return removeById(SIGNED_PRE_KEYS_TABLE, id); return removeById(SIGNED_PRE_KEYS_TABLE, id);
} }
@ -1918,6 +1946,7 @@ async function getAllItems() {
async function bulkAddItems(array: Array<ItemType>) { async function bulkAddItems(array: Array<ItemType>) {
return bulkAdd(ITEMS_TABLE, array); return bulkAdd(ITEMS_TABLE, array);
} }
bulkAddItems.needsSerial = true;
async function removeItemById(id: string) { async function removeItemById(id: string) {
return removeById(ITEMS_TABLE, id); return removeById(ITEMS_TABLE, id);
} }
@ -1990,6 +2019,7 @@ async function getSessionsById(conversationId: string) {
async function bulkAddSessions(array: Array<SessionType>) { async function bulkAddSessions(array: Array<SessionType>) {
return bulkAdd(SESSIONS_TABLE, array); return bulkAdd(SESSIONS_TABLE, array);
} }
bulkAddSessions.needsSerial = true;
async function removeSessionById(id: string) { async function removeSessionById(id: string) {
return removeById(SESSIONS_TABLE, id); return removeById(SESSIONS_TABLE, id);
} }
@ -2043,6 +2073,7 @@ async function bulkAdd(table: string, array: Array<any>) {
throw error; throw error;
} }
} }
bulkAdd.needsSerial = true;
async function getById(table: string, id: string | number) { async function getById(table: string, id: string | number) {
const db = getInstance(); const db = getInstance();
@ -2452,7 +2483,10 @@ async function getMessageCount(conversationId?: string) {
async function saveMessage( async function saveMessage(
data: MessageType, data: MessageType,
{ forceSave }: { forceSave?: boolean } = {} {
forceSave,
alreadyInTransaction,
}: { forceSave?: boolean; alreadyInTransaction?: boolean } = {}
) { ) {
const db = getInstance(); const db = getInstance();
const { const {
@ -2502,7 +2536,13 @@ async function saveMessage(
}; };
if (id && !forceSave) { if (id && !forceSave) {
await db.run( if (!alreadyInTransaction) {
await db.run('BEGIN TRANSACTION;');
}
try {
await Promise.all([
db.run(
`UPDATE messages SET `UPDATE messages SET
id = $id, id = $id,
json = $json, json = $json,
@ -2527,7 +2567,38 @@ async function saveMessage(
unread = $unread unread = $unread
WHERE id = $id;`, WHERE id = $id;`,
payload payload
),
db.run('DELETE FROM messages_fts WHERE id = $id;', {
$id: id,
}),
]);
if (body) {
await db.run(
`INSERT INTO messages_fts(
id,
body
) VALUES (
$id,
$body
); );
`,
{
$id: id,
$body: body,
}
);
}
if (!alreadyInTransaction) {
await db.run('COMMIT TRANSACTION;');
}
} catch (error) {
if (!alreadyInTransaction) {
await db.run('ROLLBACK;');
}
throw error;
}
return id; return id;
} }
@ -2537,7 +2608,13 @@ async function saveMessage(
id: id || generateUUID(), id: id || generateUUID(),
}; };
await db.run( if (!alreadyInTransaction) {
await db.run('BEGIN TRANSACTION;');
}
try {
await Promise.all([
db.run(
`INSERT INTO messages ( `INSERT INTO messages (
id, id,
json, json,
@ -2588,10 +2665,36 @@ async function saveMessage(
$id: toCreate.id, $id: toCreate.id,
$json: objectToJSON(toCreate), $json: objectToJSON(toCreate),
} }
),
db.run(
`INSERT INTO messages_fts(
id,
body
) VALUES (
$id,
$body
); );
`,
{
$id: id,
$body: body,
}
),
]);
if (!alreadyInTransaction) {
await db.run('COMMIT TRANSACTION;');
}
} catch (error) {
if (!alreadyInTransaction) {
await db.run('ROLLBACK;');
}
throw error;
}
return toCreate.id; return toCreate.id;
} }
saveMessage.needsSerial = true;
async function saveMessages( async function saveMessages(
arrayOfMessages: Array<MessageType>, arrayOfMessages: Array<MessageType>,
@ -2603,7 +2706,7 @@ async function saveMessages(
try { try {
await Promise.all([ await Promise.all([
...map(arrayOfMessages, async message => ...map(arrayOfMessages, async message =>
saveMessage(message, { forceSave }) saveMessage(message, { forceSave, alreadyInTransaction: true })
), ),
]); ]);
@ -2617,16 +2720,49 @@ saveMessages.needsSerial = true;
async function removeMessage(id: string) { async function removeMessage(id: string) {
const db = getInstance(); const db = getInstance();
await db.run('DELETE FROM messages WHERE id = $id;', { $id: id }); await db.run('BEGIN TRANSACTION;');
try {
await Promise.all([
db.run('DELETE FROM messages WHERE id = $id;', { $id: id }),
db.run('DELETE FROM messages_fts WHERE id = $id;', { $id: id }),
]);
await db.run('COMMIT TRANSACTION;');
} catch (error) {
await db.run('ROLLBACK;');
throw error;
} }
}
removeMessage.needsSerial = true;
async function removeMessages(ids: Array<string>) { async function removeMessages(ids: Array<string>) {
const db = getInstance(); const db = getInstance();
await db.run( await db.run('BEGIN TRANSACTION;');
`DELETE FROM messages WHERE id IN ( ${ids.map(() => '?').join(', ')} );`,
try {
await Promise.all([
db.run(
`DELETE FROM messages WHERE id IN ( ${ids
.map(() => '?')
.join(', ')} );`,
ids ids
); ),
db.run(
`DELETE FROM messages_fts WHERE id IN ( ${ids
.map(() => '?')
.join(', ')} );`,
ids
),
]);
await db.run('COMMIT TRANSACTION;');
} catch (error) {
await db.run('ROLLBACK;');
throw error;
} }
}
removeMessages.needsSerial = true;
async function getMessageById(id: string) { async function getMessageById(id: string) {
const db = getInstance(); const db = getInstance();