Reduce number of SQL queries during conversation update
This commit is contained in:
parent
765b3eddc4
commit
962515031d
5 changed files with 90 additions and 91 deletions
|
@ -713,9 +713,7 @@ export async function startApp(): Promise<void> {
|
||||||
const conversation = window.ConversationController.get(selectedId);
|
const conversation = window.ConversationController.get(selectedId);
|
||||||
assert(conversation, "Conversation wasn't found");
|
assert(conversation, "Conversation wasn't found");
|
||||||
|
|
||||||
conversation.queueJob('maybeSetPendingUniversalTimer', () =>
|
await conversation.updateLastMessage();
|
||||||
conversation.maybeSetPendingUniversalTimer()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1061,11 +1061,6 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
// On successful fetch - mark contact as registered.
|
// On successful fetch - mark contact as registered.
|
||||||
this.setRegistered();
|
this.setRegistered();
|
||||||
|
|
||||||
// If we couldn't apply universal timer before - try it again.
|
|
||||||
this.queueJob('maybeSetPendingUniversalTimer', async () =>
|
|
||||||
this.maybeSetPendingUniversalTimer()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid(): boolean {
|
isValid(): boolean {
|
||||||
|
@ -2759,7 +2754,9 @@ export class ConversationModel extends window.Backbone
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
async maybeSetPendingUniversalTimer(): Promise<void> {
|
async maybeSetPendingUniversalTimer(
|
||||||
|
hasUserInitiatedMessages: boolean
|
||||||
|
): Promise<void> {
|
||||||
if (!isDirectConversation(this.attributes)) {
|
if (!isDirectConversation(this.attributes)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2768,7 +2765,8 @@ export class ConversationModel extends window.Backbone
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await window.Signal.Data.hasUserInitiatedMessages(this.get('id'))) {
|
if (hasUserInitiatedMessages) {
|
||||||
|
await this.maybeApplyUniversalTimer(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2787,7 +2785,7 @@ export class ConversationModel extends window.Backbone
|
||||||
this.set('pendingUniversalTimer', notificationId);
|
this.set('pendingUniversalTimer', notificationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async maybeApplyUniversalTimer(): Promise<void> {
|
async maybeApplyUniversalTimer(forceRemove: boolean): Promise<void> {
|
||||||
const notificationId = this.get('pendingUniversalTimer');
|
const notificationId = this.get('pendingUniversalTimer');
|
||||||
if (!notificationId) {
|
if (!notificationId) {
|
||||||
return;
|
return;
|
||||||
|
@ -2801,7 +2799,7 @@ export class ConversationModel extends window.Backbone
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.get('expireTimer')) {
|
if (this.get('expireTimer') || forceRemove) {
|
||||||
this.set('pendingUniversalTimer', undefined);
|
this.set('pendingUniversalTimer', undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3405,7 +3403,7 @@ export class ConversationModel extends window.Backbone
|
||||||
timestamp
|
timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.maybeApplyUniversalTimer();
|
await this.maybeApplyUniversalTimer(false);
|
||||||
|
|
||||||
const expireTimer = this.get('expireTimer');
|
const expireTimer = this.get('expireTimer');
|
||||||
|
|
||||||
|
@ -3604,7 +3602,7 @@ export class ConversationModel extends window.Backbone
|
||||||
this.queueJob('sendMessage', async () => {
|
this.queueJob('sendMessage', async () => {
|
||||||
const now = timestamp || Date.now();
|
const now = timestamp || Date.now();
|
||||||
|
|
||||||
await this.maybeApplyUniversalTimer();
|
await this.maybeApplyUniversalTimer(false);
|
||||||
|
|
||||||
const expireTimer = this.get('expireTimer');
|
const expireTimer = this.get('expireTimer');
|
||||||
|
|
||||||
|
@ -3822,28 +3820,25 @@ export class ConversationModel extends window.Backbone
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.queueJob('maybeSetPendingUniversalTimer', async () =>
|
|
||||||
this.maybeSetPendingUniversalTimer()
|
|
||||||
);
|
|
||||||
|
|
||||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||||
if (!ourConversationId) {
|
if (!ourConversationId) {
|
||||||
throw new Error('updateLastMessage: Failed to fetch ourConversationId');
|
throw new Error('updateLastMessage: Failed to fetch ourConversationId');
|
||||||
}
|
}
|
||||||
|
|
||||||
const conversationId = this.id;
|
const conversationId = this.id;
|
||||||
let [previewMessage, activityMessage] = await Promise.all([
|
|
||||||
window.Signal.Data.getLastConversationPreview({
|
const lastMessages = await window.Signal.Data.getLastConversationMessages({
|
||||||
conversationId,
|
conversationId,
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
Message: window.Whisper.Message,
|
Message: window.Whisper.Message,
|
||||||
}),
|
});
|
||||||
window.Signal.Data.getLastConversationActivity({
|
|
||||||
conversationId,
|
// This runs as a job to avoid race conditions
|
||||||
ourConversationId,
|
this.queueJob('maybeSetPendingUniversalTimer', async () =>
|
||||||
Message: window.Whisper.Message,
|
this.maybeSetPendingUniversalTimer(lastMessages.hasUserInitiatedMessages)
|
||||||
}),
|
);
|
||||||
]);
|
|
||||||
|
let { preview: previewMessage, activity: activityMessage } = lastMessages;
|
||||||
|
|
||||||
// Register the message with MessageController so that if it already exists
|
// Register the message with MessageController so that if it already exists
|
||||||
// in memory we use that data instead of the data from the db which may
|
// in memory we use that data instead of the data from the db which may
|
||||||
|
@ -4157,7 +4152,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
// This call actually removes universal timer notification and clears
|
// This call actually removes universal timer notification and clears
|
||||||
// the pending flags.
|
// the pending flags.
|
||||||
await this.maybeApplyUniversalTimer();
|
await this.maybeApplyUniversalTimer(true);
|
||||||
|
|
||||||
window.Signal.Data.updateConversation(this.attributes);
|
window.Signal.Data.updateConversation(this.attributes);
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ import {
|
||||||
IdentityKeyType,
|
IdentityKeyType,
|
||||||
ItemKeyType,
|
ItemKeyType,
|
||||||
ItemType,
|
ItemType,
|
||||||
|
LastConversationMessagesType,
|
||||||
MessageType,
|
MessageType,
|
||||||
MessageTypeUnhydrated,
|
MessageTypeUnhydrated,
|
||||||
PreKeyType,
|
PreKeyType,
|
||||||
|
@ -190,7 +191,6 @@ const dataInterface: ClientInterface = {
|
||||||
searchMessagesInConversation,
|
searchMessagesInConversation,
|
||||||
|
|
||||||
getMessageCount,
|
getMessageCount,
|
||||||
hasUserInitiatedMessages,
|
|
||||||
saveMessage,
|
saveMessage,
|
||||||
saveMessages,
|
saveMessages,
|
||||||
removeMessage,
|
removeMessage,
|
||||||
|
@ -213,8 +213,7 @@ const dataInterface: ClientInterface = {
|
||||||
getTapToViewMessagesNeedingErase,
|
getTapToViewMessagesNeedingErase,
|
||||||
getOlderMessagesByConversation,
|
getOlderMessagesByConversation,
|
||||||
getNewerMessagesByConversation,
|
getNewerMessagesByConversation,
|
||||||
getLastConversationActivity,
|
getLastConversationMessages,
|
||||||
getLastConversationPreview,
|
|
||||||
getMessageMetricsForConversation,
|
getMessageMetricsForConversation,
|
||||||
hasGroupCallHistoryMessage,
|
hasGroupCallHistoryMessage,
|
||||||
migrateConversationMessages,
|
migrateConversationMessages,
|
||||||
|
@ -1063,10 +1062,6 @@ async function getMessageCount(conversationId?: string) {
|
||||||
return channels.getMessageCount(conversationId);
|
return channels.getMessageCount(conversationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hasUserInitiatedMessages(conversationId: string) {
|
|
||||||
return channels.hasUserInitiatedMessages(conversationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveMessage(
|
async function saveMessage(
|
||||||
data: MessageType,
|
data: MessageType,
|
||||||
options?: { forceSave?: boolean }
|
options?: { forceSave?: boolean }
|
||||||
|
@ -1267,7 +1262,7 @@ async function getNewerMessagesByConversation(
|
||||||
|
|
||||||
return new MessageCollection(handleMessageJSON(messages));
|
return new MessageCollection(handleMessageJSON(messages));
|
||||||
}
|
}
|
||||||
async function getLastConversationActivity({
|
async function getLastConversationMessages({
|
||||||
conversationId,
|
conversationId,
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
Message,
|
Message,
|
||||||
|
@ -1275,33 +1270,21 @@ async function getLastConversationActivity({
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
ourConversationId: string;
|
ourConversationId: string;
|
||||||
Message: typeof MessageModel;
|
Message: typeof MessageModel;
|
||||||
}): Promise<MessageModel | undefined> {
|
}): Promise<LastConversationMessagesType> {
|
||||||
const result = await channels.getLastConversationActivity({
|
const {
|
||||||
|
preview,
|
||||||
|
activity,
|
||||||
|
hasUserInitiatedMessages,
|
||||||
|
} = await channels.getLastConversationMessages({
|
||||||
conversationId,
|
conversationId,
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
});
|
});
|
||||||
if (result) {
|
|
||||||
return new Message(result);
|
return {
|
||||||
}
|
preview: preview ? new Message(preview) : undefined,
|
||||||
return undefined;
|
activity: activity ? new Message(activity) : undefined,
|
||||||
}
|
hasUserInitiatedMessages,
|
||||||
async function getLastConversationPreview({
|
};
|
||||||
conversationId,
|
|
||||||
ourConversationId,
|
|
||||||
Message,
|
|
||||||
}: {
|
|
||||||
conversationId: string;
|
|
||||||
ourConversationId: string;
|
|
||||||
Message: typeof MessageModel;
|
|
||||||
}): Promise<MessageModel | undefined> {
|
|
||||||
const result = await channels.getLastConversationPreview({
|
|
||||||
conversationId,
|
|
||||||
ourConversationId,
|
|
||||||
});
|
|
||||||
if (result) {
|
|
||||||
return new Message(result);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
async function getMessageMetricsForConversation(conversationId: string) {
|
async function getMessageMetricsForConversation(conversationId: string) {
|
||||||
const result = await channels.getMessageMetricsForConversation(
|
const result = await channels.getMessageMetricsForConversation(
|
||||||
|
|
|
@ -201,6 +201,18 @@ export type UnprocessedUpdateType = {
|
||||||
decrypted?: string;
|
decrypted?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LastConversationMessagesServerType = {
|
||||||
|
activity?: MessageType;
|
||||||
|
preview?: MessageType;
|
||||||
|
hasUserInitiatedMessages: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LastConversationMessagesType = {
|
||||||
|
activity?: MessageModel;
|
||||||
|
preview?: MessageModel;
|
||||||
|
hasUserInitiatedMessages: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type DataInterface = {
|
export type DataInterface = {
|
||||||
close: () => Promise<void>;
|
close: () => Promise<void>;
|
||||||
removeDB: () => Promise<void>;
|
removeDB: () => Promise<void>;
|
||||||
|
@ -302,7 +314,6 @@ export type DataInterface = {
|
||||||
options?: { forceSave?: boolean }
|
options?: { forceSave?: boolean }
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
getMessageCount: (conversationId?: string) => Promise<number>;
|
getMessageCount: (conversationId?: string) => Promise<number>;
|
||||||
hasUserInitiatedMessages: (conversationId: string) => Promise<boolean>;
|
|
||||||
getAllMessageIds: () => Promise<Array<string>>;
|
getAllMessageIds: () => Promise<Array<string>>;
|
||||||
getMessageMetricsForConversation: (
|
getMessageMetricsForConversation: (
|
||||||
conversationId: string
|
conversationId: string
|
||||||
|
@ -476,14 +487,10 @@ export type ServerInterface = DataInterface & {
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
options?: { limit?: number; receivedAt?: number; sentAt?: number }
|
options?: { limit?: number; receivedAt?: number; sentAt?: number }
|
||||||
) => Promise<Array<MessageTypeUnhydrated>>;
|
) => Promise<Array<MessageTypeUnhydrated>>;
|
||||||
getLastConversationActivity: (options: {
|
getLastConversationMessages: (options: {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
ourConversationId: string;
|
ourConversationId: string;
|
||||||
}) => Promise<MessageType | undefined>;
|
}) => Promise<LastConversationMessagesServerType>;
|
||||||
getLastConversationPreview: (options: {
|
|
||||||
conversationId: string;
|
|
||||||
ourConversationId: string;
|
|
||||||
}) => Promise<MessageType | undefined>;
|
|
||||||
getTapToViewMessagesNeedingErase: () => Promise<Array<MessageType>>;
|
getTapToViewMessagesNeedingErase: () => Promise<Array<MessageType>>;
|
||||||
removeConversation: (id: Array<string> | string) => Promise<void>;
|
removeConversation: (id: Array<string> | string) => Promise<void>;
|
||||||
removeMessage: (id: string) => Promise<void>;
|
removeMessage: (id: string) => Promise<void>;
|
||||||
|
@ -576,16 +583,11 @@ export type ClientInterface = DataInterface & {
|
||||||
MessageCollection: typeof MessageModelCollectionType;
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
}
|
}
|
||||||
) => Promise<MessageModelCollectionType>;
|
) => Promise<MessageModelCollectionType>;
|
||||||
getLastConversationActivity: (options: {
|
getLastConversationMessages: (options: {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
ourConversationId: string;
|
ourConversationId: string;
|
||||||
Message: typeof MessageModel;
|
Message: typeof MessageModel;
|
||||||
}) => Promise<MessageModel | undefined>;
|
}) => Promise<LastConversationMessagesType>;
|
||||||
getLastConversationPreview: (options: {
|
|
||||||
conversationId: string;
|
|
||||||
ourConversationId: string;
|
|
||||||
Message: typeof MessageModel;
|
|
||||||
}) => Promise<MessageModel | undefined>;
|
|
||||||
getTapToViewMessagesNeedingErase: (options: {
|
getTapToViewMessagesNeedingErase: (options: {
|
||||||
MessageCollection: typeof MessageModelCollectionType;
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
}) => Promise<MessageModelCollectionType>;
|
}) => Promise<MessageModelCollectionType>;
|
||||||
|
|
|
@ -52,6 +52,7 @@ import {
|
||||||
IdentityKeyType,
|
IdentityKeyType,
|
||||||
ItemKeyType,
|
ItemKeyType,
|
||||||
ItemType,
|
ItemType,
|
||||||
|
LastConversationMessagesServerType,
|
||||||
MessageMetricsType,
|
MessageMetricsType,
|
||||||
MessageType,
|
MessageType,
|
||||||
MessageTypeUnhydrated,
|
MessageTypeUnhydrated,
|
||||||
|
@ -179,7 +180,6 @@ const dataInterface: ServerInterface = {
|
||||||
searchMessagesInConversation,
|
searchMessagesInConversation,
|
||||||
|
|
||||||
getMessageCount,
|
getMessageCount,
|
||||||
hasUserInitiatedMessages,
|
|
||||||
saveMessage,
|
saveMessage,
|
||||||
saveMessages,
|
saveMessages,
|
||||||
removeMessage,
|
removeMessage,
|
||||||
|
@ -203,8 +203,7 @@ const dataInterface: ServerInterface = {
|
||||||
getOlderMessagesByConversation,
|
getOlderMessagesByConversation,
|
||||||
getNewerMessagesByConversation,
|
getNewerMessagesByConversation,
|
||||||
getMessageMetricsForConversation,
|
getMessageMetricsForConversation,
|
||||||
getLastConversationActivity,
|
getLastConversationMessages,
|
||||||
getLastConversationPreview,
|
|
||||||
hasGroupCallHistoryMessage,
|
hasGroupCallHistoryMessage,
|
||||||
migrateConversationMessages,
|
migrateConversationMessages,
|
||||||
|
|
||||||
|
@ -3513,10 +3512,7 @@ async function getMessageCount(conversationId?: string): Promise<number> {
|
||||||
return row['count(*)'];
|
return row['count(*)'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called only for private conversations
|
function hasUserInitiatedMessages(conversationId: string): boolean {
|
||||||
async function hasUserInitiatedMessages(
|
|
||||||
conversationId: string
|
|
||||||
): Promise<boolean> {
|
|
||||||
const db = getInstance();
|
const db = getInstance();
|
||||||
|
|
||||||
// We apply the limit in the sub-query so that `json_extract` wouldn't run
|
// We apply the limit in the sub-query so that `json_extract` wouldn't run
|
||||||
|
@ -3538,10 +3534,10 @@ async function hasUserInitiatedMessages(
|
||||||
'keychange',
|
'keychange',
|
||||||
'group-v1-migration',
|
'group-v1-migration',
|
||||||
'universal-timer-notification',
|
'universal-timer-notification',
|
||||||
'change-number-notification'
|
'change-number-notification',
|
||||||
|
'group-v2-change'
|
||||||
)
|
)
|
||||||
) AND
|
)
|
||||||
json_extract(json, '$.expirationTimerUpdate') IS NULL
|
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
);
|
);
|
||||||
`
|
`
|
||||||
|
@ -4218,13 +4214,13 @@ function getNewestMessageForConversation(
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLastConversationActivity({
|
function getLastConversationActivity({
|
||||||
conversationId,
|
conversationId,
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
}: {
|
}: {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
ourConversationId: string;
|
ourConversationId: string;
|
||||||
}): Promise<MessageType | undefined> {
|
}): MessageType | undefined {
|
||||||
const db = getInstance();
|
const db = getInstance();
|
||||||
const row = prepare(
|
const row = prepare(
|
||||||
db,
|
db,
|
||||||
|
@ -4270,13 +4266,13 @@ async function getLastConversationActivity({
|
||||||
|
|
||||||
return jsonToObject(row.json);
|
return jsonToObject(row.json);
|
||||||
}
|
}
|
||||||
async function getLastConversationPreview({
|
function getLastConversationPreview({
|
||||||
conversationId,
|
conversationId,
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
}: {
|
}: {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
ourConversationId: string;
|
ourConversationId: string;
|
||||||
}): Promise<MessageType | undefined> {
|
}): MessageType | undefined {
|
||||||
const db = getInstance();
|
const db = getInstance();
|
||||||
const row = prepare(
|
const row = prepare(
|
||||||
db,
|
db,
|
||||||
|
@ -4317,6 +4313,31 @@ async function getLastConversationPreview({
|
||||||
|
|
||||||
return jsonToObject(row.json);
|
return jsonToObject(row.json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getLastConversationMessages({
|
||||||
|
conversationId,
|
||||||
|
ourConversationId,
|
||||||
|
}: {
|
||||||
|
conversationId: string;
|
||||||
|
ourConversationId: string;
|
||||||
|
}): Promise<LastConversationMessagesServerType> {
|
||||||
|
const db = getInstance();
|
||||||
|
|
||||||
|
return db.transaction(() => {
|
||||||
|
return {
|
||||||
|
activity: getLastConversationActivity({
|
||||||
|
conversationId,
|
||||||
|
ourConversationId,
|
||||||
|
}),
|
||||||
|
preview: getLastConversationPreview({
|
||||||
|
conversationId,
|
||||||
|
ourConversationId,
|
||||||
|
}),
|
||||||
|
hasUserInitiatedMessages: hasUserInitiatedMessages(conversationId),
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
function getOldestUnreadMessageForConversation(
|
function getOldestUnreadMessageForConversation(
|
||||||
conversationId: string
|
conversationId: string
|
||||||
): MessageMetricsType | undefined {
|
): MessageMetricsType | undefined {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue