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 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() {
singleQueue = new Queue({ concurrency: 1, timeout: 1000 * 60 * 2 });
singleQueue = new Queue({ concurrency: 1 });
return singleQueue;
}
function makeNewMultipleQueue() {
multipleQueue = new Queue({ concurrency: 10, timeout: 1000 * 60 * 2 });
multipleQueue = new Queue({ concurrency: 10 });
return multipleQueue;
}

View file

@ -54,12 +54,12 @@ export const ConfirmationDialog = React.memo(
const handleAction = React.useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
onClose();
if (e.currentTarget.dataset.action) {
const actionIndex = parseInt(e.currentTarget.dataset.action, 10);
const { action } = actions[actionIndex];
action();
}
onClose();
},
[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 = [
updateToSchemaVersion1,
updateToSchemaVersion2,
@ -1644,6 +1668,7 @@ const SCHEMA_VERSIONS = [
updateToSchemaVersion20,
updateToSchemaVersion21,
updateToSchemaVersion22,
updateToSchemaVersion23,
];
async function updateSchema(instance: PromisifiedSQLDatabase) {
@ -1849,6 +1874,7 @@ async function getIdentityKeyById(id: string) {
async function bulkAddIdentityKeys(array: Array<IdentityKeyType>) {
return bulkAdd(IDENTITY_KEYS_TABLE, array);
}
bulkAddIdentityKeys.needsSerial = true;
async function removeIdentityKeyById(id: string) {
return removeById(IDENTITY_KEYS_TABLE, id);
}
@ -1869,6 +1895,7 @@ async function getPreKeyById(id: number) {
async function bulkAddPreKeys(array: Array<PreKeyType>) {
return bulkAdd(PRE_KEYS_TABLE, array);
}
bulkAddPreKeys.needsSerial = true;
async function removePreKeyById(id: number) {
return removeById(PRE_KEYS_TABLE, id);
}
@ -1889,6 +1916,7 @@ async function getSignedPreKeyById(id: number) {
async function bulkAddSignedPreKeys(array: Array<SignedPreKeyType>) {
return bulkAdd(SIGNED_PRE_KEYS_TABLE, array);
}
bulkAddSignedPreKeys.needsSerial = true;
async function removeSignedPreKeyById(id: number) {
return removeById(SIGNED_PRE_KEYS_TABLE, id);
}
@ -1918,6 +1946,7 @@ async function getAllItems() {
async function bulkAddItems(array: Array<ItemType>) {
return bulkAdd(ITEMS_TABLE, array);
}
bulkAddItems.needsSerial = true;
async function removeItemById(id: string) {
return removeById(ITEMS_TABLE, id);
}
@ -1990,6 +2019,7 @@ async function getSessionsById(conversationId: string) {
async function bulkAddSessions(array: Array<SessionType>) {
return bulkAdd(SESSIONS_TABLE, array);
}
bulkAddSessions.needsSerial = true;
async function removeSessionById(id: string) {
return removeById(SESSIONS_TABLE, id);
}
@ -2043,6 +2073,7 @@ async function bulkAdd(table: string, array: Array<any>) {
throw error;
}
}
bulkAdd.needsSerial = true;
async function getById(table: string, id: string | number) {
const db = getInstance();
@ -2452,7 +2483,10 @@ async function getMessageCount(conversationId?: string) {
async function saveMessage(
data: MessageType,
{ forceSave }: { forceSave?: boolean } = {}
{
forceSave,
alreadyInTransaction,
}: { forceSave?: boolean; alreadyInTransaction?: boolean } = {}
) {
const db = getInstance();
const {
@ -2502,7 +2536,13 @@ async function saveMessage(
};
if (id && !forceSave) {
await db.run(
if (!alreadyInTransaction) {
await db.run('BEGIN TRANSACTION;');
}
try {
await Promise.all([
db.run(
`UPDATE messages SET
id = $id,
json = $json,
@ -2527,7 +2567,38 @@ async function saveMessage(
unread = $unread
WHERE id = $id;`,
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;
}
@ -2537,7 +2608,13 @@ async function saveMessage(
id: id || generateUUID(),
};
await db.run(
if (!alreadyInTransaction) {
await db.run('BEGIN TRANSACTION;');
}
try {
await Promise.all([
db.run(
`INSERT INTO messages (
id,
json,
@ -2588,10 +2665,36 @@ async function saveMessage(
$id: toCreate.id,
$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;
}
saveMessage.needsSerial = true;
async function saveMessages(
arrayOfMessages: Array<MessageType>,
@ -2603,7 +2706,7 @@ async function saveMessages(
try {
await Promise.all([
...map(arrayOfMessages, async message =>
saveMessage(message, { forceSave })
saveMessage(message, { forceSave, alreadyInTransaction: true })
),
]);
@ -2617,16 +2720,49 @@ saveMessages.needsSerial = true;
async function removeMessage(id: string) {
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>) {
const db = getInstance();
await db.run(
`DELETE FROM messages WHERE id IN ( ${ids.map(() => '?').join(', ')} );`,
await db.run('BEGIN TRANSACTION;');
try {
await Promise.all([
db.run(
`DELETE FROM messages WHERE id IN ( ${ids
.map(() => '?')
.join(', ')} );`,
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) {
const db = getInstance();