diff --git a/includes/rules.core.inc b/includes/rules.core.inc
index 559d1cf..bf45b39 100644
--- a/includes/rules.core.inc
+++ b/includes/rules.core.inc
@@ -339,7 +339,14 @@ abstract class RulesExtendable extends FacesExtendable {
     if (isset($this->cache[$this->hook][$this->itemName])) {
       $this->itemInfo = &$this->cache[$this->hook][$this->itemName];
     }
-    // Set up the Faces Extenders
+    else {
+      $this->cache += rules_fetch_data($this->hook);
+      if (isset($this->cache[$this->hook][$this->itemName])) {
+        $this->itemInfo = &$this->cache[$this->hook][$this->itemName];
+      }
+    }
+
+    // Set up the Faces Extenders.
     if (!empty($this->itemInfo['faces_cache'])) {
       list($this->facesMethods, $this->facesIncludes, $this->faces) = $this->itemInfo['faces_cache'];
     }
@@ -1064,11 +1071,32 @@ abstract class RulesPlugin extends RulesExtendable {
   }
 
   /**
+   * Returns the caching id for this instance.
+   *
+   * @return string
+   *   Caching id.
+   */
+  public function getCacheId(){
+    $plugin_info = $this->pluginInfo();
+    if (!empty($plugin_info['component'])) {
+      return 'comp_' . $this->name;
+    }
+    elseif ($this instanceof RulesReactionRule) {
+      return 'event_' . $this->name;
+    }
+    return $plugin_info['class'] . '_' . $this->name;
+  }
+
+  /**
    * Saves the configuration to the database, regardless whether this is invoked
    * on the rules configuration or a contained rule element.
    */
   public function save($name = NULL, $module = 'rules') {
     if (isset($this->parent)) {
+      // Clear cache of this child element if this isn't a rebuild.
+      if (empty($this->is_rebuild)) {
+        cache_clear_all($this->getCacheId(), 'cache_rules', TRUE);
+      }
       $this->parent->sortChildren();
       return $this->parent->save($name, $module);
     }
@@ -1103,6 +1131,12 @@ abstract class RulesPlugin extends RulesExtendable {
       }
       $this->ensureNameExists();
       $this->data = $this;
+      // Load the stored entity, if any, to use it for variable comparison in
+      // the cache clearing logic below.
+      if (!isset($this->original) && !empty($this->id)) {
+        $this->original = entity_load_unchanged('rules_config', $this->id);
+      }
+      $original = isset($this->original) ? $this->original : NULL;
       $return = entity_get_controller('rules_config')->save($this);
       unset($this->data);
 
@@ -1113,21 +1147,30 @@ abstract class RulesPlugin extends RulesExtendable {
       else {
         $plugin_info = $this->pluginInfo();
         if (!empty($plugin_info['component'])) {
-          // When component variables changes rebuild the complete cache so the
-          // changes to the provided action/condition take affect.
-          if (empty($this->original) || $this->componentVariables() != $this->original->componentVariables()) {
+          // When it's an existing component and the variables were changed
+          // rebuild the whole cache. If it's a new component it will be built
+          // on demand.
+          if (!empty($original) && $this->componentVariables() != $original->componentVariables()) {
             rules_clear_cache();
           }
-          // Clear components cached for evaluation.
-          cache_clear_all('comp_', 'cache_rules', TRUE);
+          else {
+            _rules_rebuild_component_cache($this->getCacheId());
+          }
         }
         elseif ($this->plugin == 'reaction rule') {
-          // Clear event sets cached for evaluation.
-          cache_clear_all('event_', 'cache_rules', TRUE);
-          // Clear event whitelist for rebuild.
-          cache_clear_all('rules_event_whitelist', 'cache_rules', TRUE);
+          // Rebuild event whitelist.
+          rules_is_event_configured(NULL, TRUE);
+          $events_to_rebuild = $this->events();
+          // Make sure any remove event is updated too.
+          if (!empty($original)) {
+            $events_to_rebuild += array_diff($original->events, $events_to_rebuild);
+          }
+          // Rebuild the event sets cached for evaluation.
+          foreach ($events_to_rebuild as $event_name) {
+            RulesEventSet::rebuildEventCache('event_' . $event_name);
+          }
+          cache_clear_all($this->getCacheId(), 'cache_rules', TRUE);
         }
-        drupal_static_reset('rules_get_cache');
         drupal_static_reset('rules_config_update_dirty_flag');
       }
 
@@ -1469,6 +1512,15 @@ abstract class RulesAbstractPlugin extends RulesPlugin {
 
   protected function setUp() {
     parent::setUp();
+
+    // If the info isn't cached and it's a component try to build it.
+    if (!isset($this->cache[$this->itemName . '_info'][$this->elementName]) && strpos($this->elementName, 'component_') === 0) {
+      if ($rules_config = rules_config_load(substr($this->elementName, 10))) {
+        // Ensure the action info / condition info cache is updated.
+        _rules_rebuild_component_cache($rules_config->getCacheId());
+      }
+    }
+
     if (isset($this->cache[$this->itemName . '_info'][$this->elementName])) {
       $this->info = $this->cache[$this->itemName . '_info'][$this->elementName];
       // Remember that the info has been correctly setup.
diff --git a/includes/rules.plugins.inc b/includes/rules.plugins.inc
index 90b306d..85913b4 100644
--- a/includes/rules.plugins.inc
+++ b/includes/rules.plugins.inc
@@ -756,18 +756,30 @@ class RulesEventSet extends RulesRuleSet {
    * @see rules_get_cache()
    * @see rules_invoke_event()
    */
-  public static function rebuildEventCache() {
+  public static function rebuildEventCache($cid = NULL) {
     // Set up the per-event cache.
+    // Fetch all event information.
     $events = rules_fetch_data('event_info');
+
+    // Define the conditions that apply to fetch the event rules.
+    $conditions = array('plugin' => 'reaction rule', 'active' => TRUE);
+
+    // If a specific cid was requested just fetch the rules that are triggered
+    // by this event.
+    $cache_event_name = str_replace('event_', '', $cid);
+    if (!empty($cid) && isset($events[$cache_event_name])) {
+      $conditions['event'] = $cache_event_name;
+    }
+
     $sets = array();
     // Add all rules associated with this event to an EventSet for caching.
-    $rules = rules_config_load_multiple(FALSE, array('plugin' => 'reaction rule', 'active' => TRUE));
+    $rules = rules_config_load_multiple(FALSE, $conditions);
 
     foreach ($rules as $name => $rule) {
       foreach ($rule->events() as $event_name) {
         $event_base_name = rules_get_event_base_name($event_name);
-        // Skip not defined events.
-        if (empty($events[$event_base_name])) {
+        // Skip not defined or not handled events.
+        if (empty($events[$event_base_name]) || (!empty($cache_event_name) && $cache_event_name != $event_name)) {
           continue;
         }
         // Create an event set if not yet done.
@@ -809,7 +821,7 @@ class RulesEventSet extends RulesRuleSet {
     }
     // Cache a whitelist of configured events so we can use it to speed up later
     // calls. See rules_invoke_event().
-    rules_set_cache('rules_event_whitelist', array_flip(array_keys($sets)));
+    rules_is_event_configured(NULL, TRUE);
   }
 
   protected function stateVariables($element = NULL) {
diff --git a/modules/rules_core.rules.inc b/modules/rules_core.rules.inc
index d4215fa..ca93815 100644
--- a/modules/rules_core.rules.inc
+++ b/modules/rules_core.rules.inc
@@ -229,49 +229,85 @@ function rules_rules_core_data_processor_info() {
  * @see rules_core_modules()
  */
 function rules_rules_core_condition_info() {
-  $defaults = array(
-    'group' => t('Components'),
-    'base' => 'rules_element_invoke_component',
-    'named parameter' => TRUE,
-    'access callback' => 'rules_element_invoke_component_access_callback',
-  );
   $items = array();
   foreach (rules_get_components(FALSE, 'condition') as $name => $config) {
-    $items['component_' . $name] = $defaults + array(
-      'label' => $config->plugin() . ': ' . drupal_ucfirst($config->label()),
-      'parameter' => $config->parameterInfo(),
-    );
-    $items['component_' . $name]['#config_name'] = $name;
+    $items['component_' . $name] = rules_rule_build_condition_info($config);
   }
   return $items;
 }
 
 /**
+ * Builds the condition info for a rules component.
+ *
+ * @param RulesConditionInterface $rules_config
+ *   The rules component to deliver the condition info for.
+ *
+ * @return array
+ *   Condition info array as required by hook_rules_condition_info().
+ */
+function rules_rule_build_condition_info(RulesConditionInterface $rules_config) {
+  static $defaults;
+  // Ensures that t() is called just once for this.
+  if (!$defaults) {
+    $defaults = array(
+      'group' => t('Components'),
+      'base' => 'rules_element_invoke_component',
+      'named parameter' => TRUE,
+      'access callback' => 'rules_element_invoke_component_access_callback',
+    );
+  }
+  return $defaults + array(
+    'label' => $rules_config->plugin() . ': ' . drupal_ucfirst($rules_config->label()),
+    'parameter' => $rules_config->parameterInfo(),
+    '#config_name' => $rules_config->name,
+    'module' => 'rules',
+  );
+}
+
+/**
  * Implements hook_rules_action_info() on behalf of the pseudo rules_core
  * module.
  *
  * @see rules_core_modules()
  */
 function rules_rules_core_action_info() {
-  $defaults = array(
-    'group' => t('Components'),
-    'base' => 'rules_element_invoke_component',
-    'named parameter' => TRUE,
-    'access callback' => 'rules_element_invoke_component_access_callback',
-  );
   $items = array();
   foreach (rules_get_components(FALSE, 'action') as $name => $config) {
-    $items['component_' . $name] = $defaults + array(
-      'label' => $config->plugin() . ': ' . drupal_ucfirst($config->label()),
-      'parameter' => $config->parameterInfo(),
-      'provides' => $config->providesVariables(),
-    );
-    $items['component_' . $name]['#config_name'] = $name;
+    $items['component_' . $name] = rules_rule_build_action_info($config);
   }
   return $items;
 }
 
 /**
+ * Builds the action info for a rules component.
+ *
+ * @param RulesActionInterface $rules_config
+ *   The rules component to deliver the action info for.
+ *
+ * @return array
+ *   Action info array as required by hook_rules_action_info().
+ */
+function rules_rule_build_action_info(RulesActionInterface $rules_config) {
+  static $defaults;
+  // Ensures that t() is called just once for this.
+  if (!$defaults) {
+    $defaults = array(
+      'group' => t('Components'),
+      'base' => 'rules_element_invoke_component',
+      'named parameter' => TRUE,
+      'access callback' => 'rules_element_invoke_component_access_callback',
+    );
+  }
+  return $defaults + array(
+    'label' => $rules_config->plugin() . ': ' . drupal_ucfirst($rules_config->label()),
+    'parameter' => $rules_config->parameterInfo(),
+    'provides' => $rules_config->providesVariables(),
+    '#config_name' => $rules_config->name,
+    'module' => 'rules',
+  );
+}
+
+/**
  * Implements RulesPluginUIInterface::operations() for the action.
  */
 function rules_element_invoke_component_operations(RulesPlugin $element) {
diff --git a/rules.module b/rules.module
index 719852c..9c8383c 100644
--- a/rules.module
+++ b/rules.module
@@ -362,6 +362,20 @@ function &rules_get_cache($cid = 'data') {
     if ($get = cache_get($cid . $cid_suffix, 'cache_rules')) {
       $cache[$cid] = $get->data;
     }
+    elseif ($cid === 'data') {
+      // There is no 'data' cache so we need to rebuild it. Make sure subsequent
+      // cache gets of the main 'data' cache during rebuild get the interim
+      // cache by passing in the reference of the static cache variable.
+      _rules_rebuild_cache($cache['data']);
+    }
+    elseif (strpos($cid, 'comp_') === 0) {
+      $cache[$cid] = FALSE;
+      _rules_rebuild_component_cache($cid);
+    }
+    elseif (strpos($cid, 'event_') === 0) {
+      $cache[$cid] = FALSE;
+      RulesEventSet::rebuildEventCache($cid);
+    }
     else {
       // Prevent stampeding by ensuring the cache is rebuilt just once at the
       // same time.
@@ -432,10 +446,23 @@ function _rules_rebuild_cache(&$cache) {
  *
  * @see rules_invoke_component()
  * @see rules_get_cache()
+ *
+ * @param string $cid
+ *   The cid of a specific component to re-build.
  */
-function _rules_rebuild_component_cache() {
-  $components = rules_get_components();
+function _rules_rebuild_component_cache($cid = NULL) {
+  // Ensure the environment is ready.
+  module_load_include('inc', 'rules', 'rules.rules');
 
+  $conditions = array();
+  // If a specific cid was requested only load that component.
+  if (!empty($cid)) {
+    // Remove comp_ prefix to filter by name.
+    $conditions['name'] = substr($cid, 5);
+  }
+  $components = rules_get_components(FALSE, NULL, $conditions);
+
+  $data_cache = &rules_get_cache();
   foreach ($components as $id => $component) {
     // If a component is marked as dirty, check if this still applies.
     if ($component->dirty) {
@@ -449,8 +476,24 @@ function _rules_rebuild_component_cache() {
       // Allow modules to alter the cached component.
       drupal_alter('rules_component', $component->plugin, $component);
       rules_set_cache('comp_' . $component->name, $component);
+
+      // Ensure the action / condition info cache is updated.
+      if ($component instanceof RulesActionInterface) {
+        $data_cache['action_info']['component_' . $component->name] = rules_rule_build_action_info($component);
+        $data_cache['action_info'] += rules_discover_plugins('RulesActionHandlerInterface');
+        drupal_alter('rules_action_info', $data_cache['action_info']);
+      }
+      elseif ($component instanceof RulesConditionInterface) {
+        $data_cache['condition_info']['component_' . $component->name] = rules_rule_build_condition_info($component);
+        $data_cache['condition_info'] += rules_discover_plugins('RulesConditionHandlerInterface');
+        drupal_alter('rules_condition_info', $data_cache['condition_info']);
+      }
     }
   }
+  // Reset static cache from rules_fetch_data().
+  drupal_static_reset('rules_fetch_data');
+  // Make sure the plugins info cache is consistent.
+  _rules_rebuild_cache($data_cache);
 }
 
 /**
@@ -481,12 +524,51 @@ function rules_flush_caches() {
  */
 function rules_clear_cache() {
   cache_clear_all('*', 'cache_rules', TRUE);
+  rules_is_event_configured(NULL, TRUE);
   drupal_static_reset('rules_get_cache');
   drupal_static_reset('rules_fetch_data');
   drupal_static_reset('rules_config_update_dirty_flag');
   entity_get_controller('rules_config')->resetCache();
 }
 
+
+/**
+ * Checks if a certain event has to be handled.
+ *
+ * We maintain a whitelist of configured events to reduce the number of cache
+ * reads.
+ *
+ * @param string $event_name
+ *   The name of the event to check.
+ * @param bool $reset
+ *   If set to true the cache is rebuild.
+ *
+ * @return bool
+ *   Whether the event is configured or not.
+ */
+function rules_is_event_configured($event_name, $reset = FALSE) {
+  $rules_event_whitelist = &drupal_static(__FUNCTION__);
+  if (!isset($rules_event_whitelist)) {
+    $rules_event_whitelist = rules_get_cache('rules_event_whitelist');
+  }
+  if ($rules_event_whitelist === FALSE || $reset) {
+    // Fetch all configured events that have a related active rule.
+    $query = db_select('rules_trigger')
+      ->fields('rules_trigger', array('event'))
+      ->groupBy('event');
+    $query->innerJoin('rules_config', NULL, 'rules_config.id = rules_trigger.id');
+    $query->condition('rules_config.active', 1);
+    $configured_events = $query->execute()
+      ->fetchCol();
+    $rules_event_whitelist = array();
+    if (!empty($configured_events)) {
+      $rules_event_whitelist = array_combine($configured_events, $configured_events);
+    }
+    rules_set_cache('rules_event_whitelist', $rules_event_whitelist);
+  }
+  return isset($rules_event_whitelist[$event_name]);
+}
+
 /**
  * Imports the given export and returns the imported configuration.
  *
@@ -1020,13 +1102,15 @@ function rules_invoke_event() {
   $args = func_get_args();
   $event_name = $args[0];
   unset($args[0]);
-  // We maintain a whitelist of configured events to reduces the number of cache
-  // reads. If the whitelist is empty we proceed and it is rebuilt.
-  if (rules_event_invocation_enabled()) {
-    $whitelist = rules_get_cache('rules_event_whitelist');
-    if ((empty($whitelist) || isset($whitelist[$event_name])) && $event = rules_get_cache('event_' . $event_name)) {
-      $event->executeByArgs($args);
-    }
+
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast['rules_is_event_configured'])) {
+    rules_is_event_configured($event_name);
+    $drupal_static_fast['rules_is_event_configured'] = &drupal_static('rules_is_event_configured');
+  }
+
+  if (!defined('MAINTENANCE_MODE') && isset($drupal_static_fast['rules_is_event_configured'][$event_name]) && $event = rules_get_cache('event_' . $event_name)) {
+    $event->executeByArgs($args);
   }
 }
 
@@ -1048,13 +1132,14 @@ function rules_invoke_event() {
  * @see rules_invoke_event()
  */
 function rules_invoke_event_by_args($event_name, $args = array()) {
-  // We maintain a whitelist of configured events to reduces the number of cache
-  // reads. If the whitelist is empty we proceed and it is rebuilt.
-  if (rules_event_invocation_enabled()) {
-    $whitelist = rules_get_cache('rules_event_whitelist');
-    if ((empty($whitelist) || isset($whitelist[$event_name])) && $event = rules_get_cache('event_' . $event_name)) {
-      $event->executeByArgs($args);
-    }
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast['rules_is_event_configured'])) {
+    rules_is_event_configured($event_name);
+    $drupal_static_fast['rules_is_event_configured'] = &drupal_static('rules_is_event_configured');
+  }
+
+  if (!defined('MAINTENANCE_MODE') && isset($drupal_static_fast['rules_is_event_configured'][$event_name]) && $event = rules_get_cache('event_' . $event_name)) {
+    $event->executeByArgs($args);
   }
 }
 
