This commit is contained in:
Andris Reinman 2016-05-26 12:53:12 +03:00
parent 773977dd96
commit 8546040e97
6 changed files with 38 additions and 19 deletions

View file

@ -1,5 +1,9 @@
# Changelog # Changelog
## 1.10.1 2016-05-26
* Fix a bug with SMTP transport instance where campaign sending stalled until server was restarted
## 1.10.0 2016-05-25 ## 1.10.0 2016-05-25
* Fetch multiple unsent messages at once to speed up delivery * Fetch multiple unsent messages at once to speed up delivery

View file

@ -8,7 +8,7 @@
title="mailtrain" title="mailtrain"
[log] [log]
level="silly" level="verbose"
[www] [www]
# HTTP port to listen on # HTTP port to listen on

View file

@ -6,6 +6,7 @@ let nodemailer = require('nodemailer');
let openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt; let openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt;
let settings = require('./models/settings'); let settings = require('./models/settings');
let tools = require('./tools'); let tools = require('./tools');
let caches = require('./caches');
let Handlebars = require('handlebars'); let Handlebars = require('handlebars');
let fs = require('fs'); let fs = require('fs');
let path = require('path'); let path = require('path');
@ -102,6 +103,14 @@ function createMailer(callback) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
let oldListeners = [];
if (module.exports.transport) {
oldListeners = module.exports.transport.listeners('idle');
module.exports.transport.removeAllListeners('idle');
module.exports.transport.removeAllListeners('stream');
}
module.exports.transport = nodemailer.createTransport({ module.exports.transport = nodemailer.createTransport({
pool: true, pool: true,
host: configItems.smtpHostname, host: configItems.smtpHostname,
@ -114,9 +123,9 @@ function createMailer(callback) {
}, },
debug: !!configItems.smtpLog, debug: !!configItems.smtpLog,
logger: !configItems.smtpLog ? false : { logger: !configItems.smtpLog ? false : {
debug: log.info.bind(log, 'Mail'), debug: log.verbose.bind(log, 'Mail'),
info: log.verbose.bind(log, 'Mail'), info: log.info.bind(log, 'Mail'),
error: log.info.bind(log, 'Mail') error: log.error.bind(log, 'Mail')
}, },
maxConnections: Number(configItems.smtpMaxConnections), maxConnections: Number(configItems.smtpMaxConnections),
maxMessages: Number(configItems.smtpMaxMessages), maxMessages: Number(configItems.smtpMaxMessages),
@ -129,6 +138,12 @@ function createMailer(callback) {
passphrase: configItems.pgpPassphrase passphrase: configItems.pgpPassphrase
})); }));
if (oldListeners.length) {
log.info('Mail', 'Reattaching %s idle listeners', oldListeners.length);
oldListeners.forEach(listener => module.exports.transport.on('idle', listener));
}
caches.cache.delete('sender queue');
return callback(null, module.exports.transport); return callback(null, module.exports.transport);
}); });
} }

View file

@ -745,6 +745,8 @@ module.exports.delete = (id, callback) => {
if (err) { if (err) {
return callback(err); return callback(err);
} }
caches.cache.delete('sender queue');
return callback(null, affected); return callback(null, affected);
}); });
}); });

View file

