[FIX] mail: speed up insert by using localId instead of find

Part of task-2308312

X-original-commit: 407dc9796543a27b6695c8d4da922c13dece5b38
This commit is contained in:
Sébastien Theys
2020-08-03 15:49:02 +00:00
parent 7f3a8d9928
commit 24fba9fed7
21 changed files with 202 additions and 117 deletions
@@ -293,6 +293,10 @@ class ChatWindow extends Component {
* CTRL-C & CTRL-V when on the home menu.
*/
ev.stopPropagation();
if (!this.chatWindow) {
// prevent crash during delete
return;
}
switch (ev.key) {
case 'Tab':
ev.preventDefault();
@@ -346,7 +346,7 @@ QUnit.test("'channel_fetch' notification received is correctly handled", async f
assert.expect(3);
await this.start();
const currentPartner = this.env.models['mail.partner'].create({
const currentPartner = this.env.models['mail.partner'].insert({
id: this.env.session.partner_id,
display_name: "Demo User",
});
@@ -402,7 +402,7 @@ QUnit.test("'channel_seen' notification received is correctly handled", async fu
assert.expect(3);
await this.start();
const currentPartner = this.env.models['mail.partner'].create({
const currentPartner = this.env.models['mail.partner'].insert({
id: this.env.session.partner_id,
display_name: "Demo User",
});
@@ -457,7 +457,7 @@ QUnit.test("'channel_fetch' notification then 'channel_seen' received are corre
assert.expect(4);
await this.start();
const currentPartner = this.env.models['mail.partner'].create({
const currentPartner = this.env.models['mail.partner'].insert({
id: this.env.session.partner_id,
display_name: "Demo User",
});
@@ -537,10 +537,12 @@ QUnit.test('do not show messaging seen indicator if not authored by me', async f
id: 11,
partnerSeenInfos: [['create', [
{
id: this.env.session.partner_id,
lastFetchedMessage: [['insert', {id: 100}]],
partner: [['insert', {id: this.env.session.partner_id}]],
},
{
id: 100,
lastFetchedMessage: [['insert', {id: 100}]],
partner: [['link', author]],
},
@@ -548,7 +550,7 @@ QUnit.test('do not show messaging seen indicator if not authored by me', async f
model: 'mail.channel',
});
const threadViewer = this.env.models['mail.thread_viewer'].create({ thread: [['link', thread]] });
const message = this.env.models['mail.message'].create({
const message = this.env.models['mail.message'].insert({
author: [['link', author]],
body: "<p>Test</p>",
id: 100,
@@ -572,7 +574,7 @@ QUnit.test('do not show messaging seen indicator if before last seen by all mess
assert.expect(3);
await this.start();
const currentPartner = this.env.models['mail.partner'].create({
const currentPartner = this.env.models['mail.partner'].insert({
id: this.env.session.partner_id,
display_name: "Demo User",
});
@@ -591,7 +593,7 @@ QUnit.test('do not show messaging seen indicator if before last seen by all mess
id: 100,
originThread: [['link', thread]],
});
const message = this.env.models['mail.message'].create({
const message = this.env.models['mail.message'].insert({
author: [['link', currentPartner]],
body: "<p>Test</p>",
id: 99,
@@ -600,10 +602,12 @@ QUnit.test('do not show messaging seen indicator if before last seen by all mess
thread.update({
partnerSeenInfos: [['create', [
{
id: currentPartner.id,
lastSeenMessage: [['link', lastSeenMessage]],
partner: [['link', currentPartner]],
},
{
id: 100,
lastSeenMessage: [['link', lastSeenMessage]],
partner: [['insert', {id: 100}]],
},
@@ -632,18 +636,19 @@ QUnit.test('only show messaging seen indicator if authored by me, after last see
assert.expect(3);
await this.start();
const currentPartner = this.env.models['mail.partner'].create({
const currentPartner = this.env.models['mail.partner'].insert({
id: this.env.session.partner_id,
display_name: "Demo User"
});
const thread = this.env.models['mail.thread'].create({
id: 11,
partnerSeenInfos: [['create', [
{
id: currentPartner.id,
lastSeenMessage: [['insert', {id: 100}]],
partner: [['link', currentPartner]],
},
{
id: 100,
partner: [['insert', {id: 100}]],
lastFetchedMessage: [['insert', {id: 100}]],
lastSeenMessage: [['insert', {id: 99}]],
@@ -656,7 +661,7 @@ QUnit.test('only show messaging seen indicator if authored by me, after last see
model: 'mail.channel',
});
const threadViewer = this.env.models['mail.thread_viewer'].create({ thread: [['link', thread]] });
const message = this.env.models['mail.message'].create({
const message = this.env.models['mail.message'].insert({
author: [['link', currentPartner]],
body: "<p>Test</p>",
id: 100,
@@ -677,8 +682,8 @@ QUnit.test('only show messaging seen indicator if authored by me, after last see
assert.containsN(
document.body,
'.o_MessageSeenIndicator_icon',
2,
"message component should have two checks (V)"
1,
"message component should have one check (V) because the message was fetched by everyone but no other member than author has seen the message"
);
});
@@ -60,10 +60,12 @@ QUnit.test('rendering when just one has received the message', async function (a
model: 'mail.channel',
partnerSeenInfos: [['create', [
{
id: 10,
partner: [['create', {id: 10}]],
lastFetchedMessage: [['insert', {id: 100}]]
},
{
id: 100,
partner: [['create', {id: 100}]],
},
]]],
@@ -72,7 +74,7 @@ QUnit.test('rendering when just one has received the message', async function (a
message: [['insert', {id: 100}]],
}]],
});
const message = this.env.models['mail.message'].create({
const message = this.env.models['mail.message'].insert({
author: [['insert', { id: this.env.session.partner_id, display_name: "Demo User" }]],
body: "<p>Test</p>",
id: 100,
@@ -105,10 +107,12 @@ QUnit.test('rendering when everyone have received the message', async function (
model: 'mail.channel',
partnerSeenInfos: [['create', [
{
id: 10,
partner: [['create', {id: 10}]],
lastFetchedMessage: [['insert', {id: 100}]],
},
{
id: 100,
partner: [['create', {id: 100}]],
lastFetchedMessage: [['insert', {id: 99}]],
},
@@ -118,7 +122,7 @@ QUnit.test('rendering when everyone have received the message', async function (
message: [['insert', {id: 100}]],
}]],
});
const message = this.env.models['mail.message'].create({
const message = this.env.models['mail.message'].insert({
author: [['insert', { id: this.env.session.partner_id, display_name: "Demo User" }]],
body: "<p>Test</p>",
id: 100,
@@ -151,11 +155,13 @@ QUnit.test('rendering when just one has seen the message', async function (asser
model: 'mail.channel',
partnerSeenInfos: [['create', [
{
id: 10,
partner: [['create', {id: 10}]],
lastFetchedMessage: [['insert', {id: 100}]],
lastSeenMessage: [['insert', {id: 100}]],
},
{
id: 100,
partner: [['create', {id: 100}]],
lastFetchedMessage: [['insert', {id: 99}]],
},
@@ -165,7 +171,7 @@ QUnit.test('rendering when just one has seen the message', async function (asser
message: [['insert', {id: 100}]],
}]],
});
const message = this.env.models['mail.message'].create({
const message = this.env.models['mail.message'].insert({
author: [['insert', { id: this.env.session.partner_id, display_name: "Demo User" }]],
body: "<p>Test</p>",
id: 100,
@@ -199,11 +205,13 @@ QUnit.test('rendering when just one has seen & received the message', async func
model: 'mail.channel',
partnerSeenInfos: [['create', [
{
id: 10,
partner: [['create', {id: 10}]],
lastFetchedMessage: [['insert', {id: 100}]],
lastSeenMessage: [['insert', {id: 100}]],
},
{
id: 100,
partner: [['create', {id: 100}]],
},
]]],
@@ -212,7 +220,7 @@ QUnit.test('rendering when just one has seen & received the message', async func
message: [['insert', {id: 100}]],
}]],
});
const message = this.env.models['mail.message'].create({
const message = this.env.models['mail.message'].insert({
author: [['insert', { id: this.env.session.partner_id, display_name: "Demo User" }]],
body: "<p>Test</p>",
id: 100,
@@ -246,11 +254,13 @@ QUnit.test('rendering when just everyone has seen the message', async function (
model: 'mail.channel',
partnerSeenInfos: [['create', [
{
id: 10,
partner: [['create', {id: 10}]],
lastFetchedMessage: [['insert', {id: 100}]],
lastSeenMessage: [['insert', {id: 100}]],
},
{
id: 100,
partner: [['create', {id: 100}]],
lastFetchedMessage: [['insert', {id: 100}]],
lastSeenMessage: [['insert', {id: 100}]],
@@ -261,7 +271,7 @@ QUnit.test('rendering when just everyone has seen the message', async function (
message: [['insert', {id: 100}]],
}]],
});
const message = this.env.models['mail.message'].create({
const message = this.env.models['mail.message'].insert({
author: [['insert', { id: this.env.session.partner_id, display_name: "Demo User" }]],
body: "<p>Test</p>",
id: 100,
@@ -126,7 +126,10 @@ class ModelManager {
const res = dataList.map(data => {
const record = new Model({ valid: true });
Object.defineProperty(record, 'env', { get: () => Model.env });
record.localId = record._createRecordLocalId(data);
record.localId = Model._createRecordLocalId(data);
if (Model.get(record.localId)) {
throw Error(`A record already exists for model "${Model.modelName}" with localId "${record.localId}".`);
}
// Contains field values of record.
record.__values = {};
// Contains revNumber of record for checking record update in useStore.
@@ -207,7 +210,6 @@ class ModelManager {
}
record.update(data);
delete this._records[record.localId];
delete this.env.store.state[record.localId];
});
}
@@ -277,7 +279,8 @@ class ModelManager {
const isMulti = typeof data[Symbol.iterator] === 'function';
const dataList = isMulti ? data : [data];
const res = dataList.map(data => {
let record = Model.find(Model._findFunctionFromData(data));
const localId = Model._createRecordLocalId(data);
let record = Model.get(localId);
if (!record) {
record = Model.create(data);
} else {
@@ -209,6 +209,13 @@ function factory(dependencies) {
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
/**
* @private
* @returns {boolean}
@@ -6,7 +6,20 @@ const { attr, one2many } = require('mail/static/src/model/model_field.js');
function factory(dependencies) {
class ActivityType extends dependencies['mail.model'] {}
class ActivityType extends dependencies['mail.model'] {
//----------------------------------------------------------------------
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
}
ActivityType.fields = {
activities: one2many('mail.activity', {
@@ -49,6 +49,20 @@ function factory(dependencies) {
return data2;
}
/**
* @override
*/
static create(data) {
const isMulti = typeof data[Symbol.iterator] === 'function';
const dataList = isMulti ? data : [data];
for (const data of dataList) {
if (!data.id) {
data.id = getAttachmentNextTemporaryId();
}
}
return super.create(...arguments);
}
/**
* View provided attachment(s), with given attachment initially. Prompts
* the attachment viewer.
@@ -94,6 +108,13 @@ function factory(dependencies) {
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
/**
* @private
* @returns {mail.composer[]}
@@ -185,17 +206,6 @@ function factory(dependencies) {
return match[1];
}
/**
* @private
* @returns {integer}
*/
_computeId() {
if (this.isTemporary && (this.id === undefined || this.id > 0)) {
return getAttachmentNextTemporaryId();
}
return this.id;
}
/**
* @private
* @returns {boolean}
@@ -236,18 +246,6 @@ function factory(dependencies) {
return this.mimetype && this.mimetype.split('/').shift();
}
/**
* @override
*/
_createRecordLocalId(data) {
const { id, isTemporary = false } = data;
const Attachment = this.env.models['mail.attachment'];
if (isTemporary) {
return `${Attachment.modelName}_${nextTemporaryId}`;
}
return `${Attachment.modelName}_${id}`;
}
}
Attachment.fields = {
@@ -291,10 +289,7 @@ function factory(dependencies) {
'url',
],
}),
id: attr({
compute: '_computeId',
dependencies: ['isTemporary'],
}),
id: attr(),
isLinkedToComposer: attr({
compute: '_computeIsLinkedToComposer',
dependencies: ['composers'],
@@ -6,7 +6,20 @@ const { attr } = require('mail/static/src/model/model_field.js');
function factory(dependencies) {
class Country extends dependencies['mail.model'] {}
class Country extends dependencies['mail.model'] {
//----------------------------------------------------------------------
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
}
Country.fields = {
id: attr(),
@@ -153,6 +153,13 @@ function factory(dependencies) {
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
/**
* @private
* @returns {string}
@@ -43,6 +43,17 @@ function factory(dependencies) {
return data2;
}
//----------------------------------------------------------------------
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
}
FollowerSubtype.fields = {
@@ -56,6 +56,17 @@ function factory(dependencies) {
}
}
//----------------------------------------------------------------------
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
}
MailTemplate.fields = {
@@ -315,6 +315,13 @@ function factory(dependencies) {
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
/**
* @returns {boolean}
*/
@@ -432,14 +439,6 @@ function factory(dependencies) {
return [['replace', threads]];
}
/**
* @override
*/
_createRecordLocalId(data) {
const Message = this.env.models['mail.message'];
return `${Message.modelName}_${data.id}`;
}
}
Message.fields = {
@@ -67,6 +67,13 @@ function factory(dependencies) {
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
/**
* Manually called as not always called when necessary
*
+7 -17
View File
@@ -39,9 +39,9 @@ function factory() {
*/
init() {}
//--------------------------------------------------------------------------
//----------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
//----------------------------------------------------------------------
/**
* Returns all records of this model that match provided criteria.
@@ -154,20 +154,9 @@ function factory() {
this.env.modelManager.update(this, data);
}
//--------------------------------------------------------------------------
//----------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @static
* @private
* @param {Object} data
* @param {any} data.id
* @return {function}
*/
static _findFunctionFromData(data) {
return record => record.id === data.id;
}
//----------------------------------------------------------------------
/**
* This method generates a local id for this record that is
@@ -180,12 +169,13 @@ function factory() {
* track relations and records in the system instead of arbitrary
* number to differenciate them.
*
* @static
* @private
* @param {Object} data
* @returns {string}
*/
_createRecordLocalId(data) {
return _.uniqueId(`${this.constructor.modelName}_`);
static _createRecordLocalId(data) {
return _.uniqueId(`${this.modelName}_`);
}
/**
@@ -46,6 +46,17 @@ function factory(dependencies) {
return data2;
}
//----------------------------------------------------------------------
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
}
Notification.fields = {
@@ -63,9 +63,8 @@ function factory(dependencies) {
/**
* @override
*/
_createRecordLocalId(data) {
const NotificationGroup = this.env.models['mail.notification_group'];
return `${NotificationGroup.modelName}_${data.id}`;
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
/**
@@ -156,6 +156,13 @@ function factory(dependencies) {
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
/**
* @static
* @private
@@ -211,14 +218,6 @@ function factory(dependencies) {
return this.name || this.display_name;
}
/**
* @override
*/
_createRecordLocalId(data) {
const Partner = this.env.models['mail.partner'];
return `${Partner.modelName}_${data.id}`;
}
}
Partner.fields = {
+7 -18
View File
@@ -764,8 +764,13 @@ function factory(dependencies) {
/**
* @override
*/
static _findFunctionFromData(data) {
return record => record.id === data.id && record.model === data.model;
static _createRecordLocalId(data) {
const { channel_type, id, model } = data;
let threadModel = model;
if (!threadModel && channel_type) {
threadModel = 'mail.channel';
}
return `${this.modelName}_${threadModel}_${id}`;
}
/**
@@ -1006,22 +1011,6 @@ function factory(dependencies) {
);
}
/**
* @override
*/
_createRecordLocalId(data) {
const { channel_type, id, isTemporary = false, model } = data;
let threadModel = model;
if (!threadModel && channel_type) {
threadModel = 'mail.channel';
}
const Thread = this.env.models['mail.thread'];
if (isTemporary) {
return `${Thread.modelName}_${id}`;
}
return `${Thread.modelName}_${threadModel}_${id}`;
}
/**
* @private
* @param {Object} param0
@@ -94,6 +94,17 @@ function factory(dependencies) {
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
const {
stringifiedDomain = '[]',
thread: [[commandInsert, thread]],
} = data;
return `${this.modelName}_[${thread.localId}]_<${stringifiedDomain}>`;
}
/**
* @private
*/
@@ -204,18 +215,6 @@ function factory(dependencies) {
)]];
}
/**
* @override
*/
_createRecordLocalId(data) {
const {
stringifiedDomain = '[]',
thread: [[commandInsert, thread]],
} = data;
const ThreadCache = this.env.models['mail.thread_cache'];
return `${ThreadCache.modelName}_[${thread.localId}]_<${stringifiedDomain}>`;
}
/**
* @private
* @param {Array} domain
@@ -6,7 +6,20 @@ const { attr, many2one } = require('mail/static/src/model/model_field.js');
function factory(dependencies) {
class ThreadPartnerSeenInfo extends dependencies['mail.model'] {}
class ThreadPartnerSeenInfo extends dependencies['mail.model'] {
//----------------------------------------------------------------------
// Private
//----------------------------------------------------------------------
/**
* @override
*/
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
}
ThreadPartnerSeenInfo.modelName = 'mail.thread_partner_seen_info';
+2 -2
View File
@@ -42,8 +42,8 @@ function factory(dependencies) {
/**
* @override
*/
_createRecordLocalId(data) {
return `${this.env.models['mail.user'].modelName}_${data.id}`;
static _createRecordLocalId(data) {
return `${this.modelName}_${data.id}`;
}
/**