1251881Speter/*
2251881Speter * cache.c: cache interface for Subversion
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include "cache.h"
25251881Speter
26251881Spetersvn_error_t *
27251881Spetersvn_cache__set_error_handler(svn_cache__t *cache,
28251881Speter                             svn_cache__error_handler_t handler,
29251881Speter                             void *baton,
30251881Speter                             apr_pool_t *scratch_pool)
31251881Speter{
32251881Speter  cache->error_handler = handler;
33251881Speter  cache->error_baton = baton;
34251881Speter  return SVN_NO_ERROR;
35251881Speter}
36251881Speter
37251881Spetersvn_boolean_t
38251881Spetersvn_cache__is_cachable(svn_cache__t *cache,
39251881Speter                       apr_size_t size)
40251881Speter{
41251881Speter  /* having no cache means we can't cache anything */
42251881Speter  if (cache == NULL)
43251881Speter    return FALSE;
44251881Speter
45251881Speter  return cache->vtable->is_cachable(cache->cache_internal, size);
46251881Speter}
47251881Speter
48251881Speter/* Give the error handler callback a chance to replace or ignore the
49251881Speter   error. */
50251881Speterstatic svn_error_t *
51251881Speterhandle_error(svn_cache__t *cache,
52251881Speter             svn_error_t *err,
53251881Speter             apr_pool_t *pool)
54251881Speter{
55251881Speter  if (err)
56251881Speter    {
57251881Speter      cache->failures++;
58251881Speter      if (cache->error_handler)
59251881Speter        err = (cache->error_handler)(err, cache->error_baton, pool);
60251881Speter    }
61251881Speter
62251881Speter  return err;
63251881Speter}
64251881Speter
65251881Speter
66251881Spetersvn_error_t *
67251881Spetersvn_cache__get(void **value_p,
68251881Speter               svn_boolean_t *found,
69251881Speter               svn_cache__t *cache,
70251881Speter               const void *key,
71251881Speter               apr_pool_t *result_pool)
72251881Speter{
73251881Speter  svn_error_t *err;
74251881Speter
75251881Speter  /* In case any errors happen and are quelched, make sure we start
76251881Speter     out with FOUND set to false. */
77251881Speter  *found = FALSE;
78251881Speter#ifdef SVN_DEBUG
79299742Sdim  if (cache->pretend_empty)
80251881Speter    return SVN_NO_ERROR;
81251881Speter#endif
82251881Speter
83251881Speter  cache->reads++;
84251881Speter  err = handle_error(cache,
85251881Speter                     (cache->vtable->get)(value_p,
86251881Speter                                          found,
87251881Speter                                          cache->cache_internal,
88251881Speter                                          key,
89251881Speter                                          result_pool),
90251881Speter                     result_pool);
91251881Speter
92251881Speter  if (*found)
93251881Speter    cache->hits++;
94251881Speter
95251881Speter  return err;
96251881Speter}
97251881Speter
98251881Spetersvn_error_t *
99299742Sdimsvn_cache__has_key(svn_boolean_t *found,
100299742Sdim                   svn_cache__t *cache,
101299742Sdim                   const void *key,
102299742Sdim                   apr_pool_t *scratch_pool)
103299742Sdim{
104299742Sdim  *found = FALSE;
105299742Sdim#ifdef SVN_DEBUG
106299742Sdim  if (cache->pretend_empty)
107299742Sdim    return SVN_NO_ERROR;
108299742Sdim#endif
109299742Sdim
110299742Sdim  return handle_error(cache,
111299742Sdim                      (cache->vtable->has_key)(found,
112299742Sdim                                               cache->cache_internal,
113299742Sdim                                               key,
114299742Sdim                                               scratch_pool),
115299742Sdim                      scratch_pool);
116299742Sdim}
117299742Sdim
118299742Sdimsvn_error_t *
119251881Spetersvn_cache__set(svn_cache__t *cache,
120251881Speter               const void *key,
121251881Speter               void *value,
122251881Speter               apr_pool_t *scratch_pool)
123251881Speter{
124251881Speter  cache->writes++;
125251881Speter  return handle_error(cache,
126251881Speter                      (cache->vtable->set)(cache->cache_internal,
127251881Speter                                           key,
128251881Speter                                           value,
129251881Speter                                           scratch_pool),
130251881Speter                      scratch_pool);
131251881Speter}
132251881Speter
133251881Speter
134251881Spetersvn_error_t *
135251881Spetersvn_cache__iter(svn_boolean_t *completed,
136251881Speter                svn_cache__t *cache,
137251881Speter                svn_iter_apr_hash_cb_t user_cb,
138251881Speter                void *user_baton,
139251881Speter                apr_pool_t *scratch_pool)
140251881Speter{
141251881Speter#ifdef SVN_DEBUG
142299742Sdim  if (cache->pretend_empty)
143251881Speter    /* Pretend CACHE is empty. */
144251881Speter    return SVN_NO_ERROR;
145251881Speter#endif
146251881Speter
147251881Speter  return (cache->vtable->iter)(completed,
148251881Speter                               cache->cache_internal,
149251881Speter                               user_cb,
150251881Speter                               user_baton,
151251881Speter                               scratch_pool);
152251881Speter}
153251881Speter
154251881Spetersvn_error_t *
155251881Spetersvn_cache__get_partial(void **value,
156251881Speter                       svn_boolean_t *found,
157251881Speter                       svn_cache__t *cache,
158251881Speter                       const void *key,
159251881Speter                       svn_cache__partial_getter_func_t func,
160251881Speter                       void *baton,
161251881Speter                       apr_pool_t *result_pool)
162251881Speter{
163251881Speter  svn_error_t *err;
164251881Speter
165251881Speter  /* In case any errors happen and are quelched, make sure we start
166251881Speter  out with FOUND set to false. */
167251881Speter  *found = FALSE;
168251881Speter#ifdef SVN_DEBUG
169299742Sdim  if (cache->pretend_empty)
170251881Speter    return SVN_NO_ERROR;
171251881Speter#endif
172251881Speter
173251881Speter  cache->reads++;
174251881Speter  err = handle_error(cache,
175251881Speter                     (cache->vtable->get_partial)(value,
176251881Speter                                                  found,
177251881Speter                                                  cache->cache_internal,
178251881Speter                                                  key,
179251881Speter                                                  func,
180251881Speter                                                  baton,
181251881Speter                                                  result_pool),
182251881Speter                     result_pool);
183251881Speter
184251881Speter  if (*found)
185251881Speter    cache->hits++;
186251881Speter
187251881Speter  return err;
188251881Speter}
189251881Speter
190251881Spetersvn_error_t *
191251881Spetersvn_cache__set_partial(svn_cache__t *cache,
192251881Speter                       const void *key,
193251881Speter                       svn_cache__partial_setter_func_t func,
194251881Speter                       void *baton,
195251881Speter                       apr_pool_t *scratch_pool)
196251881Speter{
197251881Speter  cache->writes++;
198251881Speter  return handle_error(cache,
199251881Speter                      (cache->vtable->set_partial)(cache->cache_internal,
200251881Speter                                                   key,
201251881Speter                                                   func,
202251881Speter                                                   baton,
203251881Speter                                                   scratch_pool),
204251881Speter                      scratch_pool);
205251881Speter}
206251881Speter
207251881Spetersvn_error_t *
208251881Spetersvn_cache__get_info(svn_cache__t *cache,
209251881Speter                    svn_cache__info_t *info,
210251881Speter                    svn_boolean_t reset,
211251881Speter                    apr_pool_t *result_pool)
212251881Speter{
213251881Speter  /* write general statistics */
214251881Speter
215299742Sdim  memset(info, 0, sizeof(*info));
216251881Speter  info->gets = cache->reads;
217251881Speter  info->hits = cache->hits;
218251881Speter  info->sets = cache->writes;
219251881Speter  info->failures = cache->failures;
220251881Speter
221251881Speter  /* Call the cache implementation for filling the blanks.
222251881Speter   * It might also replace some of the general stats but
223251881Speter   * this is currently not done.
224251881Speter   */
225251881Speter  SVN_ERR((cache->vtable->get_info)(cache->cache_internal,
226251881Speter                                    info,
227251881Speter                                    reset,
228251881Speter                                    result_pool));
229251881Speter
230251881Speter  /* reset statistics */
231251881Speter
232251881Speter  if (reset)
233251881Speter    {
234251881Speter      cache->reads = 0;
235251881Speter      cache->hits = 0;
236251881Speter      cache->writes = 0;
237251881Speter      cache->failures = 0;
238251881Speter    }
239251881Speter
240251881Speter  return SVN_NO_ERROR;
241251881Speter}
242251881Speter
243251881Spetersvn_string_t *
244251881Spetersvn_cache__format_info(const svn_cache__info_t *info,
245299742Sdim                       svn_boolean_t access_only,
246251881Speter                       apr_pool_t *result_pool)
247251881Speter{
248251881Speter  enum { _1MB = 1024 * 1024 };
249251881Speter
250251881Speter  apr_uint64_t misses = info->gets - info->hits;
251251881Speter  double hit_rate = (100.0 * (double)info->hits)
252251881Speter                  / (double)(info->gets ? info->gets : 1);
253251881Speter  double write_rate = (100.0 * (double)info->sets)
254251881Speter                    / (double)(misses ? misses : 1);
255251881Speter  double data_usage_rate = (100.0 * (double)info->used_size)
256251881Speter                         / (double)(info->data_size ? info->data_size : 1);
257251881Speter  double data_entry_rate = (100.0 * (double)info->used_entries)
258251881Speter                 / (double)(info->total_entries ? info->total_entries : 1);
259251881Speter
260299742Sdim  const char *histogram = "";
261299742Sdim  if (!access_only)
262299742Sdim    {
263299742Sdim      svn_stringbuf_t *text = svn_stringbuf_create_empty(result_pool);
264251881Speter
265299742Sdim      int i;
266299742Sdim      int count = sizeof(info->histogram) / sizeof(info->histogram[0]);
267299742Sdim      for (i = count - 1; i >= 0; --i)
268299742Sdim        if (info->histogram[i] > 0 || text->len > 0)
269299742Sdim          text = svn_stringbuf_createf(result_pool,
270299742Sdim                                       i == count - 1
271299742Sdim                                         ? "%s%12" APR_UINT64_T_FMT
272299742Sdim                                           " buckets with >%d entries\n"
273299742Sdim                                         : "%s%12" APR_UINT64_T_FMT
274299742Sdim                                           " buckets with %d entries\n",
275299742Sdim                                       text->data, info->histogram[i], i);
276299742Sdim
277299742Sdim      histogram = text->data;
278299742Sdim    }
279299742Sdim
280299742Sdim  return access_only
281299742Sdim       ? svn_string_createf(result_pool,
282299742Sdim                            "%s\n"
283251881Speter                            "gets    : %" APR_UINT64_T_FMT
284251881Speter                            ", %" APR_UINT64_T_FMT " hits (%5.2f%%)\n"
285251881Speter                            "sets    : %" APR_UINT64_T_FMT
286299742Sdim                            " (%5.2f%% of misses)\n",
287299742Sdim                            info->id,
288299742Sdim                            info->gets,
289299742Sdim                            info->hits, hit_rate,
290299742Sdim                            info->sets, write_rate)
291299742Sdim       : svn_string_createf(result_pool,
292299742Sdim
293299742Sdim                            "%s\n"
294299742Sdim                            "gets    : %" APR_UINT64_T_FMT
295299742Sdim                            ", %" APR_UINT64_T_FMT " hits (%5.2f%%)\n"
296299742Sdim                            "sets    : %" APR_UINT64_T_FMT
297251881Speter                            " (%5.2f%% of misses)\n"
298251881Speter                            "failures: %" APR_UINT64_T_FMT "\n"
299251881Speter                            "used    : %" APR_UINT64_T_FMT " MB (%5.2f%%)"
300251881Speter                            " of %" APR_UINT64_T_FMT " MB data cache"
301251881Speter                            " / %" APR_UINT64_T_FMT " MB total cache memory\n"
302251881Speter                            "          %" APR_UINT64_T_FMT " entries (%5.2f%%)"
303299742Sdim                            " of %" APR_UINT64_T_FMT " total\n%s",
304251881Speter
305251881Speter                            info->id,
306251881Speter
307251881Speter                            info->gets,
308251881Speter                            info->hits, hit_rate,
309251881Speter                            info->sets, write_rate,
310251881Speter                            info->failures,
311251881Speter
312251881Speter                            info->used_size / _1MB, data_usage_rate,
313251881Speter                            info->data_size / _1MB,
314251881Speter                            info->total_size / _1MB,
315251881Speter
316251881Speter                            info->used_entries, data_entry_rate,
317299742Sdim                            info->total_entries,
318299742Sdim                            histogram);
319251881Speter}
320