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