Commit 154dec54 authored by Bas de Nooijer's avatar Bas de Nooijer

Merge branch 'feature/parallel-queries' into develop

parents d553c099 eb6ea670
<?php
require('init.php');
htmlHeader();
// create a client instance and autoload the customize request plugin
$client = new Solarium_Client($config);
$parallel = $client->getPlugin('parallelexecution');
// create two queries to execute in an array. Keys are important for fetching the results later!
$queries = array(
'instock' => $client->createSelect()->setQuery('inStock:true'),
'lowprice' => $client->createSelect()->setQuery('price:[1 TO 300]'),
);
// first execute the queries the normal way and time it
$start = microtime(true);
$client->execute($queries['instock']);
$client->execute($queries['lowprice']);
echo 'Execution time for normal "serial" execution of two queries: ' . round(microtime(true)-$start, 3);
echo '<hr/>';
// now execute the two queries parallel and time it
$start = microtime(true);
$results = $parallel->execute($queries);
echo 'Execution time for parallel execution of two queries: ' . round(microtime(true)-$start, 3);
htmlFooter();
// Note: for this example on a default Solr index (with a tiny index) the performance gain is minimal to none.
// With a bigger dataset, more complex queries or multiple solr instances the performance gain is much more.
// For testing you can use a Solr delay component (see https://github.com/basdenooijer/raspberry-solr-plugins) to
// artificially slow Solr down by an exact amount of time.
\ No newline at end of file
...@@ -120,7 +120,8 @@ ...@@ -120,7 +120,8 @@
<ul style="list-style:none;"> <ul style="list-style:none;">
<li><a href="7.1-plugin-loadbalancer.php">7.1 Loadbalancer</a></li> <li><a href="7.1-plugin-loadbalancer.php">7.1 Loadbalancer</a></li>
<li><a href="7.2-plugin-postbigrequest.php">7.2 Post Big Requests</a></li> <li><a href="7.2-plugin-postbigrequest.php">7.2 Post Big Requests</a></li>
<li><a href="7.3-plugin-customizerequest.php">7.3 customize Requests</a></li> <li><a href="7.3-plugin-customizerequest.php">7.3 Customize Requests</a></li>
<li><a href="7.4-plugin-parallelexecution.php">7.4 Parallel Execution</a></li>
</ul> </ul>
</ul> </ul>
......
...@@ -153,6 +153,7 @@ class Solarium_Client extends Solarium_Configurable ...@@ -153,6 +153,7 @@ class Solarium_Client extends Solarium_Configurable
'loadbalancer' => 'Solarium_Plugin_Loadbalancer', 'loadbalancer' => 'Solarium_Plugin_Loadbalancer',
'postbigrequest' => 'Solarium_Plugin_PostBigRequest', 'postbigrequest' => 'Solarium_Plugin_PostBigRequest',
'customizerequest' => 'Solarium_Plugin_CustomizeRequest', 'customizerequest' => 'Solarium_Plugin_CustomizeRequest',
'parallelexecution' => 'Solarium_Plugin_ParallelExecution',
); );
/** /**
...@@ -450,7 +451,7 @@ class Solarium_Client extends Solarium_Configurable ...@@ -450,7 +451,7 @@ class Solarium_Client extends Solarium_Configurable
* @param bool $resultOverride * @param bool $resultOverride
* @return void|mixed * @return void|mixed
*/ */
public function triggerEvent($event, $params, $resultOverride = false) public function triggerEvent($event, $params = array(), $resultOverride = false)
{ {
// Add namespacing // Add namespacing
$event = 'event'.$event; $event = 'event'.$event;
......
...@@ -70,9 +70,7 @@ class Solarium_Client_Adapter_Curl extends Solarium_Client_Adapter ...@@ -70,9 +70,7 @@ class Solarium_Client_Adapter_Curl extends Solarium_Client_Adapter
*/ */
public function execute($request) public function execute($request)
{ {
list($data, $headers) = $this->_getData($request); return $this->_getData($request);
$this->check($data, $headers);
return new Solarium_Client_Response($data, $headers);
} }
/** /**
...@@ -82,57 +80,77 @@ class Solarium_Client_Adapter_Curl extends Solarium_Client_Adapter ...@@ -82,57 +80,77 @@ class Solarium_Client_Adapter_Curl extends Solarium_Client_Adapter
* @return array * @return array
*/ */
protected function _getData($request) protected function _getData($request)
{
// @codeCoverageIgnoreStart
$handle = $this->createHandle($request);
$httpResponse = curl_exec($handle);
return $this->getResponse($handle, $httpResponse);
// @codeCoverageIgnoreEnd
}
public function getResponse($handle, $httpResponse)
{
// @codeCoverageIgnoreStart
if ($httpResponse !== false) {
$data = $httpResponse;
$info = curl_getinfo($handle);
$headers = array();
$headers[] = 'HTTP/1.1 ' . $info['http_code']. ' OK';
} else {
$headers = array();
$data = '';
}
curl_close($handle);
$this->check($data, $headers);
return new Solarium_Client_Response($data, $headers);
// @codeCoverageIgnoreEnd
}
/**
* Create curl handle for a request
*
* @param Solarium_Client_Request $request
* @return resource
*/
public function createHandle($request)
{ {
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
$uri = $this->getBaseUri() . $request->getUri(); $uri = $this->getBaseUri() . $request->getUri();
$method = $request->getMethod(); $method = $request->getMethod();
$options = $this->_createOptions($request); $options = $this->_createOptions($request);
$ch = curl_init(); $handler = curl_init();
curl_setopt($ch, CURLOPT_URL, $uri); curl_setopt($handler, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($handler, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($handler, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $options['timeout']); curl_setopt($handler, CURLOPT_TIMEOUT, $options['timeout']);
if (!isset($options['headers']['Content-Type'])) {
$options['headers']['Content-Type'] = 'text/xml; charset=utf-8';
}
if (!isset($options['headers']['Content-Type'])) { if (!isset($options['headers']['Content-Type'])) {
$options['headers']['Content-Type'] = 'text/xml; charset=utf-8'; $options['headers']['Content-Type'] = 'text/xml; charset=utf-8';
} }
if (count($options['headers'])) { if (count($options['headers'])) {
$arr = array(); $headers = array();
foreach ($options['headers'] as $k => $v) { foreach ($options['headers'] as $key => $value) {
$arr[] = $k . ": " . $v; $headers[] = $key . ": " . $value;
} }
curl_setopt($ch, CURLOPT_HTTPHEADER, $arr); curl_setopt($handler, CURLOPT_HTTPHEADER, $headers);
} }
if ($method == Solarium_Client_Request::METHOD_POST) { if ($method == Solarium_Client_Request::METHOD_POST) {
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($handler, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request->getRawData()); curl_setopt($handler, CURLOPT_POSTFIELDS, $request->getRawData());
$httpResponse = curl_exec($ch);
} else if ($method == Solarium_Client_Request::METHOD_GET) { } else if ($method == Solarium_Client_Request::METHOD_GET) {
curl_setopt($ch, CURLOPT_HTTPGET, true); curl_setopt($handler, CURLOPT_HTTPGET, true);
$httpResponse = curl_exec($ch);
} else if ($method == Solarium_Client_Request::METHOD_HEAD) { } else if ($method == Solarium_Client_Request::METHOD_HEAD) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD'); curl_setopt($handler, CURLOPT_CUSTOMREQUEST, 'HEAD');
$httpResponse = curl_exec($ch);
} else { } else {
throw new Solarium_Exception("unsupported method: $method"); throw new Solarium_Exception("unsupported method: $method");
} }
$headers = array(); $data = ''; return $handler;
if ($httpResponse !== false) {
$data = $httpResponse;
$info = curl_getinfo($ch);
$headers = array();
$headers[] = 'HTTP/1.1 ' . $info['http_code']. ' OK';
}
return array($data, $headers);
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
......
<?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.
*
* @copyright Copyright 2011 Bas de Nooijer <solarium@raspberry.nl>
* @license http://github.com/basdenooijer/solarium/raw/master/COPYING
* @link http://www.solarium-project.org/
*
* @package Solarium
*/
/**
* ParallelExecution plugin
*
* You can use this plugin to run multiple queries parallel. This functionality depends on the curl adapter so you
* do need to have curl available in your PHP environment.
*
* While query execution is parallel, the results only become available as soon as all requests have finished. So the
* time of the slowest query will be the effective execution time for all queries.
*
* @package Solarium
* @subpackage Plugin
*/
class Solarium_Plugin_ParallelExecution extends Solarium_Plugin_Abstract
{
/**
* 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
* the result array. You can mix all querytypes in the input array.
*
* @param array $queries
* @return array
*/
public function execute($queries)
{
// @codeCoverageIgnoreStart
$adapter = $this->_client->setAdapter('Solarium_Client_Adapter_Curl')->getAdapter();
// create handles and add all handles to the multihandle
$multiHandle = curl_multi_init();
$handles = array();
foreach($queries as $key => $query) {
$request = $this->_client->createRequest($query);
$handle = $adapter->createHandle($request);
curl_multi_add_handle($multiHandle, $handle);
$handles[$key] = $handle;
}
// executing multihandle (all requests)
$this->_client->triggerEvent('ParallelExecutionStart');
do {
curl_multi_exec($multiHandle, $running);
} while($running > 0);
$this->_client->triggerEvent('ParallelExecutionEnd');
// get the results
$results = array();
foreach ($handles as $key => $handle) {
try {
curl_multi_remove_handle($multiHandle, $handle);
$response = $adapter->getResponse($handle, curl_multi_getcontent($handle));
$results[$key] = $this->_client->createResult($queries[$key], $response);
} catch(Solarium_Client_HttpException $e) {
$results[$key] = $e;
}
}
curl_multi_close($multiHandle);
return $results;
// @codeCoverageIgnoreEnd
}
}
\ No newline at end of file
...@@ -76,8 +76,7 @@ class Solarium_Client_Adapter_CurlTest extends PHPUnit_Framework_TestCase ...@@ -76,8 +76,7 @@ class Solarium_Client_Adapter_CurlTest extends PHPUnit_Framework_TestCase
$response = $mock->execute($request); $response = $mock->execute($request);
$this->assertEquals($body,$response->getBody()); $this->assertEquals($data, $response);
$this->assertEquals($headers,$response->getHeaders());
} }
} }
\ 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