There is a memory leak in the current version of symfony’s sfLucenePlugin. I found when I tried to do a data import, I kept getting the following error message:

Fatal error: require_once(): Failed opening required ‘Zend/Search/Lucene/Exception.php’ (include_path=’.:/php:/plugins/sfLucenePlugin/lib/vendor’) in /plugins/sfLucenePlugin/lib/vendor/Zend/Search/Lucene/Storage/File/Filesystem.php on line 66

Rebuilding the index fixed this issue, but the daunting possibility of an import task breaking my site is something I cannot live with. Because using ./symfony lucene:rebuild frontend is such a reliable method (and does not seem to have any memory leaks), I would try bypassing my models from indexing when they saved, and just run the rebuild task at the end of my import. So I extended the sfLuceneDoctrineTemplate and sfLuceneDoctrineListener classes to the two classes below, and implemented a “saveNoIndex” function on the template, to allow me to save my objects without indexing them. Check it out:


// /path/to/project/lib/behavior/myLuceneDoctrineTemplate.class.php
class myLuceneDoctrineTemplate extends sfLuceneDoctrineTemplate
{
  protected $_no_index = false;
  /**
   * setTableDefinition
   *
   * @return void
   */
  public function setTableDefinition()
  {
    $this->addListener(new myLuceneDoctrineListener);
  }

  public function saveNoIndex()
  {
    $this->_no_index = true;
    $this->getInvoker()->save();
  }

  public function doIndex()
  {
    return ($this->_no_index === false);
  }
}

// /path/to/project/lib/behavior/myLuceneDoctrineListener.class.php
class myLuceneDoctrineListener extends sfLuceneDoctrineListener
{
   /**
   * Executes save routine
   */
  public function postSave(Doctrine_Event $event)
  {
    if ($event->getInvoker()->doIndex())
    {
      try {
        $this->saveIndex($event->getInvoker());
      } catch(sfException $e) {
        // no context define, cannot do anything,
      }
    }
  }
}

Once you finish your import task, you can either run the lucene rebuild task from the CLI, via cron job, or have your own symfony task run it from within the task. Mine looks like this:


class mySymfonyImportTask extends sfBaseTask
{
  // configure()
  ...
  protected function execute($arguments = array(), $options = array())
  {
    ...
    // finally, rebuild the indexes
    $this->runLuceneRebuild();
    $this->logSection('import', "Completed.  Added $count new item(s)");
  }

  public function runLuceneRebuild()
  {
    $this->logSection('import', "running lucene cleanup task");
    $luceneTask = new sfLuceneRebuildTask($this->dispatcher, $this->formatter);
    $luceneTask->run(array('application' => 'frontend'), array());
  }
}

Add comment