When it comes to sending newsletters, 8 Thousand recipients is not a high number, let alone a number that will consume all available memory.
What seems to be missing is an optimization of the objects in use so as not to fill the memory with the same information repeated 8 Thousand times.
Possible optimisation
function enviarNewsletterUsuarios() {
// recolher os destinatários
$usuarios = getUsuarios();
// se temos destinatários
if ($usuarios) {
/* Preparar o envio definindo os valores comuns
*/
$mail = new PHPMailer;
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'host'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'user'; // SMTP username
$mail->Password = 'pass'; // SMTP password
$mail->SMTPSecure = 'tls'; // Enable encryption, 'ssl' also accepted
$mail->From = 'teste';
$mail->FromName = 'teste';
$mail->WordWrap = 50; // Set word wrap to 50 characters
$mail->isHTML(true); // Set email format to HTML
$mail->Subject = 'Newsletter';
$mail->Body = utf8_decode('tesste');
/* Por cada destinatário enviar o email
*/
foreach ($usuarios as $usuario) {
$mail->addAddress($usuario->email); // Add a recipient
if ($mail->send()) {
// acção quando correu bem
} else {
// correu mal :(
}
/* Limpa a lista de destinatários para evitar que o email
* a enviar vá acumulando destinatários
*/
$mail->ClearAllRecipients();
}
return true;
}
}
In your code I also noticed that you have:
$objetivo = getObjetivo($usuario->guid);
$ideias = getIdeiaPorObjetivo($objetivo, $usuario->cidade);
In case it is something that will be "injected" in the body of the email or the subject of it, I already recommend a Queue to send in a database because the job of sending the email is exactly that, send the email. Its information must be prepared and ready to use or effectively faces high memory consumption problems.
Example of a table for Queue newsletter:
CREATE TABLE IF NOT EXISTS `newsletter_queue` (
`id` int(13) NOT NULL AUTO_INCREMENT COMMENT 'Internal ID',
`newsletter_id` int(13) NOT NULL COMMENT 'ID from the table "newsletter"',
`entity_id` int(13) NOT NULL COMMENT 'ID from the table "entity".',
`entity_type` enum('entity','subscriber','admin user','unknown') NOT NULL DEFAULT 'entity' COMMENT 'The entity type for statistics.',
`send_method` set('mail','smtp') NOT NULL DEFAULT '' COMMENT 'Message is sent using PHP mail() or SMTP.',
`from_email` varchar(255) NOT NULL COMMENT 'Sets the From email address for the message',
`from_name` varchar(255) NOT NULL COMMENT 'Sets the From name of the message',
`smtp_host` varchar(255) NOT NULL COMMENT 'Sets the SMTP hosts. All hosts must be separated by a semicolon. You can also specify a different port for each host by using this format: [hostname:port] (e.g. "smtp1.example.com:25;smtp2.example.com"). Hosts will be tried in order.',
`smtp_port` int(4) NOT NULL DEFAULT '25' COMMENT 'Sets the default SMTP server port.',
`smtp_auth` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Sets SMTP authentication. Utilizes the Username and Password variables.',
`smtp_secure` enum('','ssl','tls','starttls') NOT NULL DEFAULT '' COMMENT 'Sets connection prefix.',
`smtp_username` varchar(255) NOT NULL COMMENT 'Sets SMTP username.',
`smtp_password` varchar(255) NOT NULL COMMENT 'Sets SMTP password.',
`recipient_mail` varchar(255) NOT NULL,
`recipient_name` varchar(255) NOT NULL,
`attachment` varchar(255) NOT NULL,
`content_type` varchar(50) NOT NULL,
`priority` enum('1','3','5') NOT NULL DEFAULT '3' COMMENT 'Email priority (1 = High, 3 = Normal, 5 = low)',
`charset` enum('iso-8859-1','utf-8','windows-1252') NOT NULL DEFAULT 'utf-8' COMMENT 'Sets the CharSet of the message.',
`send_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `newsletter_id` (`newsletter_id`),
KEY `entity_id` (`entity_id`),
KEY `entity_type` (`entity_type`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
With a table like this, you have a function that adds emails to the Queue and your sending function will be limited to sending X emails every X minutes. This way you definitely solve memory issues now or in the future when your CRON runs to send emails.
How’s your
getUsuarios()
? Maybe the solution is there. You would need to slowly return users, either individually or in blocks, so reading in the database would load less information. Individually would be easier to treat but would lose performance.– Maniero
The function is only an alias for elgg_list_entities() of the Elgg http://reference.elgg.org/entities_8php.html#af085c8362e49c4f52d4f1fcf58ca6fb8 framework
– Marcelo de Andrade
This information is very relevant. And then I owe you because I know nothing of this framework. I hope he uses
yield
. Anyway, if you’re carrying everything, the blame for the high consumption is on this function. I wouldn’t know how to solve an external problem I don’t know about. Of course I could advise you to access the data in another way, but it’s probably not what you want.– Maniero
I’m still studying the Framework and will not be able to tell you about it. If you have any ideas, thank you for sharing it.
– Marcelo de Andrade