mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-03-02 02:53:01 +08:00
[local-sync] Fix UniqueConstraintError inserting contacts from messages
Summary: Because of JavaScript's asynchronous nature, it is possible that we will be processing several downloaded messages concurrently. This can lead to calling extractContacts() in an interleaved fashion, which it was not designed to handle. It looks up which contacts are already in the database and then performs inserts or updates accordingly, assuming nothing has changed in the contacts table in between---which is not true! If several messages have similar contacts, an insert race can cause one of the inserts to throw an unhandled exception. We fix this by simply catching the unique constraint error, and falling back to an update instead. (There's not really a better way to deal with write races other than to enforce that we process contacts from messages serially, as transactions are of no help here.) This commit also removes extractContacts()'s return value, which is not currently used and I found confusing. Test Plan: manual Reviewers: juan, evan, mark, jackie Reviewed By: jackie Differential Revision: https://phab.nylas.com/D3555
This commit is contained in:
parent
de1b67287c
commit
0644d2663d
1 changed files with 18 additions and 8 deletions
|
@ -1,3 +1,4 @@
|
|||
const Sequelize = require('sequelize');
|
||||
|
||||
function isContactMeaningful(contact) {
|
||||
// some suggestions: http://stackoverflow.com/questions/6317714/apache-camel-mail-to-identify-auto-generated-messages
|
||||
|
@ -31,6 +32,7 @@ async function extractContacts({db, message}) {
|
|||
}
|
||||
contactsDataById.set(id, cdata)
|
||||
})
|
||||
|
||||
const existingContacts = await Contact.findAll({
|
||||
where: {
|
||||
id: Array.from(contactsDataById.keys()),
|
||||
|
@ -38,18 +40,26 @@ async function extractContacts({db, message}) {
|
|||
})
|
||||
|
||||
for (const c of contactsDataById.values()) {
|
||||
const existing = existingContacts.find(({id}) => id === c.id)
|
||||
if (!existing) {
|
||||
await Contact.create(c)
|
||||
} else {
|
||||
const updateRequired = (c.name !== existing.name);
|
||||
if (updateRequired) {
|
||||
await existing.update(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
const existing = existingContacts.find(({id}) => id === c.id);
|
||||
|
||||
return message;
|
||||
if (!existing) {
|
||||
Contact.create(c).catch(Sequelize.ValidationError, (err) => {
|
||||
if (err.name !== "SequelizeUniqueConstraintError") {
|
||||
console.log('Unknown error inserting contact', err);
|
||||
throw err;
|
||||
} else {
|
||||
// Another message with the same contact was processing concurrently,
|
||||
// and beat us to inserting. Since contacts are never deleted within
|
||||
// an account, we can safely assume that we can perform an update
|
||||
// instead.
|
||||
Contact.find({where: {id: c.id}}).then(
|
||||
(row) => { row.update(c) });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
existing.update(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = extractContacts
|
||||
|
|
Loading…
Reference in a new issue