diff --git a/includes/rules.core.inc b/includes/rules.core.inc
index 24d85c6..1439973 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 getCid(){
+    $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->getCid(), '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,20 +1147,31 @@ 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->getCid());
+          }
         }
         elseif ($this->plugin == 'reaction rule') {
-          // Clear event sets cached for evaluation.
-          cache_clear_all('event_', 'cache_rules', TRUE);
-          variable_del('rules_event_whitelist');
+          // Rebuild event whitelist.
+          rules_is_event_configured(NULL, TRUE);
+          // Rebuild the event sets cached for evaluation.
+          foreach ($this->events() as $event_name) {
+            RulesEventSet::rebuildEventCache('event' . $event_name);
+          }
+          // Make sure any remove event is updated too.
+          if (!empty($original)) {
+            foreach (array_diff($original->events, $this->events()) as $event_name) {
+              RulesEventSet::rebuildEventCache('event' . $event_name);
+            }
+          }
+          cache_clear_all($this->getCid(), 'cache_rules', TRUE);
         }
-        drupal_static_reset('rules_get_cache');
         drupal_static_reset('rules_config_update_dirty_flag');
       }
 
@@ -1468,6 +1513,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->getCid());
+      }
+    }
+
     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 85b7296..3f5beec 100644
--- a/includes/rules.plugins.inc
+++ b/includes/rules.plugins.inc
@@ -756,12 +756,24 @@ 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) {
@@ -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().
-    variable_set('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 39fff93..bfd8274 100644
--- a/modules/rules_core.rules.inc
+++ b/modules/rules_core.rules.inc
@@ -223,49 +223,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 d2ff900..07d4cac 100644
--- a/rules.module
+++ b/rules.module
@@ -326,11 +326,11 @@ function &rules_get_cache($cid = 'data') {
     }
     elseif (strpos($cid, 'comp_') === 0) {
       $cache[$cid] = FALSE;
-      _rules_rebuild_component_cache();
+      _rules_rebuild_component_cache($cid);
     }
     elseif (strpos($cid, 'event_') === 0) {
       $cache[$cid] = FALSE;
-      RulesEventSet::rebuildEventCache();
+      RulesEventSet::rebuildEventCache($cid);
     }
     else {
       $cache[$cid] = FALSE;
@@ -371,10 +371,20 @@ 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) {
+  $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) {
@@ -388,8 +398,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);
 }
 
 /**
@@ -420,13 +446,48 @@ function rules_flush_caches() {
  */
 function rules_clear_cache() {
   cache_clear_all('*', 'cache_rules', TRUE);
-  variable_del('rules_event_whitelist');
+  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 = 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.
  *
@@ -962,11 +1023,7 @@ 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. We access it directly via the global $conf as this is fast without
-  // having to introduce another static cache. Then, if the whitelist is unset,
-  // we ignore it so cache rebuilding is triggered.
-  if (!defined('MAINTENANCE_MODE') && (!isset($conf['rules_event_whitelist']) || isset($conf['rules_event_whitelist'][$event_name])) && $event = rules_get_cache('event_' . $event_name)) {
+  if (!defined('MAINTENANCE_MODE') && rules_is_event_configured($event_name) && $event = rules_get_cache('event_' . $event_name)) {
     $event->executeByArgs($args);
   }
 }
@@ -991,11 +1048,7 @@ function rules_invoke_event() {
 function rules_invoke_event_by_args($event_name, $args = array()) {
   global $conf;
 
-  // We maintain a whitelist of configured events to reduces the number of cache
-  // reads. We access it directly via the global $conf as this is fast without
-  // having to introduce another static cache. Then, if the whitelist is unset,
-  // we ignore it so cache rebuilding is triggered.
-  if (!defined('MAINTENANCE_MODE') && (!isset($conf['rules_event_whitelist']) || isset($conf['rules_event_whitelist'][$event_name])) && $event = rules_get_cache('event_' . $event_name)) {
+  if (!defined('MAINTENANCE_MODE') && rules_is_event_configured($event_name) && $event = rules_get_cache('event_' . $event_name)) {
     $event->executeByArgs($args);
   }
 }
