ECCache.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #ifndef ECCACHE_INCLUDED
  18. #define ECCACHE_INCLUDED
  19. #include <kopano/zcdefs.h>
  20. #include <list>
  21. #include <string>
  22. #include <vector>
  23. #include <utility>
  24. #include <cassert>
  25. #include <kopano/platform.h>
  26. namespace KC {
  27. template<typename Key> class KeyEntry _kc_final {
  28. public:
  29. Key key;
  30. time_t ulLastAccess;
  31. };
  32. template<typename Key>
  33. bool KeyEntryOrder(const KeyEntry<Key> &a, const KeyEntry<Key> &b) {
  34. return a.ulLastAccess < b.ulLastAccess;
  35. }
  36. template<typename Value>
  37. unsigned int GetCacheAdditionalSize(const Value &val) {
  38. return 0;
  39. }
  40. class ECsCacheEntry {
  41. public:
  42. time_t ulLastAccess = 0;
  43. };
  44. class _kc_export ECCacheBase {
  45. public:
  46. typedef unsigned long count_type;
  47. typedef uint64_t size_type;
  48. _kc_hidden virtual ~ECCacheBase(void) _kc_impdtor;
  49. _kc_hidden virtual count_type ItemCount(void) const = 0;
  50. _kc_hidden virtual size_type Size(void) const = 0;
  51. _kc_hidden size_type MaxSize(void) const { return m_ulMaxSize; }
  52. _kc_hidden long MaxAge(void) const { return m_lMaxAge; }
  53. _kc_hidden size_type HitCount(void) const { return m_ulCacheHit; }
  54. _kc_hidden size_type ValidCount(void) const { return m_ulCacheValid; }
  55. // Decrement the valid count. Used from ECCacheManger::GetCell.
  56. _kc_hidden void DecrementValidCount(void)
  57. {
  58. assert(m_ulCacheValid >= 1);
  59. --m_ulCacheValid;
  60. }
  61. // Call the provided callback with some statistics.
  62. void RequestStats(void(callback)(const std::string &, const std::string &, const std::string &, void*), void *obj);
  63. // Dump statistics
  64. void DumpStats(void) const;
  65. protected:
  66. ECCacheBase(const std::string &strCachename, size_type ulMaxSize, long lMaxAge);
  67. _kc_hidden void IncrementHitCount(void) { ++m_ulCacheHit; }
  68. _kc_hidden void IncrementValidCount(void) { ++m_ulCacheValid; }
  69. _kc_hidden void ClearCounters(void) { m_ulCacheHit = m_ulCacheValid = 0; }
  70. private:
  71. const std::string m_strCachename;
  72. const size_type m_ulMaxSize;
  73. const long m_lMaxAge;
  74. size_type m_ulCacheHit = 0, m_ulCacheValid = 0;
  75. };
  76. template<typename _MapType> class ECCache _kc_final : public ECCacheBase {
  77. public:
  78. typedef typename _MapType::key_type key_type;
  79. typedef typename _MapType::mapped_type mapped_type;
  80. ECCache(const std::string &strCachename, size_type ulMaxSize, long lMaxAge)
  81. : ECCacheBase(strCachename, ulMaxSize, lMaxAge)
  82. , m_ulSize(0)
  83. { }
  84. ECRESULT ClearCache()
  85. {
  86. m_map.clear();
  87. m_ulSize = 0;
  88. ClearCounters();
  89. return erSuccess;
  90. }
  91. count_type ItemCount(void) const _kc_override
  92. {
  93. return m_map.size();
  94. }
  95. size_type Size(void) const _kc_override
  96. {
  97. // it works with map and hash_map
  98. return (m_map.size() * (sizeof(typename _MapType::value_type) + sizeof(_MapType) )) + m_ulSize;
  99. }
  100. ECRESULT RemoveCacheItem(const key_type &key)
  101. {
  102. auto iter = m_map.find(key);
  103. if (iter == m_map.end())
  104. return KCERR_NOT_FOUND;
  105. m_ulSize -= GetCacheAdditionalSize(iter->second);
  106. m_ulSize -= GetCacheAdditionalSize(key);
  107. m_map.erase(iter);
  108. return erSuccess;
  109. }
  110. ECRESULT GetCacheItem(const key_type &key, mapped_type **lppValue)
  111. {
  112. ECRESULT er = erSuccess;
  113. time_t tNow = GetProcessTime();
  114. auto iter = m_map.find(key);
  115. if (iter != m_map.end()) {
  116. // Cache age of the cached item, if expired remove the item from the cache
  117. if (MaxAge() != 0 && (long)(tNow - iter->second.ulLastAccess) >= MaxAge()) {
  118. /*
  119. * Because of templates, there is no guarantee
  120. * that m_map keeps iterators valid while
  121. * elements are deleted from it. Track them in
  122. * a separate delete list.
  123. */
  124. std::vector<key_type> dl;
  125. // Loop through all items and check
  126. for (iter = m_map.begin(); iter != m_map.end(); ++iter)
  127. if ((long)(tNow - iter->second.ulLastAccess) >= MaxAge())
  128. dl.push_back(iter->first);
  129. for (const auto &i : dl)
  130. m_map.erase(i);
  131. er = KCERR_NOT_FOUND;
  132. } else {
  133. *lppValue = &iter->second;
  134. // If we have an aging cache, we don't update the timestamp,
  135. // so we can't keep a value longer in the cache than the max age.
  136. // If we have a non-aging cache, we need to update it,
  137. // to see the oldest 5% to purge from the cache.
  138. if (MaxAge() == 0)
  139. iter->second.ulLastAccess = tNow;
  140. er = erSuccess;
  141. }
  142. } else {
  143. er = KCERR_NOT_FOUND;
  144. }
  145. IncrementHitCount();
  146. if (er == erSuccess)
  147. IncrementValidCount();
  148. return er;
  149. }
  150. ECRESULT GetCacheRange(const key_type &lower, const key_type &upper, std::list<typename _MapType::value_type> *values)
  151. {
  152. auto iLower = m_map.lower_bound(lower);
  153. auto iUpper = m_map.upper_bound(upper);
  154. for (auto i = iLower; i != iUpper; ++i)
  155. values->push_back(*i);
  156. return erSuccess;
  157. }
  158. ECRESULT AddCacheItem(const key_type &key, const mapped_type &value)
  159. {
  160. typedef typename _MapType::value_type value_type;
  161. if (MaxSize() == 0)
  162. return erSuccess;
  163. auto result = m_map.insert(value_type(key, value));
  164. if (result.second == false) {
  165. // The key already exists but its value is unmodified. So update it now
  166. m_ulSize += GetCacheAdditionalSize(value);
  167. m_ulSize -= GetCacheAdditionalSize(result.first->second);
  168. result.first->second = value;
  169. result.first->second.ulLastAccess = GetProcessTime();
  170. // Since there is a very small chance that we need to purge the cache, we're skipping that here.
  171. } else {
  172. // We just inserted a new entry.
  173. m_ulSize += GetCacheAdditionalSize(value);
  174. m_ulSize += GetCacheAdditionalSize(key);
  175. result.first->second.ulLastAccess = GetProcessTime();
  176. UpdateCache(0.05F);
  177. }
  178. return erSuccess;
  179. }
  180. // Used in ECCacheManager::SetCell, where the content of a cache item is modified.
  181. ECRESULT AddToSize(int64_t ulSize)
  182. {
  183. m_ulSize += ulSize;
  184. return erSuccess;
  185. }
  186. private:
  187. ECRESULT PurgeCache(float ratio)
  188. {
  189. std::list<KeyEntry<key_type> > lstEntries;
  190. for (const auto &im : m_map) {
  191. KeyEntry<key_type> k;
  192. k.key = im.first;
  193. k.ulLastAccess = im.second.ulLastAccess;
  194. lstEntries.push_back(std::move(k));
  195. }
  196. lstEntries.sort(KeyEntryOrder<key_type>);
  197. // We now have a list of all cache items, sorted by access time, (oldest first)
  198. unsigned int ulDelete = (unsigned int)(m_map.size() * ratio);
  199. // Remove the oldest ulDelete entries from the cache, removing [ratio] % of all
  200. // cache entries.
  201. for (auto iterEntry = lstEntries.cbegin();
  202. iterEntry != lstEntries.cend() && ulDelete > 0;
  203. ++iterEntry, --ulDelete) {
  204. auto iterMap = m_map.find(iterEntry->key);
  205. assert(iterMap != m_map.end());
  206. m_ulSize -= GetCacheAdditionalSize(iterMap->second);
  207. m_ulSize -= GetCacheAdditionalSize(iterMap->first);
  208. m_map.erase(iterMap);
  209. }
  210. return erSuccess;
  211. }
  212. ECRESULT UpdateCache(float ratio)
  213. {
  214. if( Size() > MaxSize()) {
  215. PurgeCache(ratio);
  216. }
  217. return erSuccess;
  218. }
  219. _MapType m_map;
  220. size_type m_ulSize;
  221. };
  222. } /* namespace */
  223. #endif // ndef ECCACHE_INCLUDED