Index: media.filter.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/media/media.filter.inc,v
retrieving revision 1.14
diff -u -p -r1.14 media.filter.inc
--- media.filter.inc	29 Nov 2010 19:29:17 -0000	1.14
+++ media.filter.inc	7 Dec 2010 15:32:51 -0000
@@ -145,6 +145,10 @@ function _media_markup($match, $wysiwyg 
     if (!$media_obj) {
      throw new Exception('Could not load media object');
     }
+
+    // Track the fid of this media object to the media_filter_usage table.
+    media_filter_track_usage($media['fid']);
+
     $settings = is_array($media['attributes']) ? $media['attributes'] : array();
     $attribute_whitelist = media_variable_get('wysiwyg_allowed_attributes');
     $settings = array_intersect_key($settings, array_flip($attribute_whitelist));
@@ -358,3 +362,59 @@ function media_get_file_without_label($m
   $media->override = $settings;
   return field_view_field('media', $media, 'file', $format);
 }
+
+/**
+ * Clear the filter cache when a referenced media object is updated.
+ *
+ * The media filter calls media_load(). This means that if a media object
+ * is updated, the check_markup() and field caches could return stale content.
+ * There are several possible approaches to deal with this:
+ *  - Disable filter caching in media_filter_info(), this was found to cause a
+ *    30% performance hit from profiling four node teasers, due to both the
+ *    media filter itself, and other filters that can't be cached.
+ *  - Clear the filter and field caches whenever any media node is updated, this
+ *    would ensure cache coherency but would reduce the effectiveness of those
+ *    caches on high traffic sites with lots of media content updates.
+ *  - The approach taken here: Record the fid of all media objects that are
+ *    referenced by the media filter. Only clear the filter and field caches
+ *    when one of these is updated, as opposed to all media objects.
+ *  - @todo: consider an EntityFieldQuery to limit cache clearing to only those
+ *    entities that use a text format with the media filter, possibly checking
+ *    the contents of those fields to further limit this to fields referencing
+ *    the media object being updated. This would need to be implemented
+ *    carefully to avoid scalability issues with large result sets, and may
+ *    not be worth the effort.
+ *
+ * @param $fid
+ *   The media fid being updated.
+ */
+function media_clear_filter_cache($fid) {
+  $referenced = db_query('SELECT fid FROM {media_filter_usage} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
+  if ($referenced) {
+    // @todo: support entity cache, either via a hook, or using module_exists().
+    cache_clear_all('*', 'cache_filter', TRUE);
+    cache_clear_all('*', 'cache_field', TRUE);
+  }
+}
+
+/**
+ * Track usage of media fids by the media filter.
+ *
+ * This function tra
+ *
+ * @param $fid
+ *   The media fid.
+ */
+function media_filter_track_usage($fid) {
+  // This function only tracks when fids are found by the media filter.
+  // It would be impractical to check when formatted text is edited to remove
+  // references to fids, however by keeping a timestamp, we can implement
+  // rudimentary garbage collection in hook_flush_caches().
+  // However we only need to track that an fid has ever been referenced,
+  // not every time, so avoid updating this table more than once per month,
+  // per fid.
+  $timestamp = db_query('SELECT timestamp FROM {media_filter_usage} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
+  if (!$timestamp || $timestamp <= REQUEST_TIME - 86400 * 30) {
+    db_merge('media_filter_usage')->key(array('fid' => $fid))->fields(array('fid' => $fid, 'timestamp' => REQUEST_TIME))->execute();
+  }
+}
Index: media.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/media/media.install,v
retrieving revision 1.17
diff -u -p -r1.17 media.install
--- media.install	26 Oct 2010 13:36:36 -0000	1.17
+++ media.install	7 Dec 2010 15:32:51 -0000
@@ -189,6 +189,8 @@ function media_schema() {
   $schema['cache_media_xml'] = drupal_get_schema_unprocessed('system', 'cache');
   $schema['cache_media_xml']['description'] = 'Cache table for the the results of retreived XML documents for remote streams.';
 
+  $schema['media_filter_usage'] = _media_update_7012_schema_media_filter_usage();
+
   return $schema;
 }
 
@@ -426,4 +428,45 @@ function media_update_7010() {
  */
 function media_update_7011() {
   media_variable_set('wysiwyg_allowed_types', array('image','video'));
-}
\ No newline at end of file
+}
+
+/**
+ * Add media_filter_usage.
+ */
+function media_update_7012() {
+  db_create_table('media_filter_usage', _media_update_7012_schema_media_filter_usage());
+}
+
+/**
+ * Schema definition for media_update_7012().
+ */
+function _media_update_7012_schema_media_filter_usage() {
+  return array(
+    'description' => 'Stores fids that have been included in the media tag in formatted textareas.',
+    'fields' => array(
+      'fid' => array(
+        'description' => 'The media {file_managed}.fid.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'timestamp' => array(
+        'description' => 'The timestamp the fid was last recorded by media_filter()',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'foreign keys' => array(
+      'file_managed' => array(
+        'table' => 'file_managed',
+        'columns' => array('fid' => 'fid'),
+      ),
+    ),
+    'primary key' => array('fid'),
+    'indexes' => array(
+      'timestamp' => array('timestamp'),
+    ),
+  );
+}
Index: media.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/media/media.module,v
retrieving revision 1.62
diff -u -p -r1.62 media.module
--- media.module	29 Nov 2010 20:06:55 -0000	1.62
+++ media.module	7 Dec 2010 15:32:51 -0000
@@ -369,6 +369,15 @@ function media_file_update($file) {
 }
 
 /**
+ * Implements hook_file_delete().
+ */
+function media_file_delete($file) {
+  // @todo: call field_attach_delete() from here, or add media_delete().
+
+  // Remove this file from the {media_filter_usage} table.
+  db_delete('media_file_usage')->condition('fid', $file->fid)->execute();
+}
+/**
  * Implement hook_page_alter().
  *
  * This is used to use our alternate template when ?render=media-popup is passed
@@ -541,6 +550,7 @@ function media_save($media) {
   }
   else {
     field_attach_update('media', $media);
+    media_clear_filter_cache($media->fid);
   }
 
   // Since the Media module can't make files into fieldable entities directly
@@ -896,6 +906,9 @@ function media_save_attached_file($file)
       //   Since we aren't, we have to update usage here too, as we do in
       //   media_save().
       file_usage_delete((object)(array) $media, 'file', 'media', $media->fid);
+
+      // Also clear cached text formats from here.
+      media_clear_filter_cache($media->fid);
     }
   }
 
@@ -1026,7 +1039,6 @@ function media_filter_info() {
     'description' => t('This filter will convert [[{type:media... ]] tags into markup'),
     'process callback' => 'media_filter',
     'tips callback' => 'media_filter_tips', // @TODO not implemented
-    'cache' => FALSE,
   );
   return $filters;
 }
@@ -1122,3 +1134,17 @@ function media_file_validate_types(stdCl
   
   return $errors;
 }
+
+/**
+ * Implements hook_flush_caches().
+ */
+function media_flush_caches() {
+  // Garbage collection for the {media_filter_usage} table. If an fid was last
+  // recorded four months ago (minimum three months due to logic in
+  // media_filter_track_usage()), remove it from this table while the filter
+  // and field caches are being cleared. If the fid is still in use, it will
+  // be added back to the table the next time check_markup() runs on that
+  // content. This prevents fids from staying in this table indefinitely,
+  // even if the post that references them is edited or deleted.
+  db_delete('media_filter_usage')->condition('timestamp', REQUEST_TIME - 86400 * 120, '<')->execute();
+}
