2012-07-09 10 views
19

APIからデータをインポートするためにSymfonyコマンドを書いています。それは動作しますが、私のデータベースに大きなJSONを挿入するとPHPメモリの使用量が増えるという問題があります。そして、私のunitOfWorkは、各アクティビティのインポート後に '2'ずつ増加します。

私はすでに解除すべての私の使用のオブジェクトを持っている、とあなたは大規模なバッチをしたいときに私はSymfony2ののドキュメントを読んでいる:http://www.doctrine-project.org/blog/doctrine2-batch-processing.html

しかし、私は$em->clear()を使用するときに私のエンティティマネージャは、このエラーを与える:

Notice: Undefined index: 000000007b56ea7100000000e366c259 in path-to-application\vendor\doctrine\lib\Doctrine\ORM\UnitOfWork.php line 2228

/** 
* @see Command 
*/ 
protected function configure() { 
    $this 
    ->setName('ks:user:runkeepersync') 
    ->setDescription('Synchroniser les activités d\'un utilisateur runkeeper') 
    ->setDefinition(array(
     new InputArgument('access_token', InputArgument::REQUIRED, 'Access token'), 
    )) 
} 

/** 
* @see Command 
*/ 
protected function execute(InputInterface $input, OutputInterface $output) { 
    $accessToken = $input->getArgument('access_token'); 
    $em = $this->getContainer()->get('doctrine')->getEntityManager(); 
    $UserHasServices = $em->getRepository('KsUserBundle:UserHasServices')->findOneByToken($accessToken); 
    if (!is_object($UserHasServices)) { 
    echo "Impossible de trouver l'utilisateur qui possède le jeton ".$accessToken.""; 
    } 
    $user = $UserHasServices->getUser(); 
    $service = $UserHasServices->getService(); 
    echo "avant de requérir l'api : ".memory_get_usage()."\n"; 
    try { 
    $rkApi = $this->getContainer()->get('ks_user.runkeeper'); 
    $rkApi->setAccessToken($accessToken); 
    $activities = $rkApi->getFitnessActivities(0,25); 
    $nbParPages = 25; 
    $nomberActivitites = $activities->size; 
    $aActivities = $activities->items; 
    $nbPages = floor ($nomberActivitites/$nbParPages); 
    $aEndurance = array("Running", "Cycling", "Mountain Biking", "Walking", "Hiking", "Downhill Skiing", "Cross-Country Skiing", "Snowboarding", "Skating","Wheelchair", "Rowing", "Elliptical", "Other"); 
    $aEnduranceUnderWater = array("Swimming"); 
    $enduranceOnEarthType = $em->getRepository('KsActivityBundle:SportType')->findOneByLabel("endurance"); 
    if (!is_object($enduranceOnEarthType)) { 
     echo "Impossible de trouver le type de sport d'endurance"; 
    } 
    $enduranceUnderWaterType = $em->getRepository('KsActivityBundle:SportType')->findOneByLabel("endurance_under_water"); 
    if (!is_object($enduranceUnderWaterType)) { 
     echo "Impossible de trouver le type de sport d'endurance sous l'eau "; 
    } 
    echo "Après avoir récupéré 25 activités : ".memory_get_usage()."\n"; 
    $a = 0; 
    for($i=0;$i<=$nbPages;$i++){ 
     if($i!=0){ 
     $activities = $rkApi->getFitnessActivities($i,25); 
     $aActivities = $activities->items; 
     } 
     foreach ($aActivities as $activity) { 
     $a = $a+1; 
     $codeSport = $this->formatNameSport($activity->type); 
     $sport = $em->getRepository('KsActivityBundle:Sport')->findOneByCodeSport($codeSport); 
     if (!is_object($sport)) { 
      $sport = new \Ks\ActivityBundle\Entity\Sport(); 
      $sport->setLabel($codeSport); 
      $sport->setCodeSport($codeSport); 
      $sport->setSportType($enduranceOnEarthType); 
      $em->persist($sport); 
      $em->flush(); 
     } 
     $activityDetail = json_decode($rkApi->requestJSONHealthGraph($activity->uri)); 
     if(in_array($activity->type, $aEndurance)){ 
      $urlActivitieDetail = $activityDetail->activity; 
      $ActivitySessionEnduranceOnEarth = new \Ks\ActivityBundle\Entity\ActivitySessionEnduranceOnEarth($user); 
      isset($activity->total_distance)? $ActivitySessionEnduranceOnEarth->setDistance($activity->total_distance) : ""; 
      isset($activity->duration)? $ActivitySessionEnduranceOnEarth->setDuration($this->secondesToTimeDuration($activity->duration)) : ""; 
      isset($activity->start_time)? $ActivitySessionEnduranceOnEarth->setIssuedAt(new \DateTime($activity->start_time)) : ""; 
      $ActivitySessionEnduranceOnEarth->setModifiedAt(new \DateTime('Now')); 
      $ActivitySessionEnduranceOnEarth->setSport($sport); 
      isset($activityDetail->total_calories)? $ActivitySessionEnduranceOnEarth->setCalories($activityDetail->total_calories) : ""; 
      isset($activityDetail->climb)? $ActivitySessionEnduranceOnEarth->setElevationGain($activityDetail->climb) : ""; 
      $maxElevation = 0; 
      $minElevation = 10000; 
      if(isset($activityDetail->path)){ 
      foreach($activityDetail->path as $gpsPoint){ 
       if($gpsPoint->altitude > $maxElevation){ 
       $maxElevation = $gpsPoint->altitude; 
       } 
       if($gpsPoint->altitude < $minElevation){ 
       $minElevation = $gpsPoint->altitude; 
       } 
      } 
      $ActivitySessionEnduranceOnEarth->setElevationMin($minElevation); 
      $ActivitySessionEnduranceOnEarth->setElevationMax($maxElevation); 
      } 
      $em->persist($ActivitySessionEnduranceOnEarth); 
      $em->flush(); 
      //Pour chaque activité on a un identifiant relatif au service qu'on synchronise 
      $ActivityComeFromService = new \Ks\ActivityBundle\Entity\ActivityComeFromService(); 
      $ActivityComeFromService->setActivity($ActivitySessionEnduranceOnEarth); 
      $ActivityComeFromService->setService($service); 
      $ActivityComeFromService->setIdWebsiteActivityService($activity->uri); 
      $ActivityComeFromService->setSourceDetailsActivity($rkApi->requestJSONHealthGraph($activity->uri)); 
      $ActivityComeFromService->setTypeSource("JSON"); 
      $em->persist($ActivityComeFromService); 
      $em->flush(); 
      echo "Import de l'activite num ".$a." type :".$activity->type." effectue avec success \n"; 
      unset($ActivitySessionEnduranceOnEarth); 
      unset($ActivityComeFromService); 
      echo "UnitOFWOrk -> ".$em->getUnitOfWork()->size()."\n"; 
     } 
     if(in_array($activity->type, $aEnduranceUnderWater)){ 
      $ActivitySessionEnduranceUnderWater = new \Ks\ActivityBundle\Entity\ActivitySessionEnduranceUnderWater($user); 
      isset($activity->total_distance)? $ActivitySessionEnduranceUnderWater->setDistance($activity->total_distance) : ""; 
      isset($activity->duration)? $ActivitySessionEnduranceUnderWater->setDuration($this->secondesToTimeDuration($activity->duration)) : ""; 
      isset($activity->start_time) && !empty($activity->start_time)? $ActivitySessionEnduranceUnderWater->setIssuedAt(new \DateTime($activity->start_time)) : ""; 
      $ActivitySessionEnduranceUnderWater->setModifiedAt(new \DateTime('Now')); 
      $ActivitySessionEnduranceUnderWater->setSport($sport); 
      isset($activityDetail->total_calories)? $ActivitySessionEnduranceUnderWater->setCalories($activityDetail->total_calories) : ""; 
      isset($activityDetail->notes)? $ActivitySessionEnduranceUnderWater->setDescription($activityDetail->notes) : ""; 
      $em->persist($ActivitySessionEnduranceUnderWater); 
      $em->flush(); 
      $ActivityComeFromService = new \Ks\ActivityBundle\Entity\ActivityComeFromService(); 
      $ActivityComeFromService->setActivity($ActivitySessionEnduranceUnderWater); 
      $ActivityComeFromService->setService($service); 
      $ActivityComeFromService->setIdWebsiteActivityService($activity->uri); 
      $ActivityComeFromService->setSourceDetailsActivity($rkApi->requestJSONHealthGraph($activity->uri)); 
      $ActivityComeFromService->setTypeSource("JSON"); 
      $em->persist($ActivityComeFromService); 
      $em->flush(); 
      echo "Import de l'activité num ".$a." type :".$activity->type." effectué avec succès\n"; 
      unset($ActivitySessionEnduranceUnderWater); 
      unset($ActivityComeFromService); 
     } 
     echo "Après chaque activité : ".memory_get_usage()."\n"; 
     unset($sport); 
     unset($activityDetail); 
     $em->clear(); 
     } 
    } 
    } catch (\Exception $e) { 
    throw $e; 
    } 
} 

