From 19707a63a5e0ae58090803f3c7c162481515f69f Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 28 Mar 2018 18:52:57 -0700 Subject: [PATCH] (dev/core#174) Forms/Sessions - Store state via Civi::cache('session') Overview ---------------------------------------- When using forms based on CiviQuickForm (specifically `CRM_Core_Controller`), `CRM_Core_Session` stores form-state via `CRM_Core_BAO_Cache::storeSessionToCache` and `::restoreSessionFromCache`, which in turn calls `CRM_Core_BAO_Cache::setItem()` and `::getItem()`. However, using `CRM_Core_BAO_Cache::setItem()` and `::getItem()` means that all session state **must** be written to MySQL. For dev/core#174, we seek the **option** to store via Redis/Memcache. Before ---------------------------------------- * (a) Form/session state is always stored via `CRM_Core_BAO_Cache::setItem()` and `civicrm_cache` table. * (b) To ensure that old sessions are periodically purged, there is special purpose logic that accesses `civicrm_cache` (roughly `delete where group_name=Sessions and created_date < now()-ttl`). * (c) On Memcache/Redis-enabled systems, the cache server functions as an extra tier. The DB provides canonical storage for form/session state. After ---------------------------------------- * (a) Form/session state is stored via `CRM_Utils_CacheInterface`. * On a typical server, this defaults to `CRM_Utils_Cache_SqlGroup` and `civicrm_cache` table. * (b) To ensure that old sessions are periodically purged, the call to `CRM_Utils_CacheInterface::set()` specifies a TTL. * It is the responsibility of the cache driver to handle TTLs. With #12360, TTL's are supported in `ArrayCache`, `SqlGroup`, and `Redis`. * (c) On Memcache/Redis-enabled systems, the cache server provides canonical storage for form/session state. --- CRM/Core/BAO/Cache.php | 81 ++++++++++++++++++++--------------------- CRM/Core/Session.php | 2 +- Civi/Core/Container.php | 1 + 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/CRM/Core/BAO/Cache.php b/CRM/Core/BAO/Cache.php index a8bde2f16b..ce1d63ede3 100644 --- a/CRM/Core/BAO/Cache.php +++ b/CRM/Core/BAO/Cache.php @@ -40,6 +40,13 @@ */ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { + /** + * When store session/form state, how long should the data be retained? + * + * @var int, number of second + */ + const DEFAULT_SESSION_TTL = 172800; // Two days: 2*24*60*60 + /** * @var array ($cacheKey => $cacheValue) */ @@ -237,7 +244,8 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { if (!empty($_SESSION[$sessionName[0]][$sessionName[1]])) { $value = $_SESSION[$sessionName[0]][$sessionName[1]]; } - self::setItem($value, 'CiviCRM Session', "{$sessionName[0]}_{$sessionName[1]}"); + $key = "{$sessionName[0]}_{$sessionName[1]}"; + Civi::cache('session')->set($key, $value, self::pickSessionTtl($key)); if ($resetSession) { $_SESSION[$sessionName[0]][$sessionName[1]] = NULL; unset($_SESSION[$sessionName[0]][$sessionName[1]]); @@ -248,7 +256,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { if (!empty($_SESSION[$sessionName])) { $value = $_SESSION[$sessionName]; } - self::setItem($value, 'CiviCRM Session', $sessionName); + Civi::cache('session')->set($sessionName, $value, self::pickSessionTtl($sessionName)); if ($resetSession) { $_SESSION[$sessionName] = NULL; unset($_SESSION[$sessionName]); @@ -275,17 +283,13 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { public static function restoreSessionFromCache($names) { foreach ($names as $key => $sessionName) { if (is_array($sessionName)) { - $value = self::getItem('CiviCRM Session', - "{$sessionName[0]}_{$sessionName[1]}" - ); + $value = Civi::cache('session')->get("{$sessionName[0]}_{$sessionName[1]}"); if ($value) { $_SESSION[$sessionName[0]][$sessionName[1]] = $value; } } else { - $value = self::getItem('CiviCRM Session', - $sessionName - ); + $value = Civi::cache('session')->get($sessionName); if ($value) { $_SESSION[$sessionName] = $value; } @@ -293,6 +297,32 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { } } + /** + * Determine how long session-state should be retained. + * + * @param string $sessionKey + * Ex: '_CRM_Admin_Form_Preferences_Display_f1a5f232e3d850a29a7a4d4079d7c37b_4654_container' + * Ex: 'CiviCRM_CRM_Admin_Form_Preferences_Display_f1a5f232e3d850a29a7a4d4079d7c37b_4654' + * @return int + * Number of seconds. + */ + protected static function pickSessionTtl($sessionKey) { + $secureSessionTimeoutMinutes = (int) Civi::settings()->get('secure_cache_timeout_minutes'); + if ($secureSessionTimeoutMinutes) { + $transactionPages = array( + 'CRM_Contribute_Controller_Contribution', + 'CRM_Event_Controller_Registration', + ); + foreach ($transactionPages as $transactionPage) { + if (strpos($sessionKey, $transactionPage) !== FALSE) { + return $secureSessionTimeoutMinutes * 60; + } + } + } + + return self::DEFAULT_SESSION_TTL; + } + /** * Do periodic cleanup of the CiviCRM session table. * @@ -305,32 +335,6 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { * @param bool $prevNext */ public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FALSE, $expired = FALSE) { - // first delete all sessions more than 20 minutes old which are related to any potential transaction - $timeIntervalMins = (int) Civi::settings()->get('secure_cache_timeout_minutes'); - if ($timeIntervalMins && $session) { - $transactionPages = array( - 'CRM_Contribute_Controller_Contribution', - 'CRM_Event_Controller_Registration', - ); - - $params = array( - 1 => array( - date('Y-m-d H:i:s', time() - $timeIntervalMins * 60), - 'String', - ), - ); - foreach ($transactionPages as $trPage) { - $params[] = array("%${trPage}%", 'String'); - $where[] = 'path LIKE %' . count($params); - } - - $sql = " -DELETE FROM civicrm_cache -WHERE group_name = 'CiviCRM Session' -AND created_date <= %1 -AND (" . implode(' OR ', $where) . ")"; - CRM_Core_DAO::executeQuery($sql, $params); - } // clean up the session cache every $cacheCleanUpNumber probabilistically $cleanUpNumber = 757; @@ -355,13 +359,8 @@ AND (" . implode(' OR ', $where) . ")"; } if ($session) { - - $sql = " -DELETE FROM civicrm_cache -WHERE group_name = 'CiviCRM Session' -AND created_date < date_sub( NOW( ), INTERVAL $timeIntervalDays DAY ) -"; - CRM_Core_DAO::executeQuery($sql); + // Session caches are just regular caches, so they expire naturally per TTL. + $expired = TRUE; } if ($expired) { diff --git a/CRM/Core/Session.php b/CRM/Core/Session.php index ccadd74bcd..b81cc961f0 100644 --- a/CRM/Core/Session.php +++ b/CRM/Core/Session.php @@ -286,7 +286,7 @@ class CRM_Core_Session { $values = &$this->_session[$this->_key]; } else { - $values = CRM_Core_BAO_Cache::getItem('CiviCRM Session', "CiviCRM_{$prefix}"); + $values = Civi::cache('session')->get("CiviCRM_{$prefix}"); } if ($values) { diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index a137d166b5..bb6ca79319 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -164,6 +164,7 @@ class Container { 'js_strings' => 'js_strings', 'community_messages' => 'community_messages', 'checks' => 'checks', + 'session' => 'CiviCRM Session', ); foreach ($basicCaches as $cacheSvc => $cacheGrp) { $container->setDefinition("cache.{$cacheSvc}", new Definition( -- 2.25.1