Blogdevphp

Command : Comment éviter les fuites mémoire lors de l'import de données

2014-06-21 symfony

Pour exécuter l'import de données, vous pouvez être amené à créer un batch.

Pour cela, vous allez créer une commande Symfony, qui va effectuer cet import à un moment donné. Pour cela, vous allez planifier un cron pour l'exécuter.

Vous pouvez être dans le cas où une grosse masse de données doit être importée et que vous devez effectuer un certain nombre de traitements.Vous serez confronté à la gestion de la mémoire, plus particulièrement à risque de fuites mémoire.

Si le script doit tourner pendant un certain temps, la quantité de mémoire, augmente au fur et à mesure car elle n'est pas libérée. Afin de ne point encombrer la mémoire, il y a de bonnes pratiques à avoir.

 

<?php

namespace Acme\BatchBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;


class ImportDonneesMetroCommand extends ContainerAwareCommand {
    
 
 private $em;

 
 protected function configure() {
  $this->setName('acme:batch:importdonnees')
  ->setDescription('Import des données')
  ;
 }
 
 
 private function init() {
  $this->em = $this->getContainer()->get('doctrine')->getManager();
  $this->em->getConnection()->getConfiguration()->setSQLLogger(null);
  
 }
 
 
 protected function execute(InputInterface $input, OutputInterface $output) {
  
  $this->init();
        
        $anteriorMemory = round(memory_get_usage(true) / 1024);
  
  while ( ... ) {
             
             try{
              
                if (round(memory_get_usage(true) / 1024) > $anteriorMemory ) {
                
                 $output->writeln(sprintf('Fuite mémoire à la ligne '. $rowIndex . ' du fichier csv'));
                 $output->writeln(sprintf('Memory usage (currently) %dKB/ (max) %dKB', round(memory_get_usage(true) / 1024), memory_get_peak_usage(true) / 1024));
                }
                
                $anteriorMemory = round(memory_get_usage(true) / 1024);
  
  /****script import****/
  
  $this->em->flush();
  $this->em->clear();
  gc_collect_cycles();
  
  }
 }

?>

Déjà dans la fonction init, par rapport à la connexion à la base de données, SQL enregistre des informations lorsqu'on intéragit avec la base de données. Pour ne pas s'encombrer de ses données, on désactive cette fonctionnalité avec la méthode setSQLLogger().

Afin de voir l'état de la mémoire,on va utiliser les méthodes memory_get_usage ainsi que memory_get_peak_usage.

De même, sous Doctrine, pour libérer de la mémoire, on va utiliser la fonction clear() après le flush, donc, juste après l'enregistrement des données en base.

Avec la version PHP 5.3, la question des fuites mémoire a été pris en compte, pour les longs scripts. Le Garbage Collector a été introduit, par rapport à cette question, afin de libérer de la mémoire. Pour cela, on utilise la fonction gc_collect_cycles() afin de l'éxécuter..

comments powered by Disqus
Copyright © 2018 blogdevphp.fr - Tous droits réservés