@ -1,7 +1,7 @@
{ {
"name": "mailtrain", "name": "mailtrain",
"private": true, "private": true,
"version": "1.10.0", "version": "1.10.1",
"description": "Self hosted email newsletter app", "description": "Self hosted email newsletter app",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -33,10 +33,10 @@
"body-parser": "^1.15.1", "body-parser": "^1.15.1",
"bounce-handler": "^7.3.2-fork.0", "bounce-handler": "^7.3.2-fork.0",
"compression": "^1.6.2", "compression": "^1.6.2",
"config": "^1.20.1", "config": "^1.20.4",
"connect-flash": "^0.1.1", "connect-flash": "^0.1.1",
"connect-redis": "^3.0.2", "connect-redis": "^3.0.2",
"cookie-parser": "^1.4.1", "cookie-parser": "^1.4.2",
"csurf": "^1.8.3", "csurf": "^1.8.3",
"csv-parse": "^1.1.0", "csv-parse": "^1.1.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
@ -50,16 +50,16 @@
"html-to-text": "^2.1.0", "html-to-text": "^2.1.0",
"humanize": "0.0.9", "humanize": "0.0.9",
"is-url": "^1.2.1", "is-url": "^1.2.1",
"isemail": "^2.1.0", "isemail": "^2.1.2",
"jsdom": "^9.0.0", "jsdom": "^9.2.0",
"juice": "^1.11.0", "juice": "^2.0.0",
"moment-timezone": "^0.5.4", "moment-timezone": "^0.5.4",
"morgan": "^1.7.0", "morgan": "^1.7.0",
"multer": "^1.1.0", "multer": "^1.1.0",
"mysql": "^2.10.2", "mysql": "^2.10.2",
"nodemailer": "^2.4.1", "nodemailer": "^2.4.2",
"nodemailer-openpgp": "^1.0.2", "nodemailer-openpgp": "^1.0.2",
"npmlog": "^2.0.3", "npmlog": "^2.0.4",
"openpgp": "^2.3.0", "openpgp": "^2.3.0",
"passport": "^0.3.2", "passport": "^0.3.2",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",

View file

@ -18,7 +18,6 @@ let request = require('request');
let caches = require('../lib/caches'); let caches = require('../lib/caches');
function findUnsent(callback) { function findUnsent(callback) {
let returnUnsent = (row, campaign) => { let returnUnsent = (row, campaign) => {
db.getConnection((err, connection) => { db.getConnection((err, connection) => {
if (err) { if (err) {
@ -59,7 +58,6 @@ function findUnsent(callback) {
// Find "normal" campaigns. Ignore RSS and drip campaigns at this point // Find "normal" campaigns. Ignore RSS and drip campaigns at this point
let query = 'SELECT `id`, `list`, `segment` FROM `campaigns` WHERE `status`=? AND (`scheduled` IS NULL OR `scheduled` <= NOW()) AND `type` IN (?, ?) LIMIT 1'; let query = 'SELECT `id`, `list`, `segment` FROM `campaigns` WHERE `status`=? AND (`scheduled` IS NULL OR `scheduled` <= NOW()) AND `type` IN (?, ?) LIMIT 1';
connection.query(query, [2, 1, 3], (err, rows) => { connection.query(query, [2, 1, 3], (err, rows) => {
connection.release(); connection.release();
if (err) { if (err) {
@ -79,6 +77,7 @@ function findUnsent(callback) {
values: [] values: []
}); });
} }
segments.getQuery(segmentId, 'subscription', next); segments.getQuery(segmentId, 'subscription', next);
}; };
@ -100,14 +99,15 @@ function findUnsent(callback) {
let values; let values;
// NOT IN // NOT IN
query = 'SELECT * FROM `subscription__' + campaign.list + '` AS subscription WHERE status=1 ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' AND id NOT IN (SELECT subscription FROM `campaign__' + campaign.id + '` campaign WHERE campaign.list = ? AND campaign.segment = ? AND campaign.subscription = subscription.id) LIMIT 100'; query = 'SELECT * FROM `subscription__' + campaign.list + '` AS subscription WHERE status=1 ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' AND id NOT IN (SELECT subscription FROM `campaign__' + campaign.id + '` campaign WHERE campaign.list = ? AND campaign.segment = ? AND campaign.subscription = subscription.id) LIMIT 150';
values = queryData.values.concat([campaign.list, campaign.segment]); values = queryData.values.concat([campaign.list, campaign.segment]);
// LEFT JOIN / IS NULL // LEFT JOIN / IS NULL
//query = 'SELECT subscription.* FROM `subscription__' + campaign.list + '` AS subscription LEFT JOIN `campaign__' + campaign.id + '` AS campaign ON campaign.list = ? AND campaign.segment = ? AND campaign.subscription = subscription.id WHERE subscription.status=1 ' + (queryData.where ? 'AND (' + queryData.where + ') ' : '') + 'AND campaign.id IS NULL LIMIT 100'; //query = 'SELECT subscription.* FROM `subscription__' + campaign.list + '` AS subscription LEFT JOIN `campaign__' + campaign.id + '` AS campaign ON campaign.list = ? AND campaign.segment = ? AND campaign.subscription = subscription.id WHERE subscription.status=1 ' + (queryData.where ? 'AND (' + queryData.where + ') ' : '') + 'AND campaign.id IS NULL LIMIT 150';
//values = [campaign.list, campaign.segment].concat(queryData.values); //values = [campaign.list, campaign.segment].concat(queryData.values);
connection.query(query, values, (err, rows) => { connection.query(query, values, (err, rows) => {
if (err) { if (err) {
connection.release(); connection.release();
return callback(err); return callback(err);
@ -146,7 +146,6 @@ function formatMessage(message, callback) {
if (!campaign) { if (!campaign) {
return callback(new Error('Campaign not found')); return callback(new Error('Campaign not found'));
} }
lists.get(message.listId, (err, list) => { lists.get(message.listId, (err, list) => {
if (err) { if (err) {
return callback(err); return callback(err);
@ -161,7 +160,6 @@ function formatMessage(message, callback) {
} }
let useVerp = config.verp.enabled && configItems.verpUse && configItems.verpHostname; let useVerp = config.verp.enabled && configItems.verpUse && configItems.verpHostname;
fields.list(list.id, (err, fieldList) => { fields.list(list.id, (err, fieldList) => {
if (err) { if (err) {
return callback(err); return callback(err);
@ -274,6 +272,7 @@ function formatMessage(message, callback) {
Object.keys(message.subscription.mergeTags).forEach(key => { Object.keys(message.subscription.mergeTags).forEach(key => {
form[key] = message.subscription.mergeTags[key]; form[key] = message.subscription.mergeTags[key];
}); });
request.post({ request.post({
url: campaign.sourceUrl, url: campaign.sourceUrl,
form form
@ -321,7 +320,6 @@ let sendLoop = () => {
} }
//log.verbose('Mail', 'Found new message to be delivered: %s', message.subscription.cid); //log.verbose('Mail', 'Found new message to be delivered: %s', message.subscription.cid);
// format message to nodemailer message format // format message to nodemailer message format
formatMessage(message, (err, mail) => { formatMessage(message, (err, mail) => {
if (err) { if (err) {