Add MailerLog method for handling transactional emails errors

The new method allows processing sending errors that happen when sending from other places of plugin
then from the Sending Queue worker.
After three failed attempts it pauses the sending and admin user will see an notice.
[MAILPOET-4736]
This commit is contained in:
Rostislav Wolny
2022-12-07 09:25:41 +01:00
committed by Aschepikov
parent 5a53406d33
commit 11beebf74d
2 changed files with 95 additions and 1 deletions

View File

@@ -16,7 +16,9 @@ use MailPoet\Settings\SettingsController;
* "status": ?string,
* "retry_attempt": ?int,
* "retry_at": ?int,
* "error": ?MailerLogError
* "error": ?MailerLogError,
* "transactional_email_last_error_at": ?int,
* "transactional_email_error_count": ?int,
* }
*/
@@ -56,6 +58,8 @@ class MailerLog {
'retry_attempt' => null,
'retry_at' => null,
'error' => null,
'transactional_email_last_error_at' => null,
'transactional_email_error_count' => null,
];
$settings = SettingsController::getInstance();
$settings->set(self::SETTING_NAME, $mailerLog);
@@ -115,6 +119,8 @@ class MailerLog {
$mailerLog['status'] = self::STATUS_PAUSED;
$mailerLog['retry_attempt'] = null;
$mailerLog['retry_at'] = null;
$mailerLog['transactional_email_last_error_at'] = null;
$mailerLog['transactional_email_error_count'] = null;
return self::updateMailerLog($mailerLog);
}
@@ -172,6 +178,34 @@ class MailerLog {
self::enforceExecutionRequirements();
}
/**
* Process error, increase transactional_email_error_count and pauses sending if it reaches retry limit
* This method is meant to be used for processing errors when sending transactional emails
* like: Confirmation Email, Preview email, Stats Notification etc.
*
* @throws \Exception
*/
public static function processTransactionalEmailError(
string $operation,
string $errorMessage,
?string $errorCode = null
): void {
$mailerLog = self::getMailerLog();
$lastErrorTime = $mailerLog['transactional_email_last_error_at'] ?? null;
$ignoreErrorThreshold = time() - (2 * 60); // 2 minutes ago
// We want to log the error max one time per 2 minutes
if ($lastErrorTime && $lastErrorTime > $ignoreErrorThreshold) {
return;
}
$mailerLog = self::setError($mailerLog, $operation, $errorMessage, $errorCode);
$mailerLog['transactional_email_last_error_at'] = time();
$mailerLog['transactional_email_error_count'] = ($mailerLog['transactional_email_error_count'] ?? 0) + 1;
self::updateMailerLog($mailerLog);
if ($mailerLog['transactional_email_error_count'] > self::RETRY_ATTEMPTS_LIMIT) {
self::pauseSending($mailerLog);
}
}
/**
* @param MailerLogData $mailerLog
* @param string $operation
@@ -242,6 +276,8 @@ class MailerLog {
$mailerLog['retry_attempt'] = null;
$mailerLog['retry_at'] = null;
$mailerLog['error'] = null;
$mailerLog['transactional_email_last_error_at'] = null;
$mailerLog['transactional_email_error_count'] = null;
return self::updateMailerLog($mailerLog);
}

View File

@@ -3,6 +3,7 @@
namespace MailPoet\Test\Mailer;
use MailPoet\Mailer\Mailer;
use MailPoet\Mailer\MailerError;
use MailPoet\Mailer\MailerLog;
use MailPoet\Settings\SettingsController;
use MailPoet\Settings\SettingsRepository;
@@ -266,6 +267,59 @@ class MailerLogTest extends \MailPoetTest {
);
}
public function testItProcessesTransactionalEmailSendingError() {
$mailerLog = MailerLog::getMailerLog();
expect($mailerLog['transactional_email_last_error_at'])->null();
expect($mailerLog['transactional_email_error_count'])->null();
MailerLog::processTransactionalEmailError(MailerError::OPERATION_SEND, 'email rejected');
$mailerLog = MailerLog::getMailerLog();
expect($mailerLog['transactional_email_last_error_at'])->equals(time(), 1);
expect($mailerLog['transactional_email_error_count'])->equals(1);
expect($mailerLog['error'])->equals(
[
'operation' => MailerError::OPERATION_SEND,
'error_message' => 'email rejected',
]
);
}
public function testItSkipsTransactionalEmailSendingErrorWhenLastLoggedIsWithinIgnoreThreshold() {
$mailerLog = MailerLog::createMailerLog();
$almostTwoMinutesAgo = time() - 110;
$mailerLog['transactional_email_last_error_at'] = $almostTwoMinutesAgo;
$mailerLog['transactional_email_error_count'] = 1;
MailerLog::updateMailerLog($mailerLog);
MailerLog::processTransactionalEmailError(MailerError::OPERATION_SEND, 'email rejected');
$mailerLog = MailerLog::getMailerLog();
expect($mailerLog['transactional_email_last_error_at'])->equals($almostTwoMinutesAgo);
expect($mailerLog['transactional_email_error_count'])->equals(1);
}
public function testItIncreaseCounterOfTransactionalEmailSendingErrorWhenLastLoggedOlderThanIgnoreThreshold() {
$mailerLog = MailerLog::createMailerLog();
$moreThanTwoMinutesAgo = time() - 130;
$mailerLog['transactional_email_last_error_at'] = $moreThanTwoMinutesAgo;
$mailerLog['transactional_email_error_count'] = 1;
MailerLog::updateMailerLog($mailerLog);
MailerLog::processTransactionalEmailError(MailerError::OPERATION_SEND, 'email rejected');
$mailerLog = MailerLog::getMailerLog();
expect($mailerLog['transactional_email_last_error_at'])->equals(time(), 1);
expect($mailerLog['transactional_email_error_count'])->equals(2);
}
public function testItPausesSendingWhenTransactionalEmailSendingErrorCountReachesLimit() {
$mailerLog = MailerLog::createMailerLog();
$moreThanTwoMinutesAgo = time() - 130;
$mailerLog['transactional_email_last_error_at'] = $moreThanTwoMinutesAgo;
$mailerLog['transactional_email_error_count'] = MailerLog::RETRY_ATTEMPTS_LIMIT;
MailerLog::updateMailerLog($mailerLog);
MailerLog::processTransactionalEmailError(MailerError::OPERATION_SEND, 'email rejected');
$mailerLog = MailerLog::getMailerLog();
expect($mailerLog['transactional_email_last_error_at'])->null();
expect($mailerLog['transactional_email_error_count'])->null();
expect(MailerLog::isSendingPaused())->true();
}
public function testItEnforcesSendingLimit() {
$mailerConfig = [
'frequency' => [
@@ -368,12 +422,16 @@ class MailerLogTest extends \MailPoetTest {
'operation' => 'operation',
'error_code' => 'error_code',
'error_message' => 'error_message',
'transactional_email_last_error_at' => time(),
'transactional_email_error_count' => 1,
];
$mailerLog['status'] = 'status';
$mailerLog = MailerLog::clearSendingErrorLog($mailerLog);
expect($mailerLog['retry_attempt'])->null();
expect($mailerLog['retry_at'])->null();
expect($mailerLog['error'])->null();
expect($mailerLog['transactional_email_last_error_at'])->null();
expect($mailerLog['transactional_email_error_count'])->null();
expect($mailerLog['status'])->equals('status');
}