diff -urN apachesolr.orig/apachesolr.admin.inc apachesolr/apachesolr.admin.inc
--- apachesolr.orig/apachesolr.admin.inc 2009-12-27 18:29:34.000000000 +0100
+++ apachesolr/apachesolr.admin.inc 2010-02-07 16:55:35.000000000 +0100
@@ -22,27 +22,6 @@
}
}
- $form['apachesolr_host'] = array(
- '#type' => 'textfield',
- '#title' => t('Solr host name'),
- '#default_value' => variable_get('apachesolr_host', 'localhost'),
- '#description' => t('Host name of your Solr server, e.g. localhost or example.com.'),
- '#required' => TRUE,
- );
- $form['apachesolr_port'] = array(
- '#type' => 'textfield',
- '#title' => t('Solr port'),
- '#default_value' => variable_get('apachesolr_port', '8983'),
- '#description' => t('Port on which the Solr server listens. The Jetty example server is 8983, while Tomcat is 8080 by default.'),
- '#required' => TRUE,
- );
- $form['apachesolr_path'] = array(
- '#type' => 'textfield',
- '#title' => t('Solr path'),
- '#default_value' => variable_get('apachesolr_path', '/solr'),
- '#description' => t('Path that identifies the Solr request handler to be used.'),
- );
-
$numbers = drupal_map_assoc(array(1, 5, 10, 20, 50, 100, 200));
$form['apachesolr_cron_limit'] = array(
'#type' => 'select',
@@ -118,6 +97,298 @@
}
/**
+ * Callback for configuring Solr servers.
+ */
+function apachesolr_settings_servers($server_id = NULL, $delete = NULL) {
+ $output = '';
+ $servers = variable_get('apachesolr_servers', array());
+ if ($delete == 'delete' && isset($servers[$server_id])) {
+ return drupal_get_form('apachesolr_settings_servers_delete', $server_id);
+ }
+ $server_id = is_null($server_id) ? 'add' : $server_id;
+ if (count($servers)) {
+ $header = array('#', t('Server name'), t('Host name'), t('Port'), t('Path'), t('Query server'), t('Index server'), t('Status'), array('data' => t('Operations'), 'colspan' => 2));
+ $rows = array();
+ foreach ($servers as $delta => $server) {
+ $ping = FALSE;
+ try {
+ $solr = apachesolr_get_server($server['host'], $server['port'], $server['path']);
+ $ping = @$solr->ping(variable_get('apachesolr_ping_timeout', 4));
+ // If there is no $solr object, there is no server available, so don't continue.
+ if (!$ping) {
+ throw new Exception(t('No Solr instance available for !host:!port/!path', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'))));
+ }
+ }
+ catch (Exception $e) {
+ watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+ }
+
+ $row = array($delta, $server['name'], $server['host'], $server['port'], $server['path'], $server['query'] ? t('Yes') : t('No'), $server['index'] ? t('Yes') : t('No'), $ping ? t('Successfully contacted') : t('Cannot be contacted!'), l(t('modify'), 'admin/settings/apachesolr/servers/'. $delta), l(t('delete'), 'admin/settings/apachesolr/servers/'. $delta .'/delete'));
+ if ($server_id == (string)$delta) {
+ $rows[] = array(
+ 'data' => $row,
+ 'class' => 'apachesolr-servers-active-server',
+ );
+ }
+ else {
+ $rows[] = $row;
+ }
+ }
+ $element = array(
+ '#type' => 'fieldset',
+ '#title' => t('Solr servers currentlly configured on this site'),
+ '#collapsible' => TRUE,
+ '#collpased' => FALSE,
+ '#value' => theme('table', $header, $rows) . ($server_id != 'add' ? '
'. print_r(array($server), TRUE) .''); + + // @todo: Delete old variables + // variable_del('apachesolr_host'); + // variable_del('apachesolr_port'); + // variable_del('apachesolr_path'); + // $ret[] = array('success' => TRUE, 'query' => 'Drupal variables: "apachesolr_host", "apachesolr_port", "apachesolr_path" were deleted.'); + + return $ret; +} diff -urN apachesolr.orig/apachesolr.module apachesolr/apachesolr.module --- apachesolr.orig/apachesolr.module 2010-01-02 15:42:58.000000000 +0100 +++ apachesolr/apachesolr.module 2010-02-07 17:07:24.000000000 +0100 @@ -9,6 +9,11 @@ define('APACHESOLR_READ_WRITE', 0); define('APACHESOLR_READ_ONLY', 1); +define('APACHESOLR_BALANCING_RANDOM', 1); +define('APACHESOLR_BALANCING_TESTDELAYED', 2); +define('APACHESOLR_BALANCING_TESTALWAYS', 3); +define('APACHESOLR_BALANCING_SELECTSERVER', 4); + /** * Implementation of hook_menu(). */ @@ -30,6 +35,30 @@ 'file' => 'apachesolr.admin.inc', 'type' => MENU_DEFAULT_LOCAL_TASK, ); + $items['admin/settings/apachesolr/servers'] = array( + 'title' => 'Solr Servers', + 'page callback' => 'apachesolr_settings_servers', + 'weight' => -9, + 'access arguments' => array('administer search'), + 'file' => 'apachesolr.admin.inc', + 'type' => MENU_LOCAL_TASK, + ); + $items['admin/settings/apachesolr/servers/%apachesolr_server'] = array( + 'title' => 'Solr Servers', + 'page callback' => 'apachesolr_settings_servers', + 'page arguments' => array(4), + 'access arguments' => array('administer search'), + 'file' => 'apachesolr.admin.inc', + 'type' => MENU_CALLBACK, + ); + $items['admin/settings/apachesolr/servers/%apachesolr_server/delete'] = array( + 'title' => 'Solr Servers', + 'page callback' => 'apachesolr_settings_servers', + 'page arguments' => array(4, 'delete'), + 'access arguments' => array('administer search'), + 'file' => 'apachesolr.admin.inc', + 'type' => MENU_CALLBACK, + ); $items['admin/settings/apachesolr/enabled-filters'] = array( 'title' => 'Enabled filters', 'page callback' => 'drupal_get_form', @@ -100,6 +129,20 @@ } /** + * Menu loader for %apachesolr_server. + * + * @param $server_id + * Argument passed. + * + * @return + * The server informations ID of FALSE. + */ +function apachesolr_server_load($server_id = NULL) { + $servers = variable_get('apachesolr_servers', array()); + return isset($servers[$server_id]) ? $servers[$server_id] : FALSE; +} + +/** * Determines Apache Solr's behavior when searching causes an exception (e.g. Solr isn't available.) * Depending on the admin settings, possibly redirect to Drupal's core search. * @@ -1311,25 +1354,158 @@ return $_searched; } -/** - * Factory method for solr singleton object. Structure allows for an arbitrary - * number of solr objects to be used based on the host, port, path combination. - * Get an instance like this: - * $solr = apachesolr_get_solr(); - */ -function apachesolr_get_solr($host = NULL, $port = NULL, $path = NULL) { - static $solr_cache; - - if (empty($host)) { - $host = variable_get('apachesolr_host', 'localhost'); +/* + * Internal method to get a solr server instance. This is exported to its own + * function so we could check servers availability through cron. + * + * This method does heavy ping() usage which implies numerous and useless + * HTTP request in most cases. That's why this method should be externalized. + * + * TODO: note that using this implementation, the Balancer override is almost + * useless, this could be improved. + * + * @param boolean $readonly + * If set to TRUE, fetch a reader, if set to FALSE, fectch a writer + * @param boolean $random = FALSE + * Set to TRUE to fetch a service randomly without ping + * + * @return Drupal_Apache_Solr_Service + * Return a configured on site service instance ready to do queries + */ +function _apachesolr_get_solr($readonly, $random = FALSE) { + static $loadbalancer; + + // Load includes and instanciate balancer if we need it. + if (!isset($loadbalancer)) { + include_once(drupal_get_path('module', 'apachesolr') . '/Drupal_Apache_Solr_Service_Balancer.php'); + try { + $loadbalancer = new Drupal_Apache_Solr_Service_Balancer(); + } + catch (Exception $e) { + watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR); + return; + } } - if (empty($port)) { - $port = variable_get('apachesolr_port', '8983'); + + // Get available servers (event if in case we are going through + // apachesolr_get_solr() this variable already been loaded this won't hurt + // because all variables are statically cached. + $servers = variable_get('apachesolr_servers', array()); + + // Go through services to load them all (matching to the $readonly + // parameter). + foreach ($servers as $delta => $server) { + try { + if ($readonly && $server['query']) { + $loadbalancer->addReadService(apachesolr_get_server($server['host'], $server['port'], $server['path'])); + } + else if (!$readonly && $server['index']) { + $loadbalancer->addReadService(apachesolr_get_server($server['host'], $server['port'], $server['path'])); + } + } + catch (Exception $e) { + watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR); + } } - if (empty($path)) { - $path = variable_get('apachesolr_path', '/solr'); + + return $readonly ? $loadbalancer->getReadService($random) : $loadbalancer->getWriteService($random); +} + +/** + * Method used to obtain the Solr load balancer object + * + * TODO: + * This function gets quite big, and should be splitted to more specific + * functions for a more coherent code. + * It should always begin with: + * - Do I have a static value ? Yes -> return it. + * - What is my configured balancing mode ? -> Specific function + * - This specific function return -> set static value and returns it. + */ +function apachesolr_get_solr($readonly = TRUE) { + static $reader, $writer; + + if (($readonly && !isset($reader)) || (!$readonly && !isset($writer)) ) { + + switch (variable_get('apachesolr_balancing_mode', APACHESOLR_BALANCING_TESTALWAYS)) { + case APACHESOLR_BALANCING_RANDOM: + case APACHESOLR_BALANCING_TESTALWAYS: + // Wipe out cache here ensure we get to _apachesolr_get_solr() function. + variable_set('apachesolr_current_reader', 0); + variable_set('apachesolr_current_writer', 0); + break; + + case APACHESOLR_BALANCING_TESTDELAYED: + // Ensure to refresh our cache each N (configured) hitcounts + $hits = variable_get('apachesolr_balancing_hitcount', 0); + if (! $hits) { + variable_set('apachesolr_current_reader', 0); + variable_set('apachesolr_current_writer', 0); + // This remains a hidden variable, that may be configure throught + // settings.php. TODO: needs documentation. + variable_set('apachesolr_balancing_hitcount', variable_get('apachesolr_balancing_hitcount_max', 50)); + } + else { + $hits--; + variable_set('apachesolr_balancing_hitcount', $hits); + } + break; + } + + // Get available servers + $servers = variable_get('apachesolr_servers', array()); + + // We use only one Solr server (this may be the main usage of this module) + // Do not load the entire Balancer API use only a single Solr object. + if (count($servers) == 1) { + $writer = apachesolr_get_server($servers[0]['host'], $servers[0]['port'], $servers[0]['path']); + $reader = $writer; + } + + // Asked for a reader, hope then the PHP runtime won't ask for a writer + // unfortunately this is difficult to predict. + else if ($readonly && $delta = variable_get('apachesolr_current_reader', FALSE)) { + $server = &$servers[$delta]; + $reader = apachesolr_get_server($server['host'], $server['port'], $server['path']); + } + + // Asked for a writer. + else if (!$readonly && $delta = variable_get('apachesolr_current_writer', FALSE)) { + $server = &$servers[$delta]; + $writer = apachesolr_get_server($server['host'], $server['port'], $server['path']); + // In case we set the writer, set also the reader on the same instance + // to ensure we are going to modify and read on the same instance. + // TODO: this might be wrong, correct me if it is. + $reader = $writer; + } + + // None variable set, we are going to run the load balancer implementation + // to find one available server. Note that this will do a lot of HTTP SolR + // ping requests so it can be quite heavy. + else { + if ($readonly) { + $reader = _apachesolr_get_solr($readonly); + } + else { + // We won't load writer and reader together, because in most cases + // clients will hit to do search requests, only cron (in most cases) + // will ask for a writer. + $writer = _apachesolr_get_solr($readonly); + // See comment upper. + $reader = $writer; + } + } } + return $readonly ? $reader : $writer; +} + +/** + * Method used to obtain a single Solr object. A clone of old apachesolr_get_solr() function but arguments are mandatory. + */ +function apachesolr_get_server($host, $port, $path) { + static $solr_cache; + if (empty($solr_cache[$host][$port][$path])) { list($module, $filepath, $class) = variable_get('apachesolr_service_class', array('apachesolr', 'Drupal_Apache_Solr_Service.php', 'Drupal_Apache_Solr_Service')); include_once(drupal_get_path('module', $module) .'/'. $filepath); diff -urN apachesolr.orig/Drupal_Apache_Solr_Service_Balancer.php apachesolr/Drupal_Apache_Solr_Service_Balancer.php --- apachesolr.orig/Drupal_Apache_Solr_Service_Balancer.php 1970-01-01 01:00:00.000000000 +0100 +++ apachesolr/Drupal_Apache_Solr_Service_Balancer.php 2010-02-07 17:05:55.000000000 +0100 @@ -0,0 +1,135 @@ +_selectReadService(); + } + + /** + * Get found write service for current PHP script execution. + * + * @param boolean $random = FALSE + * Set to TRUE to get a random server without ping. + * + * @return Drupal_Apache_Solr_Service + * Service instance + */ + public function getWriteService($random = FALSE) { + // TODO implement random + return $this->_selectWriteService(); + } + + /** + * Get summary information about the first index servive found. + */ + public function getStatsSummary() { + $service = $this->_selectWriteService(); + + do { + try { + return $service->getStatsSummary(); + } + catch (Exception $e) { + if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR + throw $e; + } + } + $service = $this->_selectWriteService(TRUE); + } while ($service); + + return FALSE; + } + + /** + * Clear cached Solr data. + */ + public function clearCache() { + foreach ($this->_readableServices as $service) { + @$service->clearCache; + } + foreach ($this->_writableServices as $service) { + @$service->clearCache; + } + } + + /** + * Get meta-data about the index. + */ + public function getLuke($num_terms = 0) { + $service = $this->_selectWriteService(); + + do { + try { + return $service->getLuke($num_terms); + } + catch (Exception $e) { + if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR + throw $e; + } + } + $service = $this->_selectWriteService(TRUE); + } while ($service); + + return FALSE; + } + /** + * Get just the field meta-data about the index. + */ + public function getFields($num_terms = 0) { + return $this->getLuke($num_terms)->fields; + } + + /** + * For some reasons this was not implemented in Balancer. + */ + public function deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600) { + $service = $this->_selectWriteService(); + + do { + try { + return $service->deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600); + } + catch (Exception $e) { + if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR + throw $e; + } + } + $service = $this->_selectWriteService(TRUE); + } while ($service); + + return FALSE; + } + + /** + * Check if an index or query server is vailable. + */ + public function ping($timeout = 2, $service_type) { + $service = $service_type == 'index' ? $this->_selectWriteService() : $this->_selectReadService(); + + do { + try { + return $service->ping($timeout); + } + catch (Exception $e) { + if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR + throw $e; + } + } + $service = $service_type == 'index' ? $this->_selectWriteService(TRUE) : $this->_selectReadService(TRUE); + } while ($service); + + return FALSE; + } +} diff -urN apachesolr.orig/Drupal_Apache_Solr_Service.php apachesolr/Drupal_Apache_Solr_Service.php --- apachesolr.orig/Drupal_Apache_Solr_Service.php 2009-12-26 18:12:12.000000000 +0100 +++ apachesolr/Drupal_Apache_Solr_Service.php 2010-02-07 15:46:15.000000000 +0100 @@ -33,7 +33,8 @@ * @return * (float) seconds taken to ping the server, FALSE if timeout occurs. */ - public function ping($timeout = 2) { + public function ping() { + $timeout = func_get_arg(0); $start = microtime(TRUE); if ($timeout <= 0.0) {