diff options
Diffstat (limited to 'modules/update/update.module')
-rw-r--r-- | modules/update/update.module | 160 |
1 files changed, 133 insertions, 27 deletions
diff --git a/modules/update/update.module b/modules/update/update.module index 6ed91d621..20f148b7d 100644 --- a/modules/update/update.module +++ b/modules/update/update.module @@ -277,9 +277,9 @@ function _update_requirement_check($project, $type) { function update_cron() { $frequency = variable_get('update_check_frequency', 1); $interval = 60 * 60 * 24 * $frequency; - // Cron should check for updates if there is no update data cached or if the configured - // update interval has elapsed. - if (!cache_get('update_info', 'cache_update') || ((REQUEST_TIME - variable_get('update_last_check', 0)) > $interval)) { + // Cron should check for updates if there is no update data cached or if the + // configured update interval has elapsed. + if (!_update_cache_get('update_available_releases') || ((REQUEST_TIME - variable_get('update_last_check', 0)) > $interval)) { update_refresh(); _update_cron_notify(); } @@ -291,10 +291,10 @@ function update_cron() { * Adds a submit handler to the system modules and themes forms, so that if a * site admin saves either form, we invalidate the cache of available updates. * - * @see update_invalidate_cache() + * @see _update_cache_clear() */ function update_form_system_themes_alter(&$form, $form_state) { - $form['#submit'][] = 'update_invalidate_cache'; + $form['#submit'][] = 'update_cache_clear_submit'; } /** @@ -303,10 +303,18 @@ function update_form_system_themes_alter(&$form, $form_state) { * Adds a submit handler to the system modules and themes forms, so that if a * site admin saves either form, we invalidate the cache of available updates. * - * @see update_invalidate_cache() + * @see _update_cache_clear() */ function update_form_system_modules_alter(&$form, $form_state) { - $form['#submit'][] = 'update_invalidate_cache'; + $form['#submit'][] = 'update_cache_clear_submit'; +} + +/** + * Helper function for use as a form submit callback. + */ +function update_cache_clear_submit($form, &$form_state) { + // Clear all update module caches. + _update_cache_clear(); } /** @@ -354,8 +362,7 @@ function update_get_available($refresh = FALSE) { break; } } - if (!$needs_refresh && ($cache = cache_get('update_info', 'cache_update')) - && $cache->expire > REQUEST_TIME) { + if (!$needs_refresh && ($cache = _update_cache_get('update_available_releases')) && $cache->expire > REQUEST_TIME) { $available = $cache->data; } elseif ($needs_refresh || $refresh) { @@ -368,24 +375,6 @@ function update_get_available($refresh = FALSE) { } /** - * Implementation of hook_flush_caches(). - * - * The function update.php (among others) calls this hook to flush the caches. - * Since we're running update.php, we are likely to install a new version of - * something, in which case, we want to check for available update data again. - */ -function update_flush_caches() { - return array('cache_update'); -} - -/** - * Invalidates any cached data relating to update status. - */ -function update_invalidate_cache() { - cache_clear_all('*', 'cache_update', TRUE); -} - -/** * Wrapper to load the include file and then refresh the release data. */ function update_refresh() { @@ -515,3 +504,120 @@ function _update_project_status_sort($a, $b) { $b_status = $b['status'] > 0 ? $b['status'] : (-10 * $b['status']); return $a_status - $b_status; } + +/** + * @defgroup update_status_cache Private update status cache system + * @{ + * + * We specifically do NOT use the core cache API for saving the fetched data + * about available updates. It is vitally important that this cache is only + * cleared when we're populating it after successfully fetching new available + * update data. Usage of the core cache API results in all sorts of potential + * problems that would result in attempting to fetch available update data all + * the time, including if a site has a "minimum cache lifetime" (which is both + * a minimum and a maximum) defined, or if a site uses memcache or another + * plug-able cache system that assumes volatile caches. + * + * Update module still uses the {cache_update} table, but instead of using + * cache_set(), cache_get(), and cache_clear_all(), there are private helper + * functions that implement these same basic tasks but ensure that the cache + * is not prematurely cleared, and that the data is always stored in the + * database, even if memcache or another cache backend is in use. + */ + +/** + * Store data in the private update status cache table. + * + * Note: this function completely ignores the {cache_update}.headers field + * since that is meaningless for the kinds of data we're caching. + * + * @param $cid + * The cache ID to save the data with. + * @param $data + * The data to store. + * @param $expire + * One of the following values: + * - CACHE_PERMANENT: Indicates that the item should never be removed except + * by explicitly using _update_cache_clear(). + * - A Unix timestamp: Indicates that the item should be kept at least until + * the given time, after which it will be invalidated. + */ +function _update_cache_set($cid, $data, $expire) { + $fields = array( + 'created' => REQUEST_TIME, + 'expire' => $expire, + 'headers' => NULL, + ); + if (!is_string($data)) { + $fields['data'] = serialize($data); + $fields['serialized'] = 1; + } + else { + $fields['data'] = $data; + $fields['serialized'] = 0; + } + db_merge('cache_update') + ->key(array('cid' => $cid)) + ->fields($fields) + ->execute(); +} + +/** + * Retrieve data from the private update status cache table. + * + * @param $cid + * The cache ID to retrieve. + * @return + * The data for the given cache ID, or NULL if the ID was not found. + */ +function _update_cache_get($cid) { + $cache = db_query("SELECT data, created, expire, serialized FROM {cache_update} WHERE cid = :cid", array(':cid' => $cid))->fetchObject(); + if (isset($cache->data)) { + if ($cache->serialized) { + $cache->data = unserialize($cache->data); + } + } + return $cache; +} + +/** + * Invalidates cached data relating to update status. + * + * @param $cid + * Optional cache ID of the record to clear from the private update module + * cache. If empty, all records will be cleared from the table. + */ +function _update_cache_clear($cid = NULL) { + $query = db_delete('cache_update'); + if (!empty($cid)) { + $query->condition('cid', $cid); + } + $query->execute(); +} + +/** + * Implementation of hook_flush_caches(). + * + * Called from update.php (among others) to flush the caches. + * Since we're running update.php, we are likely to install a new version of + * something, in which case, we want to check for available update data again. + * However, because we have our own caching system, we need to directly clear + * the database table ourselves at this point and return nothing, for example, + * on sites that use memcache where cache_clear_all() won't know how to purge + * this data. + * + * However, we only want to do this from update.php, since otherwise, we'd + * lose all the available update data on every cron run. So, we specifically + * check if the site is in MAINTENANCE_MODE == 'update' (which indicates + * update.php is running, not update module... alas for overloaded names). + */ +function update_flush_caches() { + if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') { + _update_cache_clear(); + } + return array(); +} + +/** + * @} End of "defgroup update_status_cache". + */ |