From f117a6f6d947202202c4bc9a2aa0ffb0f8f927ba Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Wed, 13 Sep 2017 12:48:43 +0300 Subject: [PATCH] fix condition where message was not inserted if a partially stored attachment already existed --- .gitignore | 1 + lib/attachments/gridstore-storage.js | 38 +++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 943173e1..0bf1dbd8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ npm-debug.log .npmrc config/production.* config/development.* +package-lock.json* emails/* !emails/README.md diff --git a/lib/attachments/gridstore-storage.js b/lib/attachments/gridstore-storage.js index fa8e1bcf..9a1320f2 100644 --- a/lib/attachments/gridstore-storage.js +++ b/lib/attachments/gridstore-storage.js @@ -35,9 +35,7 @@ class GridstoreStorage { create(attachment, hash, callback) { hash = Buffer.from(hash, 'hex'); - let returned = false; - let retried = false; let id = hash; let metadata = { @@ -52,6 +50,7 @@ class GridstoreStorage { } }); + let tryCount = 0; let tryStore = () => { this.gridfs.collection(this.bucketName + '.files').findOneAndUpdate({ _id: hash @@ -84,8 +83,11 @@ class GridstoreStorage { } if (err.code === 11000) { // most probably a race condition, try again - if (!retried) { - retried = true; + if (tryCount++ < 5) { + if (/attachments\.chunks /.test(err.message)) { + // partial chunks for a probably deleted message detected, try to clean up + return setTimeout(() => this.cleanupGarbage(id, tryStore), 100 + 200 * Math.random()); + } return setTimeout(tryStore, 10); } } @@ -205,8 +207,8 @@ class GridstoreStorage { // make sure that we do not delete a message that is already re-used 'metadata.c': 0, 'metadata.m': 0 - }, (err, result) => { - if (err || !result.deletedCount) { + }, err => { + if (err) { return processNext(); } @@ -227,6 +229,30 @@ class GridstoreStorage { processNext(); } + + cleanupGarbage(id, next) { + this.gridfs.collection('attachments.files').findOne({ + _id: id + }, (err, file) => { + if (err) { + return next(err); + } + if (file) { + // attachment entry exists, do nothing + return next(null, false); + } + + // orphaned attachment, delete data chunks + this.gridfs.collection('attachments.chunks').deleteMany({ + files_id: id + }, (err, info) => { + if (err) { + return next(err); + } + next(null, info.deletedCount); + }); + }); + } } module.exports = GridstoreStorage;