おかげで、@AdrienBrault:

は、ここに私の完全なコードです。私は--env=prod --no-debugでテストしましたが、消費するメモリーは少なくて済みますが、メモリーはまだ増加しています。エンティティマネージャを本当にクリアするにはどうすればいいですか?メモリを安定させますか?エンティティマネージャをリセットする

+0

コマンドのメモリ使用量を減少させる最初のステップは、prod環境で、無効にデバッグでそれらを実行することです: 'PHPアプリ/コンソールコマンド--env = PROD --no-DEBUG' – AdrienBrault

+1

おかげで、@AdrienBrault私は--env = prod --no-debugでテストしましたが、メモリ消費は少なくてもメモリはまだ増加しています...どうすればエンティティマネージャをクリアすることができますか?メモリを安定させますか? – psylo66

+0

@Hosh、あなたが好きではない答えの下にコメントを追加して、なぜそれが良い解決策ではないのかを説明してみませんか?彼らはさらに助けやアドバイスを提供してくれるかもしれません。 – halfer

答えて

1

試してみてください。

$this->getContainer()->get('doctrine')->resetEntityManager(); 

、その後:

$em = $this->getContainer()->get('doctrine')->getEntityManager(); 
8

symfonyはDEV環境内のすべてのSQLクエリをログに記録しますので、最初のあなたはそれを

