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
26 svn_error_t *
svn_cache__set_error_handler(svn_cache__t * cache,svn_cache__error_handler_t handler,void * baton,apr_pool_t * scratch_pool)27 svn_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
37 svn_boolean_t
svn_cache__is_cachable(svn_cache__t * cache,apr_size_t size)38 svn_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. */
50 static svn_error_t *
handle_error(svn_cache__t * cache,svn_error_t * err,apr_pool_t * pool)51 handle_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
66 svn_error_t *
svn_cache__get(void ** value_p,svn_boolean_t * found,svn_cache__t * cache,const void * key,apr_pool_t * result_pool)67 svn_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
98 svn_error_t *
svn_cache__has_key(svn_boolean_t * found,svn_cache__t * cache,const void * key,apr_pool_t * scratch_pool)99 svn_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
118 svn_error_t *
svn_cache__set(svn_cache__t * cache,const void * key,void * value,apr_pool_t * scratch_pool)119 svn_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
134 svn_error_t *
svn_cache__iter(svn_boolean_t * completed,svn_cache__t * cache,svn_iter_apr_hash_cb_t user_cb,void * user_baton,apr_pool_t * scratch_pool)135 svn_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
154 svn_error_t *
svn_cache__get_partial(void ** value,svn_boolean_t * found,svn_cache__t * cache,const void * key,svn_cache__partial_getter_func_t func,void * baton,apr_pool_t * result_pool)155 svn_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
190 svn_error_t *
svn_cache__set_partial(svn_cache__t * cache,const void * key,svn_cache__partial_setter_func_t func,void * baton,apr_pool_t * scratch_pool)191 svn_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
207 svn_error_t *
svn_cache__get_info(svn_cache__t * cache,svn_cache__info_t * info,svn_boolean_t reset,apr_pool_t * result_pool)208 svn_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
243 svn_string_t *
svn_cache__format_info(const svn_cache__info_t * info,svn_boolean_t access_only,apr_pool_t * result_pool)244 svn_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