cleanup >2 months old messages in opengroups

This commit is contained in:
Audric Ackermann 2022-04-14 10:56:34 +10:00
parent c6d66ea111
commit 323b7ec45c
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
3 changed files with 104 additions and 3 deletions

View File

@ -1469,9 +1469,11 @@ async function initializeSql({
// Clear any already deleted db entries on each app start.
vacuumDatabase(db);
cleanUpOldOpengroups();
const msgCount = getMessageCount();
const convoCount = getConversationCount();
console.info('total message count: ', msgCount);
console.info('total conversation count: ', convoCount);
} catch (error) {
@ -2212,6 +2214,26 @@ function getMessageBySenderAndSentAt({ source, sentAt }: { source: string; sentA
return map(rows, row => jsonToObject(row.json));
}
function getMessagesCountBySender({ source }: { source: string }) {
if (!source) {
throw new Error('source must be set');
}
const count = assertGlobalInstance()
.prepare(
`SELECT count(*) FROM ${MESSAGES_TABLE} WHERE
source = $source;`
)
.get({
source,
});
console.info('count messages of ', source, ' :', count);
if (!count) {
return 0;
}
return count['count(*)'] || 0;
}
function getMessageBySenderAndTimestamp({
source,
timestamp,
@ -3126,7 +3148,7 @@ function removeKnownAttachments(allAttachments: any) {
function getMessagesCountByConversation(
conversationId: string,
instance?: BetterSqlite3.Database | null
) {
): number {
const row = assertGlobalInstanceOrInstance(instance)
.prepare(`SELECT count(*) from ${MESSAGES_TABLE} WHERE conversationId = $conversationId;`)
.get({ conversationId });
@ -3334,6 +3356,81 @@ function removeV2OpenGroupRoom(conversationId: string) {
});
}
function cleanUpOldOpengroups() {
const v2Convos = getAllOpenGroupV2Conversations();
// For each opengroups, if it has more than 1000 messages, we remove all the messages older than 2 months.
// So this does not limit the size of opengroup history to 1000 messages but to 2 months.
// This is the only way we can cleanup conversations objects from users which just sent messages a while ago and with whom we never interacted.
// This is only for opengroups, and is because ALL the conversations are cached in the redux store. Having a very large number of conversations (unused) is deteriorating a lot the performance of the app.
// Another fix would be to not cache all the conversations in the redux store, but it ain't going to happen anytime soon as it would a pretty big change of the way we do things and would break a lot of the app.
const maxMessagePerOpengroupConvo = 1000;
// first remove very old messages for each opengroups
v2Convos.forEach(convo => {
const convoId = convo.id;
const messagesInConvo = getMessagesCountByConversation(convoId);
if (messagesInConvo >= maxMessagePerOpengroupConvo) {
const minute = 1000 * 60;
const twoMonths = minute * 60 * 24 * 60;
console.info(
`too many message: ${messagesInConvo} in convo: ${convoId}. Limit is ${maxMessagePerOpengroupConvo}`
);
assertGlobalInstance()
.prepare(
`
DELETE FROM ${MESSAGES_TABLE} WHERE serverTimestamp <= $serverTimestamp AND conversationId = $conversationId`
)
.run({ conversationId: convoId, serverTimestamp: Date.now() - twoMonths }); // delete messages older than twoMonths
const messagesInConvoAfter = getMessagesCountByConversation(convoId);
console.info(
`after leaning old history, we have ${messagesInConvoAfter} messages left in convo: ${convoId}`
);
const unreadCount = getUnreadCountByConversation(convoId);
const convoProps = getConversationById(convoId);
if (convoProps) {
convoProps.unreadCount = unreadCount;
updateConversation(convoProps);
}
}
});
// now, we might have a bunch of private conversation, without any interaction and no messages
// those are the conversation of the old members in the opengroups we just cleaned.
const allInactiveConvos = assertGlobalInstance()
.prepare(
`
SELECT id FROM ${CONVERSATIONS_TABLE} WHERE type = 'private' AND (active_at IS NULL OR active_at = 0)`
)
.all();
const ourNumber = getItemById('number_id');
if (!ourNumber || !ourNumber.value) {
return;
}
const ourPubkey = ourNumber.value.split('.')[0];
const allInactiveAndWithoutMessagesConvo = allInactiveConvos
.map(c => c.id as string)
.filter(convoId => {
return convoId !== ourPubkey && getMessagesCountBySender({ source: convoId }) === 0
? true
: false;
});
if (allInactiveAndWithoutMessagesConvo.length) {
allInactiveAndWithoutMessagesConvo.forEach(convoId => {
console.info(`${convoId} is not active and has 0 message in the history. Removing it`);
assertGlobalInstance()
.prepare(`DELETE FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`)
.run({ id: convoId });
});
}
}
// tslint:disable: binary-expression-operand-order
// tslint:disable: insecure-random
@ -3524,6 +3621,7 @@ export const sqlNode = {
getUnreadByConversation,
getUnreadCountByConversation,
getMessageCountByType,
getMessageBySenderAndSentAt,
filterAlreadyFetchedOpengroupMessage,
getMessageBySenderAndTimestamp,

View File

@ -42,7 +42,6 @@ export function initializeSqlChannel() {
}
});
console.warn('********* registering get-user-data-path');
ipcMain.handle('get-user-data-path', () => {
return app.getPath('userData');
});

View File

@ -374,6 +374,10 @@ export async function handleMessageJob(
conversation.updateLastMessage();
await conversation.commit();
if (conversation.id !== sendingDeviceConversation.id) {
await sendingDeviceConversation.commit();
}
void queueAttachmentDownloads(messageModel, conversation);
// Check if we need to update any profile names
// the only profile we don't update with what is coming here is ours,