Add some performance improvements (#7481)
* Add some performance improvements * fix tests
This commit is contained in:
parent
8bc1ec2dff
commit
ff601982b9
13 changed files with 82 additions and 9 deletions
|
|
@ -20,11 +20,14 @@ export async function setCurrentUserStatus(serverUrl: string, status: string) {
|
|||
throw new Error(`No current user for ${serverUrl}`);
|
||||
}
|
||||
|
||||
user.prepareStatus(status);
|
||||
await operator.batchRecords([user], 'setCurrentUserStatusOffline');
|
||||
if (user.status !== status) {
|
||||
user.prepareStatus(status);
|
||||
await operator.batchRecords([user], 'setCurrentUserStatus');
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
logError('Failed setCurrentUserStatusOffline', error);
|
||||
logError('Failed setCurrentUserStatus', error);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -378,7 +378,10 @@ export async function fetchPosts(serverUrl: string, channelId: string, page = 0,
|
|||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPosts');
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'fetchPosts');
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -344,12 +344,19 @@ export async function fetchStatusByIds(serverUrl: string, userIds: string[], fet
|
|||
return result;
|
||||
}, {});
|
||||
|
||||
const usersToBatch = [];
|
||||
for (const user of users) {
|
||||
const status = userStatuses[user.id];
|
||||
user.prepareStatus(status?.status || General.OFFLINE);
|
||||
const receivedStatus = userStatuses[user.id];
|
||||
const statusToSet = receivedStatus?.status || General.OFFLINE;
|
||||
if (statusToSet !== user.status) {
|
||||
user.prepareStatus(statusToSet);
|
||||
usersToBatch.push(user);
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(users, 'fetchStatusByIds');
|
||||
if (usersToBatch.length) {
|
||||
await operator.batchRecords(usersToBatch, 'fetchStatusByIds');
|
||||
}
|
||||
}
|
||||
|
||||
return {statuses};
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default class BaseDataOperator {
|
|||
* @param {(existing: Model, newElement: RawValue) => boolean} inputsArg.buildKeyRecordBy
|
||||
* @returns {Promise<{ProcessRecordResults}>}
|
||||
*/
|
||||
processRecords = async <T extends Model>({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs): Promise<ProcessRecordResults<T>> => {
|
||||
processRecords = async <T extends Model>({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName, shouldUpdate}: ProcessRecordsArgs): Promise<ProcessRecordResults<T>> => {
|
||||
const getRecords = async (rawValues: RawValue[]) => {
|
||||
// We will query a table where one of its fields can match a range of values. Hence, here we are extracting all those potential values.
|
||||
const columnValues: string[] = getRangeOfValues({fieldName, raws: rawValues});
|
||||
|
|
@ -92,6 +92,10 @@ export default class BaseDataOperator {
|
|||
|
||||
// We found a record in the database that matches this element; hence, we'll proceed for an UPDATE operation
|
||||
if (existingRecord) {
|
||||
if (shouldUpdate && !shouldUpdate(existingRecord, newElement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some raw value has an update_at field. We'll proceed to update only if the update_at value is different from the record's value in database
|
||||
const updateRecords = getValidRecordsForUpdate({
|
||||
tableName,
|
||||
|
|
@ -205,7 +209,7 @@ export default class BaseDataOperator {
|
|||
* @param {string} handleRecordsArgs.tableName
|
||||
* @returns {Promise<Model[]>}
|
||||
*/
|
||||
async handleRecords<T extends Model>({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true}: HandleRecordsArgs<T>, description: string): Promise<T[]> {
|
||||
async handleRecords<T extends Model>({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true, shouldUpdate}: HandleRecordsArgs<T>, description: string): Promise<T[]> {
|
||||
if (!createOrUpdateRawValues.length) {
|
||||
logWarning(
|
||||
`An empty "rawValues" array has been passed to the handleRecords method for tableName ${tableName}`,
|
||||
|
|
@ -219,6 +223,7 @@ export default class BaseDataOperator {
|
|||
tableName,
|
||||
buildKeyRecordBy,
|
||||
fieldName,
|
||||
shouldUpdate,
|
||||
});
|
||||
|
||||
let models: T[] = [];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type FileModel from '@typings/database/models/servers/file';
|
||||
|
||||
export const shouldUpdateFileRecord = (e: FileModel, n: FileInfo): boolean => {
|
||||
return Boolean(
|
||||
(n.post_id !== e.postId) ||
|
||||
(n.name !== e.name) ||
|
||||
(n.extension !== e.extension) ||
|
||||
(n.size !== e.size) ||
|
||||
((n.mime_type || '') !== e.mimeType) ||
|
||||
(n.width && n.width !== e.width) ||
|
||||
(n.height && n.height !== e.height) ||
|
||||
(n.mini_preview && n.mini_preview !== e.imageThumbnail) ||
|
||||
(n.localPath && n.localPath !== e.localPath),
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
|
||||
export const shouldUpdateThreadRecord = (e: ThreadModel, n: ThreadWithLastFetchedAt): boolean => {
|
||||
return (
|
||||
((n.last_reply_at != null) && n.last_reply_at !== e.lastReplyAt) ||
|
||||
((n.lastFetchedAt || 0) > e.lastFetchedAt) ||
|
||||
((n.last_viewed_at != null) && e.lastViewedAt !== n.last_viewed_at) ||
|
||||
(e.replyCount !== n.reply_count) ||
|
||||
((n.is_following != null) && e.isFollowing !== n.is_following) ||
|
||||
((n.unread_replies != null) && e.unreadReplies !== n.unread_replies) ||
|
||||
((n.unread_mentions != null) && e.unreadMentions !== n.unread_mentions)
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
export function shouldUpdateUserRecord(e: UserModel, n: UserProfile) {
|
||||
return Boolean(n.update_at > e.updateAt || (n.status && n.status !== e.status));
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import {Q} from '@nozbe/watermelondb';
|
|||
import {ActionType} from '@constants';
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {buildDraftKey} from '@database/operator/server_data_operator/comparators';
|
||||
import {shouldUpdateFileRecord} from '@database/operator/server_data_operator/comparators/files';
|
||||
import {
|
||||
transformDraftRecord,
|
||||
transformFileRecord,
|
||||
|
|
@ -234,6 +235,7 @@ const PostHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supercla
|
|||
deleteRawValues: pendingPostsToDelete,
|
||||
tableName,
|
||||
fieldName: 'id',
|
||||
shouldUpdate: (e: PostModel, n: Post) => n.update_at > e.updateAt,
|
||||
}));
|
||||
|
||||
const preparedPosts = (await this.prepareRecords({
|
||||
|
|
@ -309,6 +311,7 @@ const PostHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supercla
|
|||
tableName: FILE,
|
||||
fieldName: 'id',
|
||||
deleteRawValues: [],
|
||||
shouldUpdate: shouldUpdateFileRecord,
|
||||
}));
|
||||
|
||||
const postFiles = await this.prepareRecords({
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {shouldUpdateThreadRecord} from '@database/operator/server_data_operator/comparators/thread';
|
||||
import {transformThreadRecord, transformThreadParticipantRecord, transformThreadInTeamRecord, transformTeamThreadsSyncRecord} from '@database/operator/server_data_operator/transformers/thread';
|
||||
|
||||
import type ServerDataOperator from '..';
|
||||
|
|
@ -59,6 +60,7 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
|||
createOrUpdateRawValues: threads,
|
||||
tableName: 'Thread',
|
||||
prepareRecordsOnly: true,
|
||||
shouldUpdate: shouldUpdateThreadRecord,
|
||||
}, 'handleThreads(NEVER)');
|
||||
|
||||
// Should handle participants
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import {Q} from '@nozbe/watermelondb';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {shouldUpdateThreadRecord} from '@database/operator/server_data_operator/comparators/thread';
|
||||
import {
|
||||
transformThreadRecord,
|
||||
transformThreadParticipantRecord,
|
||||
|
|
@ -110,6 +111,7 @@ const ThreadHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superc
|
|||
prepareRecordsOnly: true,
|
||||
createOrUpdateRawValues: createOrUpdateThreads,
|
||||
tableName: THREAD,
|
||||
shouldUpdate: shouldUpdateThreadRecord,
|
||||
}, 'handleThreads(NEVER)');
|
||||
|
||||
// Add the models to be batched here
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {buildPreferenceKey} from '@database/operator/server_data_operator/comparators';
|
||||
import {shouldUpdateUserRecord} from '@database/operator/server_data_operator/comparators/user';
|
||||
import {
|
||||
transformPreferenceRecord,
|
||||
transformUserRecord,
|
||||
|
|
@ -102,6 +103,7 @@ describe('*** Operator: User Handlers tests ***', () => {
|
|||
tableName: 'User',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformUserRecord,
|
||||
shouldUpdate: shouldUpdateUserRecord,
|
||||
}, 'handleUsers');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {buildPreferenceKey} from '@database/operator/server_data_operator/comparators';
|
||||
import {shouldUpdateUserRecord} from '@database/operator/server_data_operator/comparators/user';
|
||||
import {
|
||||
transformPreferenceRecord,
|
||||
transformUserRecord,
|
||||
|
|
@ -126,6 +127,7 @@ const UserHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supercla
|
|||
createOrUpdateRawValues,
|
||||
tableName: USER,
|
||||
prepareRecordsOnly,
|
||||
shouldUpdate: shouldUpdateUserRecord,
|
||||
}, 'handleUsers');
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ export type ProcessRecordsArgs = {
|
|||
tableName: string;
|
||||
fieldName: string;
|
||||
buildKeyRecordBy?: (obj: Record<string, any>) => string;
|
||||
shouldUpdate?: (existing: Record<string, any>, newRaw: Record<string, any>) => boolean;
|
||||
};
|
||||
|
||||
export type HandleRecordsArgs<T extends Model> = {
|
||||
|
|
@ -165,6 +166,7 @@ export type HandleRecordsArgs<T extends Model> = {
|
|||
deleteRawValues?: RawValue[];
|
||||
tableName: string;
|
||||
prepareRecordsOnly: boolean;
|
||||
shouldUpdate?: (existingRecord: T, newRaw: RawValue) => boolean;
|
||||
};
|
||||
|
||||
export type RangeOfValueArgs = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue