Optimize conversation open performance

This commit is contained in:
Fedor Indutny 2023-01-27 09:47:24 -08:00 committed by GitHub
parent 6a80b4b837
commit 67b108c718
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 26 deletions

View file

@ -2653,7 +2653,7 @@ function getOldestMessageForConversation(
const row = db const row = db
.prepare<Query>( .prepare<Query>(
` `
SELECT * FROM messages WHERE SELECT received_at, sent_at, id FROM messages WHERE
conversationId = $conversationId AND conversationId = $conversationId AND
isStory IS 0 AND isStory IS 0 AND
(${_storyIdPredicate(storyId, includeStoryReplies)}) (${_storyIdPredicate(storyId, includeStoryReplies)})
@ -2686,7 +2686,7 @@ function getNewestMessageForConversation(
const row = db const row = db
.prepare<Query>( .prepare<Query>(
` `
SELECT * FROM messages WHERE SELECT received_at, sent_at, id FROM messages WHERE
conversationId = $conversationId AND conversationId = $conversationId AND
isStory IS 0 AND isStory IS 0 AND
(${_storyIdPredicate(storyId, includeStoryReplies)}) (${_storyIdPredicate(storyId, includeStoryReplies)})
@ -2750,35 +2750,29 @@ function getLastConversationPreview({
}): MessageType | undefined { }): MessageType | undefined {
type Row = Readonly<{ type Row = Readonly<{
json: string; json: string;
received_at: number;
sent_at: number;
}>; }>;
const db = getInstance(); const db = getInstance();
const queryTemplate = (extraClause: string): string => { const index = includeStoryReplies
return ` ? 'messages_preview'
SELECT json, received_at, sent_at FROM messages : 'messages_preview_without_story';
INDEXED BY messages_preview
WHERE
conversationId IS $conversationId AND
shouldAffectPreview IS 1 AND
isGroupLeaveEventFromOther IS 0 AND
${includeStoryReplies ? '' : 'storyId IS NULL AND'}
${extraClause}
ORDER BY received_at DESC, sent_at DESC
LIMIT 1
`;
};
const row: Row | undefined = prepare( const row: Row | undefined = prepare(
db, db,
` `
SELECT * FROM (${queryTemplate('expiresAt IS NULL')}) SELECT json FROM (
UNION ALL SELECT json, expiresAt FROM messages
SELECT * FROM (${queryTemplate('expiresAt > $now')}) INDEXED BY ${index}
WHERE
conversationId IS $conversationId AND
shouldAffectPreview IS 1 AND
isGroupLeaveEventFromOther IS 0
${includeStoryReplies ? '' : 'AND storyId IS NULL'}
ORDER BY received_at DESC, sent_at DESC ORDER BY received_at DESC, sent_at DESC
LIMIT 1; )
WHERE likely(expiresAt > $now)
LIMIT 1
` `
).get({ ).get({
conversationId, conversationId,
@ -2824,7 +2818,7 @@ async function getLastConversationMessage({
const row = db const row = db
.prepare<Query>( .prepare<Query>(
` `
SELECT * FROM messages WHERE SELECT json FROM messages WHERE
conversationId = $conversationId conversationId = $conversationId
ORDER BY received_at DESC, sent_at DESC ORDER BY received_at DESC, sent_at DESC
LIMIT 1; LIMIT 1;
@ -2855,7 +2849,7 @@ function getOldestUnseenMessageForConversation(
const row = db const row = db
.prepare<Query>( .prepare<Query>(
` `
SELECT * FROM messages WHERE SELECT received_at, sent_at, id FROM messages WHERE
conversationId = $conversationId AND conversationId = $conversationId AND
seenStatus = ${SeenStatus.Unseen} AND seenStatus = ${SeenStatus.Unseen} AND
isStory IS 0 AND isStory IS 0 AND
@ -3130,7 +3124,6 @@ async function getExpiredMessages(): Promise<Array<MessageType>> {
.prepare<Query>( .prepare<Query>(
` `
SELECT json FROM messages WHERE SELECT json FROM messages WHERE
expiresAt IS NOT NULL AND
expiresAt <= $now expiresAt <= $now
ORDER BY expiresAt ASC; ORDER BY expiresAt ASC;
` `
@ -3181,6 +3174,10 @@ async function getSoonestMessageExpiry(): Promise<undefined | number> {
.pluck(true) .pluck(true)
.get(); .get();
if (result != null && result >= Number.MAX_SAFE_INTEGER) {
return undefined;
}
return result || undefined; return result || undefined;
} }

View file

@ -0,0 +1,59 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Database } from '@signalapp/better-sqlite3';
import type { LoggerType } from '../../types/Logging';
export default function updateToSchemaVersion76(
currentVersion: number,
db: Database,
logger: LoggerType
): void {
if (currentVersion >= 76) {
return;
}
db.transaction(() => {
db.exec(
`
-- Re-created below
DROP INDEX IF EXISTS message_expires_at;
DROP INDEX IF EXISTS messages_preview;
-- Create non-null expiresAt column
ALTER TABLE messages
DROP COLUMN expiresAt;
ALTER TABLE messages
ADD COLUMN
expiresAt INT
GENERATED ALWAYS
AS (ifnull(
expirationStartTimestamp + (expireTimer * 1000),
${Number.MAX_SAFE_INTEGER}
));
-- Re-create indexes
-- Note the "s" at the end of "messages"
CREATE INDEX messages_expires_at ON messages (
expiresAt
);
-- Note that expiresAt is intentionally dropped from the index since
-- expiresAt > $now is likely to be true so we just try selecting it
-- *after* ordering by received_at/sent_at.
CREATE INDEX messages_preview ON messages
(conversationId, shouldAffectPreview, isGroupLeaveEventFromOther,
received_at, sent_at);
CREATE INDEX messages_preview_without_story ON messages
(conversationId, shouldAffectPreview, isGroupLeaveEventFromOther,
received_at, sent_at) WHERE storyId IS NULL;
`
);
db.pragma('user_version = 76');
})();
logger.info('updateToSchemaVersion76: success!');
}

View file

@ -51,6 +51,7 @@ import updateToSchemaVersion72 from './72-optimize-call-id-message-lookup';
import updateToSchemaVersion73 from './73-remove-phone-number-discovery'; import updateToSchemaVersion73 from './73-remove-phone-number-discovery';
import updateToSchemaVersion74 from './74-optimize-convo-open'; import updateToSchemaVersion74 from './74-optimize-convo-open';
import updateToSchemaVersion75 from './75-signal-tokenizer'; import updateToSchemaVersion75 from './75-signal-tokenizer';
import updateToSchemaVersion76 from './76-optimize-convo-open-2';
function updateToSchemaVersion1( function updateToSchemaVersion1(
currentVersion: number, currentVersion: number,
@ -1971,6 +1972,7 @@ export const SCHEMA_VERSIONS = [
updateToSchemaVersion73, updateToSchemaVersion73,
updateToSchemaVersion74, updateToSchemaVersion74,
updateToSchemaVersion75, updateToSchemaVersion75,
updateToSchemaVersion76,
]; ];
export function updateSchema(db: Database, logger: LoggerType): void { export function updateSchema(db: Database, logger: LoggerType): void {