Commit c04a677b authored by Bas de Nooijer's avatar Bas de Nooijer

Fixed issues #74 and #75

parent 85b9cb72
...@@ -7,17 +7,28 @@ htmlHeader(); ...@@ -7,17 +7,28 @@ htmlHeader();
$client = new Solarium_Client($config); $client = new Solarium_Client($config);
$parallel = $client->getPlugin('parallelexecution'); $parallel = $client->getPlugin('parallelexecution');
// create two queries to execute in an array. Keys are important for fetching the results later! // Add a delay param to better show the effect, as an example Solr install with
$queries = array( // only a dozen documents is too fast for good testing
'instock' => $client->createSelect()->setQuery('inStock:true'), // This param only works with the correct Solr plugin,
'lowprice' => $client->createSelect()->setQuery('price:[1 TO 300]'), // see http://www.raspberry.nl/2012/01/04/solr-delay-component/
); // If you don't have to plugin the example still works, just without the delay.
$customizer = $client->getPlugin('customizerequest');
$customizer->createCustomization(array(
'key' => 'delay',
'type' => 'param',
'name' => 'delay',
'value' => '500',
'persistent' => true
));
// create two queries to execute in an array. Keys are important for fetching the results later!
$queryInstock = $client->createSelect()->setQuery('inStock:true');
$queryLowprice = $client->createSelect()->setQuery('price:[1 TO 300]');
// first execute the queries the normal way and time it // first execute the queries the normal way and time it
$start = microtime(true); $start = microtime(true);
$client->execute($queries['instock']); $client->execute($queryInstock);
$client->execute($queries['lowprice']); $client->execute($queryLowprice);
echo 'Execution time for normal "serial" execution of two queries: ' . round(microtime(true)-$start, 3); echo 'Execution time for normal "serial" execution of two queries: ' . round(microtime(true)-$start, 3);
...@@ -26,13 +37,15 @@ echo '<hr/>'; ...@@ -26,13 +37,15 @@ echo '<hr/>';
// now execute the two queries parallel and time it // now execute the two queries parallel and time it
$start = microtime(true); $start = microtime(true);
$results = $parallel->execute($queries); $parallel->addQuery('instock', $queryInstock);
$parallel->addQuery('lowprice', $queryLowprice);
$results = $parallel->execute();
echo 'Execution time for parallel execution of two queries: ' . round(microtime(true)-$start, 3); echo 'Execution time for parallel execution of two queries: ' . round(microtime(true)-$start, 3);
htmlFooter(); htmlFooter();
// Note: for this example on a default Solr index (with a tiny index) the performance gain is minimal to none. // Note: for this example on a default Solr index (with a tiny index) running on localhost the performance gain is
// With a bigger dataset, more complex queries or multiple solr instances the performance gain is much more. // minimal to none, sometimes even slightly slower!
// For testing you can use a Solr delay component (see https://github.com/basdenooijer/raspberry-solr-plugins) to // In a realworld scenario with network latency, a bigger dataset, more complex queries or multiple solr instances the
// artificially slow Solr down by an exact amount of time. // performance gain is much more.
\ No newline at end of file \ No newline at end of file
...@@ -48,28 +48,91 @@ ...@@ -48,28 +48,91 @@
* @subpackage Plugin * @subpackage Plugin
*/ */
// @codeCoverageIgnoreStart
class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract
{ {
/**
* Default options
*
* @var array
*/
protected $_options = array(
'curlmultiselecttimeout' => 0.1,
);
/**
* Queries (and optionally clients) to execute
*
* @var array
*/
protected $_queries = array();
/**
* Add a query to execute
*
* @param string $key
* @param Solarium_Query $query
* @param null|Solarium_Client $client
* @return Solarium_Plugin_ParallelExecution
*/
public function addQuery($key, $query, $client = null)
{
if($client == null) $client = $this->_client;
$this->_queries[$key] = array(
'query' => $query,
'client' => $client,
);
return $this;
}
/**
* Get queries (and coupled client instances)
*
* @return array
*/
public function getQueries()
{
return $this->_queries;
}
/**
* Clear all queries
*
* @return self Provides fluent interface
*/
public function clearQueries() {
$this->_queries = array();
return $this;
}
// @codeCoverageIgnoreStart
/** /**
* Execute queries parallel * Execute queries parallel
* *
* Use an array of Solarium_Query objects as input. The keys of the array are important, as they are also used in * Use an array of Solarium_Query objects as input. The keys of the array are important, as they are also used in
* the result array. You can mix all querytypes in the input array. * the result array. You can mix all querytypes in the input array.
* *
* @param array $queries * @param array $queries (deprecated, use addQuery instead)
* @return array * @return array
*/ */
public function execute($queries) public function execute($queries = null)
{ {
$adapter = $this->_client->setAdapter('Solarium_Client_Adapter_Curl')->getAdapter(); // this is for backwards compatibility
if(is_array($queries)) {
foreach ($queries as $key => $query) {
$this->addQuery($key, $query);
}
}
// create handles and add all handles to the multihandle // create handles and add all handles to the multihandle
$multiHandle = curl_multi_init(); $multiHandle = curl_multi_init();
$handles = array(); $handles = array();
foreach ($queries as $key => $query) { foreach ($this->_queries as $key => $data) {
$request = $this->_client->createRequest($query); $request = $this->_client->createRequest($data['query']);
$adapter = $data['client']->setAdapter('Solarium_Client_Adapter_Curl')->getAdapter();
$handle = $adapter->createHandle($request); $handle = $adapter->createHandle($request);
curl_multi_add_handle($multiHandle, $handle); curl_multi_add_handle($multiHandle, $handle);
$handles[$key] = $handle; $handles[$key] = $handle;
...@@ -77,9 +140,20 @@ class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract ...@@ -77,9 +140,20 @@ class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract
// executing multihandle (all requests) // executing multihandle (all requests)
$this->_client->triggerEvent('ParallelExecutionStart'); $this->_client->triggerEvent('ParallelExecutionStart');
do { do {
curl_multi_exec($multiHandle, $running); $mrc = curl_multi_exec($multiHandle, $active);
} while($running > 0); } while ($mrc == CURLM_CALL_MULTI_PERFORM);
$timeout = $this->getOption('curlmultiselecttimeout');
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($multiHandle, $timeout) != -1) {
do {
$mrc = curl_multi_exec($multiHandle, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
$this->_client->triggerEvent('ParallelExecutionEnd'); $this->_client->triggerEvent('ParallelExecutionEnd');
// get the results // get the results
...@@ -88,7 +162,7 @@ class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract ...@@ -88,7 +162,7 @@ class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract
try { try {
curl_multi_remove_handle($multiHandle, $handle); curl_multi_remove_handle($multiHandle, $handle);
$response = $adapter->getResponse($handle, curl_multi_getcontent($handle)); $response = $adapter->getResponse($handle, curl_multi_getcontent($handle));
$results[$key] = $this->_client->createResult($queries[$key], $response); $results[$key] = $this->_client->createResult($this->_queries[$key]['query'], $response);
} catch(Solarium_Client_HttpException $e) { } catch(Solarium_Client_HttpException $e) {
$results[$key] = $e; $results[$key] = $e;
} }
...@@ -99,5 +173,5 @@ class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract ...@@ -99,5 +173,5 @@ class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract
return $results; return $results;
} }
// @codeCoverageIgnoreEnd
} }
// @codeCoverageIgnoreEnd
\ No newline at end of file
<?php
/**
* Copyright 2011 Bas de Nooijer. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this listof conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are
* those of the authors and should not be interpreted as representing official
* policies, either expressed or implied, of the copyright holder.
*/
class Solarium_Plugin_ParallelExecutionTest extends PHPUnit_Framework_TestCase
{
/**
* @var Solarium_Plugin_ParallelExecution
*/
protected $_plugin;
public function setUp()
{
$this->_plugin = new Solarium_Plugin_ParallelExecution();
}
public function testAddAndGetQueries()
{
$client1 = new Solarium_Client();
$client2 = new Solarium_Client(array(
'adapter' => 'MyAdapter',
'adapteroptions' => array(
'host' => 'myhost',
)
)
);
$this->_plugin->init($client1, array());
$query1 = $client1->createSelect()->setQuery('test1');
$query2 = $client1->createSelect()->setQuery('test2');
$this->_plugin->addQuery(1, $query1);
$this->_plugin->addQuery(2, $query2, $client2);
$this->assertEquals(
array(
1 => array('query' => $query1, 'client' => $client1),
2 => array('query' => $query2, 'client' => $client2),
),
$this->_plugin->getQueries()
);
}
public function testClearQueries()
{
$client = new Solarium_Client();
$this->_plugin->init($client, array());
$query1 = $client->createSelect()->setQuery('test1');
$query2 = $client->createSelect()->setQuery('test2');
$this->_plugin->addQuery(1, $query1);
$this->_plugin->addQuery(2, $query2);
$this->_plugin->clearQueries();
$this->assertEquals(
array(),
$this->_plugin->getQueries()
);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment