Add indices to improve messages_on_delete trigger

This commit is contained in:
trevor-signal 2024-06-28 20:47:05 -04:00 committed by GitHub
parent 189a8a0877
commit 7ef2a9155c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 198 additions and 110 deletions

View file

@ -0,0 +1,32 @@
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Database } from '@signalapp/better-sqlite3';
import type { LoggerType } from '../../types/Logging';
export const version = 1090;
export function updateToSchemaVersion1090(
currentVersion: number,
db: Database,
logger: LoggerType
): void {
if (currentVersion >= 1090) {
return;
}
db.transaction(() => {
db.exec(`
CREATE INDEX reactions_messageId
ON reactions (messageId);
CREATE INDEX storyReads_storyId
ON storyReads (storyId);
`);
})();
db.pragma('user_version = 1090');
logger.info('updateToSchemaVersion1090: success!');
}

View file

@ -83,10 +83,11 @@ import { updateToSchemaVersion1040 } from './1040-undownloaded-backed-up-media';
import { updateToSchemaVersion1050 } from './1050-group-send-endorsements'; import { updateToSchemaVersion1050 } from './1050-group-send-endorsements';
import { updateToSchemaVersion1060 } from './1060-addressable-messages-and-sync-tasks'; import { updateToSchemaVersion1060 } from './1060-addressable-messages-and-sync-tasks';
import { updateToSchemaVersion1070 } from './1070-attachment-backup'; import { updateToSchemaVersion1070 } from './1070-attachment-backup';
import { updateToSchemaVersion1080 } from './1080-nondisappearing-addressable';
import { import {
updateToSchemaVersion1080, updateToSchemaVersion1090,
version as MAX_VERSION, version as MAX_VERSION,
} from './1080-nondisappearing-addressable'; } from './1090-message-delete-indexes';
function updateToSchemaVersion1( function updateToSchemaVersion1(
currentVersion: number, currentVersion: number,
@ -2038,6 +2039,7 @@ export const SCHEMA_VERSIONS = [
updateToSchemaVersion1060, updateToSchemaVersion1060,
updateToSchemaVersion1070, updateToSchemaVersion1070,
updateToSchemaVersion1080, updateToSchemaVersion1080,
updateToSchemaVersion1090,
]; ];
export class DBVersionFromFutureError extends Error { export class DBVersionFromFutureError extends Error {

View file

@ -39,109 +39,108 @@ describe('SQL/updateToSchemaVersion1080', () => {
}); });
describe('Addressable Messages', () => { describe('Addressable Messages', () => {
describe('Storing of new attachment jobs', () => { it('returns only incoming/outgoing messages', () => {
it('returns only incoming/outgoing messages', () => { const conversationId = generateGuid();
const conversationId = generateGuid(); const otherConversationId = generateGuid();
const otherConversationId = generateGuid();
insertData(db, 'messages', [ insertData(db, 'messages', [
generateMessage({ generateMessage({
id: '1', id: '1',
conversationId, conversationId,
type: 'incoming', type: 'incoming',
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
}), }),
generateMessage({ generateMessage({
id: '2', id: '2',
conversationId, conversationId,
type: 'story', type: 'story',
received_at: 2, received_at: 2,
sent_at: 2, sent_at: 2,
timestamp: 2, timestamp: 2,
}), }),
generateMessage({ generateMessage({
id: '3', id: '3',
conversationId, conversationId,
type: 'outgoing', type: 'outgoing',
received_at: 3, received_at: 3,
sent_at: 3, sent_at: 3,
timestamp: 3, timestamp: 3,
}), }),
generateMessage({ generateMessage({
id: '4', id: '4',
conversationId, conversationId,
type: 'group-v1-migration', type: 'group-v1-migration',
received_at: 4, received_at: 4,
sent_at: 4, sent_at: 4,
timestamp: 4, timestamp: 4,
}), }),
generateMessage({ generateMessage({
id: '5', id: '5',
conversationId, conversationId,
type: 'group-v2-change', type: 'group-v2-change',
received_at: 5, received_at: 5,
sent_at: 5, sent_at: 5,
timestamp: 5, timestamp: 5,
}), }),
generateMessage({ generateMessage({
id: '6', id: '6',
conversationId, conversationId,
type: 'incoming', type: 'incoming',
received_at: 6, received_at: 6,
sent_at: 6, sent_at: 6,
timestamp: 6, timestamp: 6,
expireTimer: DurationInSeconds.fromMinutes(10), expireTimer: DurationInSeconds.fromMinutes(10),
}), }),
generateMessage({ generateMessage({
id: '7', id: '7',
conversationId, conversationId,
type: 'profile-change', type: 'profile-change',
received_at: 7, received_at: 7,
sent_at: 7, sent_at: 7,
timestamp: 7, timestamp: 7,
}), }),
generateMessage({ generateMessage({
id: '8', id: '8',
conversationId: otherConversationId, conversationId: otherConversationId,
type: 'incoming', type: 'incoming',
received_at: 8, received_at: 8,
sent_at: 8, sent_at: 8,
timestamp: 8, timestamp: 8,
}), }),
]); ]);
const messages = getMostRecentAddressableNondisappearingMessagesSync( const messages = getMostRecentAddressableNondisappearingMessagesSync(
db, db,
conversationId conversationId
); );
assert.lengthOf(messages, 2); assert.lengthOf(messages, 2);
assert.deepEqual(messages, [ assert.deepEqual(messages, [
{ {
id: '3', id: '3',
conversationId, conversationId,
type: 'outgoing', type: 'outgoing',
received_at: 3, received_at: 3,
sent_at: 3, sent_at: 3,
timestamp: 3, timestamp: 3,
}, },
{ {
id: '1', id: '1',
conversationId, conversationId,
type: 'incoming', type: 'incoming',
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
}, },
]); ]);
}); });
it('ensures that index is used for getMostRecentAddressableNondisappearingMessagesSync, with storyId', () => { it('ensures that index is used for getMostRecentAddressableNondisappearingMessagesSync, with storyId', () => {
const { detail } = db const { detail } = db
.prepare( .prepare(
` `
EXPLAIN QUERY PLAN EXPLAIN QUERY PLAN
SELECT json FROM messages SELECT json FROM messages
INDEXED BY messages_by_date_addressable_nondisappearing INDEXED BY messages_by_date_addressable_nondisappearing
@ -152,16 +151,15 @@ describe('SQL/updateToSchemaVersion1080', () => {
ORDER BY received_at DESC, sent_at DESC ORDER BY received_at DESC, sent_at DESC
LIMIT 5; LIMIT 5;
` `
) )
.get(); .get();
assert.notInclude(detail, 'B-TREE'); assert.notInclude(detail, 'B-TREE');
assert.notInclude(detail, 'SCAN'); assert.notInclude(detail, 'SCAN');
assert.include( assert.include(
detail, detail,
'SEARCH messages USING INDEX messages_by_date_addressable_nondisappearing (conversationId=? AND isAddressableMessage=?)' 'SEARCH messages USING INDEX messages_by_date_addressable_nondisappearing (conversationId=? AND isAddressableMessage=?)'
); );
});
}); });
}); });
}); });

View file

@ -0,0 +1,56 @@
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import type { Database } from '@signalapp/better-sqlite3';
import SQL from '@signalapp/better-sqlite3';
import { updateToVersion } from './helpers';
describe('SQL/updateToSchemaVersion1090', () => {
let db: Database;
beforeEach(() => {
db = new SQL(':memory:');
updateToVersion(db, 1090);
});
afterEach(() => {
db.close();
});
describe('Additional messages_on_delete indexes', () => {
it('uses index for selecting reactions by messageId', () => {
const details = db
.prepare(
`EXPLAIN QUERY PLAN
SELECT rowid FROM reactions
WHERE messageId = '123';
`
)
.all()
.map(step => step.detail)
.join(', ');
assert.strictEqual(
details,
'SEARCH reactions USING COVERING INDEX reactions_messageId (messageId=?)'
);
});
it('uses index for selecting storyReads by storyId', () => {
const details = db
.prepare(
`EXPLAIN QUERY PLAN
DELETE FROM storyReads WHERE storyId = '123';
`
)
.all()
.map(step => step.detail)
.join(', ');
assert.strictEqual(
details,
'SEARCH storyReads USING INDEX storyReads_storyId (storyId=?)'
);
});
});
});