1/* 2 * cache.c: cache interface for Subversion 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24#include "cache.h" 25 26svn_error_t * 27svn_cache__set_error_handler(svn_cache__t *cache, 28 svn_cache__error_handler_t handler, 29 void *baton, 30 apr_pool_t *scratch_pool) 31{ 32 cache->error_handler = handler; 33 cache->error_baton = baton; 34 return SVN_NO_ERROR; 35} 36 37svn_boolean_t 38svn_cache__is_cachable(svn_cache__t *cache, 39 apr_size_t size) 40{ 41 /* having no cache means we can't cache anything */ 42 if (cache == NULL) 43 return FALSE; 44 45 return cache->vtable->is_cachable(cache->cache_internal, size); 46} 47 48/* Give the error handler callback a chance to replace or ignore the 49 error. */ 50static svn_error_t * 51handle_error(svn_cache__t *cache, 52 svn_error_t *err, 53 apr_pool_t *pool) 54{ 55 if (err) 56 { 57 cache->failures++; 58 if (cache->error_handler) 59 err = (cache->error_handler)(err, cache->error_baton, pool); 60 } 61 62 return err; 63} 64 65 66svn_error_t * 67svn_cache__get(void **value_p, 68 svn_boolean_t *found, 69 svn_cache__t *cache, 70 const void *key, 71 apr_pool_t *result_pool) 72{ 73 svn_error_t *err; 74 75 /* In case any errors happen and are quelched, make sure we start 76 out with FOUND set to false. */ 77 *found = FALSE; 78#ifdef SVN_DEBUG 79 if (cache->pretend_empty) 80 return SVN_NO_ERROR; 81#endif 82 83 cache->reads++; 84 err = handle_error(cache, 85 (cache->vtable->get)(value_p, 86 found, 87 cache->cache_internal, 88 key, 89 result_pool), 90 result_pool); 91 92 if (*found) 93 cache->hits++; 94 95 return err; 96} 97 98svn_error_t * 99svn_cache__has_key(svn_boolean_t *found, 100 svn_cache__t *cache, 101 const void *key, 102 apr_pool_t *scratch_pool) 103{ 104 *found = FALSE; 105#ifdef SVN_DEBUG 106 if (cache->pretend_empty) 107 return SVN_NO_ERROR; 108#endif 109 110 return handle_error(cache, 111 (cache->vtable->has_key)(found, 112 cache->cache_internal, 113 key, 114 scratch_pool), 115 scratch_pool); 116} 117 118svn_error_t * 119svn_cache__set(svn_cache__t *cache, 120 const void *key, 121 void *value, 122 apr_pool_t *scratch_pool) 123{ 124 cache->writes++; 125 return handle_error(cache, 126 (cache->vtable->set)(cache->cache_internal, 127 key, 128 value, 129 scratch_pool), 130 scratch_pool); 131} 132 133 134svn_error_t * 135svn_cache__iter(svn_boolean_t *completed, 136 svn_cache__t *cache, 137 svn_iter_apr_hash_cb_t user_cb, 138 void *user_baton, 139 apr_pool_t *scratch_pool) 140{ 141#ifdef SVN_DEBUG 142 if (cache->pretend_empty) 143 /* Pretend CACHE is empty. */ 144 return SVN_NO_ERROR; 145#endif 146 147 return (cache->vtable->iter)(completed, 148 cache->cache_internal, 149 user_cb, 150 user_baton, 151 scratch_pool); 152} 153 154svn_error_t * 155svn_cache__get_partial(void **value, 156 svn_boolean_t *found, 157 svn_cache__t *cache, 158 const void *key, 159 svn_cache__partial_getter_func_t func, 160 void *baton, 161 apr_pool_t *result_pool) 162{ 163 svn_error_t *err; 164 165 /* In case any errors happen and are quelched, make sure we start 166 out with FOUND set to false. */ 167 *found = FALSE; 168#ifdef SVN_DEBUG 169 if (cache->pretend_empty) 170 return SVN_NO_ERROR; 171#endif 172 173 cache->reads++; 174 err = handle_error(cache, 175 (cache->vtable->get_partial)(value, 176 found, 177 cache->cache_internal, 178 key, 179 func, 180 baton, 181 result_pool), 182 result_pool); 183 184 if (*found) 185 cache->hits++; 186 187 return err; 188} 189 190svn_error_t * 191svn_cache__set_partial(svn_cache__t *cache, 192 const void *key, 193 svn_cache__partial_setter_func_t func, 194 void *baton, 195 apr_pool_t *scratch_pool) 196{ 197 cache->writes++; 198 return handle_error(cache, 199 (cache->vtable->set_partial)(cache->cache_internal, 200 key, 201 func, 202 baton, 203 scratch_pool), 204 scratch_pool); 205} 206 207svn_error_t * 208svn_cache__get_info(svn_cache__t *cache, 209 svn_cache__info_t *info, 210 svn_boolean_t reset, 211 apr_pool_t *result_pool) 212{ 213 /* write general statistics */ 214 215 memset(info, 0, sizeof(*info)); 216 info->gets = cache->reads; 217 info->hits = cache->hits; 218 info->sets = cache->writes; 219 info->failures = cache->failures; 220 221 /* Call the cache implementation for filling the blanks. 222 * It might also replace some of the general stats but 223 * this is currently not done. 224 */ 225 SVN_ERR((cache->vtable->get_info)(cache->cache_internal, 226 info, 227 reset, 228 result_pool)); 229 230 /* reset statistics */ 231 232 if (reset) 233 { 234 cache->reads = 0; 235 cache->hits = 0; 236 cache->writes = 0; 237 cache->failures = 0; 238 } 239 240 return SVN_NO_ERROR; 241} 242 243svn_string_t * 244svn_cache__format_info(const svn_cache__info_t *info, 245 svn_boolean_t access_only, 246 apr_pool_t *result_pool) 247{ 248 enum { _1MB = 1024 * 1024 }; 249 250 apr_uint64_t misses = info->gets - info->hits; 251 double hit_rate = (100.0 * (double)info->hits) 252 / (double)(info->gets ? info->gets : 1); 253 double write_rate = (100.0 * (double)info->sets) 254 / (double)(misses ? misses : 1); 255 double data_usage_rate = (100.0 * (double)info->used_size) 256 / (double)(info->data_size ? info->data_size : 1); 257 double data_entry_rate = (100.0 * (double)info->used_entries) 258 / (double)(info->total_entries ? info->total_entries : 1); 259 260 const char *histogram = ""; 261 if (!access_only) 262 { 263 svn_stringbuf_t *text = svn_stringbuf_create_empty(result_pool); 264 265 int i; 266 int count = sizeof(info->histogram) / sizeof(info->histogram[0]); 267 for (i = count - 1; i >= 0; --i) 268 if (info->histogram[i] > 0 || text->len > 0) 269 text = svn_stringbuf_createf(result_pool, 270 i == count - 1 271 ? "%s%12" APR_UINT64_T_FMT 272 " buckets with >%d entries\n" 273 : "%s%12" APR_UINT64_T_FMT 274 " buckets with %d entries\n", 275 text->data, info->histogram[i], i); 276 277 histogram = text->data; 278 } 279 280 return access_only 281 ? svn_string_createf(result_pool, 282 "%s\n" 283 "gets : %" APR_UINT64_T_FMT 284 ", %" APR_UINT64_T_FMT " hits (%5.2f%%)\n" 285 "sets : %" APR_UINT64_T_FMT 286 " (%5.2f%% of misses)\n", 287 info->id, 288 info->gets, 289 info->hits, hit_rate, 290 info->sets, write_rate) 291 : svn_string_createf(result_pool, 292 293 "%s\n" 294 "gets : %" APR_UINT64_T_FMT 295 ", %" APR_UINT64_T_FMT " hits (%5.2f%%)\n" 296 "sets : %" APR_UINT64_T_FMT 297 " (%5.2f%% of misses)\n" 298 "failures: %" APR_UINT64_T_FMT "\n" 299 "used : %" APR_UINT64_T_FMT " MB (%5.2f%%)" 300 " of %" APR_UINT64_T_FMT " MB data cache" 301 " / %" APR_UINT64_T_FMT " MB total cache memory\n" 302 " %" APR_UINT64_T_FMT " entries (%5.2f%%)" 303 " of %" APR_UINT64_T_FMT " total\n%s", 304 305 info->id, 306 307 info->gets, 308 info->hits, hit_rate, 309 info->sets, write_rate, 310 info->failures, 311 312 info->used_size / _1MB, data_usage_rate, 313 info->data_size / _1MB, 314 info->total_size / _1MB, 315 316 info->used_entries, data_entry_rate, 317 info->total_entries, 318 histogram); 319} 320