// disable logger 
$em->getConnection()->getConfiguration()->setSQLLogger(null); 
を無効にする必要があります

エンティティでイベントリスナーを使用することもできますが、メモリ使用量も増える可能性があります。あなたはそう

// remove all listeners 
foreach ($em->getEventManager()->getListeners() as $event => $listeners) { 
    foreach ($listeners as $listener) { 
     $em->getEventManager()->removeEventListener($event, $listener); 
    } 
} 

それらの必要はあなたのループのすべてのステップは、あなたの明確なエンティティマネージャとして、存在しない、あなたのコードからunsetを削除するようにそれらを無効にすることができます。

// save and clear 
$em->flush(); 
$em->getUnitOfWork()->clear(); 

教義があなたのクエリを最適化することができますことを忘れないでください、あなたのグループが1 flushに問い合わせる場合もパフォーマンスを向上させます。そのため、データの一部でflushを一度実行するのがベストプラクティスです。たとえば、次のように

// collect 100 entities and then save them 
if (($i % 100) == 0) { 
    $em->flush(); 
    $em->getUnitOfWork()->clear(); 
} 
+0

これはまさに解決策ではありません。しかし、あなたのソリューションは私を解決に導いてくれました。それは、私がオーバーライドしていたイベントリスナーの1つ(このクラスのパラメータをオーバーロードすることによって)がこれを引き起こした何かをしていたことがわかります。その定義を削除して(そして手動でそのイベントリスナーを削除するだけで)この問題は解決されました。私はあなたに恩恵を与えるでしょう。 –

+0

「未定義インデックス」はどのように取り除かれましたか?そして、あなたが話した拡張ソリューションのコードを書くことができますか? –

関連する問題