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