Cache commit objects in tunable LRU memory cache
Cache commit objects in tunable LRU memory cache

--- a/config/gitphp.conf.defaults.php
+++ b/config/gitphp.conf.defaults.php
@@ -360,6 +360,20 @@
 //	array('memcacheserver1', 11211),
 //	array('memcacheserver2')
 //);
+
+/*
+ * objectmemory
+ * Number of git objects to keep in memory during page load
+ * A higher number means more objects will be stored in memory.
+ * Storing more objects in memory means GitPHP can fetch these
+ * objects from memory rather than reloading them from the repository
+ * again, at the expense of the web process taking more RAM to keep
+ * these objects.  Increase if you have lots of memory and want to
+ * decrease the likelihood of hitting the git repo on disk (disk is
+ * the bottleneck).  Decrease if you have low web server memory or
+ * lots of projects in your install (memory is the bottleneck).
+ */
+$gitphp_conf['objectmemory'] = 100;
 
 
 

--- /dev/null
+++ b/include/cache/MemoryCache.class.php
@@ -1,1 +1,153 @@
+<?php
+/**
+ * GitPHP MemoryCache
+ *
+ * Cache to manage objects in process memory
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2012 Christopher Han
+ * @package GitPHP
+ * @subpackage Cache
+ */
 
+/**
+ * MemoryCache class
+ *
+ * @package GitPHP
+ * @subpackage Cache
+ */
+class GitPHP_MemoryCache
+{
+	/**
+	 * instance
+	 *
+	 * Stores the singleton instance
+	 *
+	 * @access protected
+	 * @static
+	 */
+	protected static $instance;
+
+	/**
+	 * objects
+	 *
+	 * Stores the objects in this cache
+	 *
+	 * @access protected
+	 */
+	protected $objects = array();
+
+	/**
+	 * cacheMap
+	 *
+	 * Map of objects in cache
+	 *
+	 * @access protected
+	 */
+	protected $cacheMap = array();
+
+	/**
+	 * GetInstance
+	 *
+	 * Returns the singleton instance
+	 *
+	 * @access public
+	 * @static
+	 * @return mixed instance of config class
+	 */
+	public static function GetInstance()
+	{
+		if (!self::$instance) {
+			self::$instance = new GitPHP_MemoryCache();
+		}
+		return self::$instance;
+	}
+
+	/**
+	 * Get
+	 *
+	 * Gets an object from the cache
+	 *
+	 * @access public
+	 * @param string $key cache key
+	 * @return mixed object from cache if found
+	 */
+	public function Get($key)
+	{
+		if (empty($key))
+			return null;
+
+		if (!isset($this->objects[$key]))
+			return null;
+
+		$object = $this->objects[$key];
+
+		$this->KeyUsed($key);
+
+		return $object;
+	}
+
+	/**
+	 * Set
+	 *
+	 * Sets an object into the cache
+	 *
+	 * @access public
+	 * @param string $key cache key
+	 * @param mixed $object object to cache
+	 */
+	public function Set($key, $object)
+	{
+		if (empty($key))
+			return;
+
+		if (isset($this->objects[$key])) {
+			$this->objects[$key] = $object;
+			$this->KeyUsed($key);
+		} else {
+			$this->Evict();
+			$this->objects[$key] = $object;
+			array_unshift($this->cacheMap, $key);
+		}
+	}
+
+	/**
+	 * Evict
+	 *
+	 * Evicts items from the cache down to the size limit
+	 *
+	 * @access private
+	 */
+	private function Evict()
+	{
+		$size = GitPHP_Config::GetInstance()->GetValue('objectmemory', 100);
+
+		while (count($this->cacheMap) >= $size) {
+			$key = array_pop($this->cacheMap);
+
+			if (!empty($key))
+				unset($this->objects[$key]);
+		}
+	}
+
+	/**
+	 * KeyUsed
+	 *
+	 * Mark key as recently used
+	 *
+	 * @access private
+	 */
+	private function KeyUsed($key)
+	{
+		if (empty($key))
+			return;
+
+		$index = array_search($key, $this->cacheMap);
+		if ($index !== false) {
+			array_splice($this->cacheMap, $index, 1);
+			array_unshift($this->cacheMap, $key);
+		}
+	}
+
+}
+

--- a/include/git/Project.class.php
+++ b/include/git/Project.class.php
@@ -16,6 +16,7 @@
 require_once(GITPHP_GITOBJECTDIR . 'Tag.class.php');
 require_once(GITPHP_GITOBJECTDIR . 'Pack.class.php');
 require_once(GITPHP_GITOBJECTDIR . 'GitConfig.class.php');
+require_once(GITPHP_CACHEDIR . 'MemoryCache.class.php');
 
 define('GITPHP_ABBREV_HASH_MIN', 7);
 
@@ -229,16 +230,6 @@
 	 * @access protected
 	 */
 	protected $website = null;
-
-	/**
-	 * commitCache
-	 *
-	 * Caches fetched commit objects in case of
-	 * repeated requests for the same object
-	 *
-	 * @access protected
-	 */
-	protected $commitCache = array();
 
 /* packfile internal variables {{{2*/
 
@@ -1020,16 +1011,23 @@
 
 		if (preg_match('/^[0-9A-Fa-f]{40}$/', $hash)) {
 
-			if (!isset($this->commitCache[$hash])) {
-				$cacheKey = 'project|' . $this->project . '|commit|' . $hash;
-				$cached = GitPHP_Cache::GetObjectCacheInstance()->Get($cacheKey);
-				if ($cached)
-					$this->commitCache[$hash] = $cached;
-				else
-					$this->commitCache[$hash] = new GitPHP_Commit($this, $hash);
-			}
-
-			return $this->commitCache[$hash];
+			$key = GitPHP_Commit::CacheKey($this->project, $hash);
+			$memoryCache = GitPHP_MemoryCache::GetInstance();
+			$commit = $memoryCache->Get($key);
+
+			if (!$commit) {
+
+				$commit = GitPHP_Cache::GetObjectCacheInstance()->Get($key);
+
+				if (!$commit) {
+					$commit = new GitPHP_Commit($this, $hash);
+				}
+
+				$memoryCache->Set($key, $commit);
+
+			}
+
+			return $commit;
 
 		}
 

comments