1 /* caching.c : in-memory caching
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22 
23 #include "fs.h"
24 #include "fs_x.h"
25 #include "id.h"
26 #include "dag_cache.h"
27 #include "index.h"
28 #include "changes.h"
29 #include "noderevs.h"
30 #include "temp_serializer.h"
31 #include "reps.h"
32 #include "../libsvn_fs/fs-loader.h"
33 
34 #include "svn_config.h"
35 #include "svn_cache_config.h"
36 
37 #include "svn_private_config.h"
38 #include "svn_hash.h"
39 #include "svn_pools.h"
40 
41 #include "private/svn_debug.h"
42 #include "private/svn_subr_private.h"
43 
44 /* Take the ORIGINAL string and replace all occurrences of ":" without
45  * limiting the key space.  Allocate the result in RESULT_POOL.
46  */
47 static const char *
normalize_key_part(const char * original,apr_pool_t * result_pool)48 normalize_key_part(const char *original,
49                    apr_pool_t *result_pool)
50 {
51   apr_size_t i;
52   apr_size_t len = strlen(original);
53   svn_stringbuf_t *normalized = svn_stringbuf_create_ensure(len,
54                                                             result_pool);
55 
56   for (i = 0; i < len; ++i)
57     {
58       char c = original[i];
59       switch (c)
60         {
61         case ':': svn_stringbuf_appendbytes(normalized, "%_", 2);
62                   break;
63         case '%': svn_stringbuf_appendbytes(normalized, "%%", 2);
64                   break;
65         default : svn_stringbuf_appendbyte(normalized, c);
66         }
67     }
68 
69   return normalized->data;
70 }
71 
72 /* *CACHE_TXDELTAS, *CACHE_FULLTEXTS, *CACHE_REVPROPS and *CACHE_NODEPROPS
73    flags will be set according to FS->CONFIG.  *CACHE_NAMESPACE receives
74    the cache prefix to use.
75 
76    Allocate CACHE_NAMESPACE in RESULT_POOL. */
77 static svn_error_t *
read_config(const char ** cache_namespace,svn_boolean_t * cache_txdeltas,svn_boolean_t * cache_fulltexts,svn_boolean_t * cache_revprops,svn_boolean_t * cache_nodeprops,svn_fs_t * fs,apr_pool_t * result_pool)78 read_config(const char **cache_namespace,
79             svn_boolean_t *cache_txdeltas,
80             svn_boolean_t *cache_fulltexts,
81             svn_boolean_t *cache_revprops,
82             svn_boolean_t *cache_nodeprops,
83             svn_fs_t *fs,
84             apr_pool_t *result_pool)
85 {
86   /* No cache namespace by default.  I.e. all FS instances share the
87    * cached data.  If you specify different namespaces, the data will
88    * share / compete for the same cache memory but keys will not match
89    * across namespaces and, thus, cached data will not be shared between
90    * namespaces.
91    *
92    * Since the namespace will be concatenated with other elements to form
93    * the complete key prefix, we must make sure that the resulting string
94    * is unique and cannot be created by any other combination of elements.
95    */
96   *cache_namespace
97     = normalize_key_part(svn_hash__get_cstring(fs->config,
98                                                SVN_FS_CONFIG_FSFS_CACHE_NS,
99                                                ""),
100                          result_pool);
101 
102   /* don't cache text deltas by default.
103    * Once we reconstructed the fulltexts from the deltas,
104    * these deltas are rarely re-used. Therefore, only tools
105    * like svnadmin will activate this to speed up operations
106    * dump and verify.
107    */
108   *cache_txdeltas
109     = svn_hash__get_bool(fs->config,
110                          SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
111                          TRUE);
112 
113   /* by default, cache fulltexts.
114    * Most SVN tools care about reconstructed file content.
115    * Thus, this is a reasonable default.
116    * SVN admin tools may set that to FALSE because fulltexts
117    * won't be re-used rendering the cache less effective
118    * by squeezing wanted data out.
119    */
120   *cache_fulltexts
121     = svn_hash__get_bool(fs->config,
122                          SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
123                          TRUE);
124 
125   /* don't cache revprops by default.
126    * Revprop caching significantly speeds up operations like
127    * svn ls -v. However, it requires synchronization that may
128    * not be available or efficient in the current server setup.
129    * Option "2" is equivalent to "1".
130    */
131   if (strcmp(svn_hash__get_cstring(fs->config,
132                                    SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
133                                    ""), "2"))
134     *cache_revprops
135       = svn_hash__get_bool(fs->config,
136                           SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
137                           FALSE);
138   else
139     *cache_revprops = TRUE;
140 
141   /* by default, cache nodeprops: this will match pre-1.10
142    * behavior where node properties caching was controlled
143    * by SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS configuration option.
144    */
145   *cache_nodeprops
146     = svn_hash__get_bool(fs->config,
147                          SVN_FS_CONFIG_FSFS_CACHE_NODEPROPS,
148                          TRUE);
149 
150   return SVN_NO_ERROR;
151 }
152 
153 
154 /* Implements svn_cache__error_handler_t
155  * This variant clears the error after logging it.
156  */
157 static svn_error_t *
warn_and_continue_on_cache_errors(svn_error_t * err,void * baton,apr_pool_t * pool)158 warn_and_continue_on_cache_errors(svn_error_t *err,
159                                   void *baton,
160                                   apr_pool_t *pool)
161 {
162   svn_fs_t *fs = baton;
163   (fs->warning)(fs->warning_baton, err);
164   svn_error_clear(err);
165 
166   return SVN_NO_ERROR;
167 }
168 
169 /* Implements svn_cache__error_handler_t
170  * This variant logs the error and passes it on to the callers.
171  */
172 static svn_error_t *
warn_and_fail_on_cache_errors(svn_error_t * err,void * baton,apr_pool_t * pool)173 warn_and_fail_on_cache_errors(svn_error_t *err,
174                               void *baton,
175                               apr_pool_t *pool)
176 {
177   svn_fs_t *fs = baton;
178   (fs->warning)(fs->warning_baton, err);
179   return err;
180 }
181 
182 #ifdef SVN_DEBUG_CACHE_DUMP_STATS
183 /* Baton to be used for the dump_cache_statistics() pool cleanup function, */
184 typedef struct dump_cache_baton_t
185 {
186   /* the pool about to be cleaned up. Will be used for temp. allocations. */
187   apr_pool_t *pool;
188 
189   /* the cache to dump the statistics for */
190   svn_cache__t *cache;
191 } dump_cache_baton_t;
192 
193 /* APR pool cleanup handler that will printf the statistics of the
194    cache referenced by the baton in BATON_VOID. */
195 static apr_status_t
dump_cache_statistics(void * baton_void)196 dump_cache_statistics(void *baton_void)
197 {
198   dump_cache_baton_t *baton = baton_void;
199 
200   apr_status_t result = APR_SUCCESS;
201   svn_cache__info_t info;
202   svn_string_t *text_stats;
203   apr_array_header_t *lines;
204   int i;
205 
206   svn_error_t *err = svn_cache__get_info(baton->cache,
207                                          &info,
208                                          TRUE,
209                                          baton->pool);
210 
211   /* skip unused caches */
212   if (! err && (info.gets > 0 || info.sets > 0))
213     {
214       text_stats = svn_cache__format_info(&info, TRUE, baton->pool);
215       lines = svn_cstring_split(text_stats->data, "\n", FALSE, baton->pool);
216 
217       for (i = 0; i < lines->nelts; ++i)
218         {
219           const char *line = APR_ARRAY_IDX(lines, i, const char *);
220 #ifdef SVN_DEBUG
221           SVN_DBG(("%s\n", line));
222 #endif
223         }
224     }
225 
226   /* process error returns */
227   if (err)
228     {
229       result = err->apr_err;
230       svn_error_clear(err);
231     }
232 
233   return result;
234 }
235 
236 static apr_status_t
dump_global_cache_statistics(void * baton_void)237 dump_global_cache_statistics(void *baton_void)
238 {
239   apr_pool_t *pool = baton_void;
240 
241   svn_cache__info_t *info = svn_cache__membuffer_get_global_info(pool);
242   svn_string_t *text_stats = svn_cache__format_info(info, FALSE, pool);
243   apr_array_header_t *lines = svn_cstring_split(text_stats->data, "\n",
244                                                 FALSE, pool);
245 
246   int i;
247   for (i = 0; i < lines->nelts; ++i)
248     {
249       const char *line = APR_ARRAY_IDX(lines, i, const char *);
250 #ifdef SVN_DEBUG
251       SVN_DBG(("%s\n", line));
252 #endif
253     }
254 
255   return APR_SUCCESS;
256 }
257 
258 #endif /* SVN_DEBUG_CACHE_DUMP_STATS */
259 
260 /* This function sets / registers the required callbacks for a given
261  * not transaction-specific CACHE object in FS, if CACHE is not NULL.
262  *
263  * All these svn_cache__t instances shall be handled uniformly. Unless
264  * ERROR_HANDLER is NULL, register it for the given CACHE in FS.
265  */
266 static svn_error_t *
init_callbacks(svn_cache__t * cache,svn_fs_t * fs,svn_cache__error_handler_t error_handler,apr_pool_t * pool)267 init_callbacks(svn_cache__t *cache,
268                svn_fs_t *fs,
269                svn_cache__error_handler_t error_handler,
270                apr_pool_t *pool)
271 {
272 #ifdef SVN_DEBUG_CACHE_DUMP_STATS
273 
274   /* schedule printing the access statistics upon pool cleanup,
275    * i.e. end of FSX session.
276    */
277   dump_cache_baton_t *baton;
278 
279   baton = apr_palloc(pool, sizeof(*baton));
280   baton->pool = pool;
281   baton->cache = cache;
282 
283   apr_pool_cleanup_register(pool,
284                             baton,
285                             dump_cache_statistics,
286                             apr_pool_cleanup_null);
287 #endif
288 
289   if (error_handler)
290     SVN_ERR(svn_cache__set_error_handler(cache,
291                                           error_handler,
292                                           fs,
293                                           pool));
294 
295   return SVN_NO_ERROR;
296 }
297 
298 /* Sets *CACHE_P to cache instance based on provided options.
299  *
300  * If DUMMY_CACHE is set, create a null cache.  Otherwise, creates a memcache
301  * if MEMCACHE is not NULL or a membuffer cache if MEMBUFFER is not NULL.
302  * Falls back to inprocess cache if no other cache type has been selected
303  * and PAGES is not 0.  Create a null cache otherwise.
304  *
305  * Use the given PRIORITY class for the new cache.  If PRIORITY is 0, then
306  * use the default priority class. HAS_NAMESPACE indicates whether we
307  * prefixed this cache instance with a namespace.
308  *
309  * Unless NO_HANDLER is true, register an error handler that reports errors
310  * as warnings to the FS warning callback.
311  *
312  * Cache is allocated in RESULT_POOL, temporaries in SCRATCH_POOL.
313  * */
314 static svn_error_t *
create_cache(svn_cache__t ** cache_p,svn_memcache_t * memcache,svn_membuffer_t * membuffer,apr_int64_t pages,apr_int64_t items_per_page,svn_cache__serialize_func_t serializer,svn_cache__deserialize_func_t deserializer,apr_ssize_t klen,const char * prefix,apr_uint32_t priority,svn_boolean_t has_namespace,svn_fs_t * fs,svn_boolean_t no_handler,svn_boolean_t dummy_cache,apr_pool_t * result_pool,apr_pool_t * scratch_pool)315 create_cache(svn_cache__t **cache_p,
316              svn_memcache_t *memcache,
317              svn_membuffer_t *membuffer,
318              apr_int64_t pages,
319              apr_int64_t items_per_page,
320              svn_cache__serialize_func_t serializer,
321              svn_cache__deserialize_func_t deserializer,
322              apr_ssize_t klen,
323              const char *prefix,
324              apr_uint32_t priority,
325              svn_boolean_t has_namespace,
326              svn_fs_t *fs,
327              svn_boolean_t no_handler,
328              svn_boolean_t dummy_cache,
329              apr_pool_t *result_pool,
330              apr_pool_t *scratch_pool)
331 {
332   svn_cache__error_handler_t error_handler = no_handler
333                                            ? NULL
334                                            : warn_and_fail_on_cache_errors;
335   if (priority == 0)
336     priority = SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY;
337 
338   if (dummy_cache)
339     {
340       SVN_ERR(svn_cache__create_null(cache_p, prefix, result_pool));
341     }
342   else if (memcache)
343     {
344       SVN_ERR(svn_cache__create_memcache(cache_p, memcache,
345                                          serializer, deserializer, klen,
346                                          prefix, result_pool));
347       error_handler = no_handler
348                     ? NULL
349                     : warn_and_continue_on_cache_errors;
350     }
351   else if (membuffer)
352     {
353       /* We assume caches with namespaces to be relatively short-lived,
354        * i.e. their data will not be needed after a while. */
355       SVN_ERR(svn_cache__create_membuffer_cache(
356                 cache_p, membuffer, serializer, deserializer,
357                 klen, prefix, priority, FALSE, has_namespace,
358                 result_pool, scratch_pool));
359     }
360   else if (pages)
361     {
362       SVN_ERR(svn_cache__create_inprocess(
363                 cache_p, serializer, deserializer, klen, pages,
364                 items_per_page, FALSE, prefix, result_pool));
365     }
366   else
367     {
368       SVN_ERR(svn_cache__create_null(cache_p, prefix, result_pool));
369     }
370 
371   SVN_ERR(init_callbacks(*cache_p, fs, error_handler, result_pool));
372 
373   return SVN_NO_ERROR;
374 }
375 
376 svn_error_t *
svn_fs_x__initialize_caches(svn_fs_t * fs,apr_pool_t * scratch_pool)377 svn_fs_x__initialize_caches(svn_fs_t *fs,
378                             apr_pool_t *scratch_pool)
379 {
380   svn_fs_x__data_t *ffd = fs->fsap_data;
381   const char *prefix = apr_pstrcat(scratch_pool,
382                                    "fsx:", fs->uuid,
383                                    "--", ffd->instance_id,
384                                    "/", normalize_key_part(fs->path,
385                                                            scratch_pool),
386                                    ":",
387                                    SVN_VA_NULL);
388   svn_membuffer_t *membuffer;
389   svn_boolean_t no_handler = ffd->fail_stop;
390   svn_boolean_t cache_txdeltas;
391   svn_boolean_t cache_fulltexts;
392   svn_boolean_t cache_revprops;
393   svn_boolean_t cache_nodeprops;
394   const char *cache_namespace;
395   svn_boolean_t has_namespace;
396 
397   /* Evaluating the cache configuration. */
398   SVN_ERR(read_config(&cache_namespace,
399                       &cache_txdeltas,
400                       &cache_fulltexts,
401                       &cache_revprops,
402                       &cache_nodeprops,
403                       fs,
404                       scratch_pool));
405 
406   prefix = apr_pstrcat(scratch_pool, "ns:", cache_namespace, ":", prefix,
407                        SVN_VA_NULL);
408   has_namespace = strlen(cache_namespace) > 0;
409 
410   membuffer = svn_cache__get_global_membuffer_cache();
411 
412   /* General rules for assigning cache priorities:
413    *
414    * - Data that can be reconstructed from other elements has low prio
415    *   (e.g. fulltexts etc.)
416    * - Index data required to find any of the other data has high prio
417    *   (e.g. noderevs, L2P and P2L index pages)
418    * - everthing else should use default prio
419    */
420 
421 #ifdef SVN_DEBUG_CACHE_DUMP_STATS
422 
423   /* schedule printing the global access statistics upon pool cleanup,
424    * i.e. end of FSX session.
425    */
426   if (membuffer)
427     apr_pool_cleanup_register(fs->pool,
428                               fs->pool,
429                               dump_global_cache_statistics,
430                               apr_pool_cleanup_null);
431 #endif
432 
433   /* 1st level DAG node cache */
434   ffd->dag_node_cache = svn_fs_x__create_dag_cache(fs->pool);
435 
436   /* Very rough estimate: 1K per directory. */
437   SVN_ERR(create_cache(&(ffd->dir_cache),
438                        NULL,
439                        membuffer,
440                        1024, 8,
441                        svn_fs_x__serialize_dir_entries,
442                        svn_fs_x__deserialize_dir_entries,
443                        sizeof(svn_fs_x__id_t),
444                        apr_pstrcat(scratch_pool, prefix, "DIR", SVN_VA_NULL),
445                        SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
446                        has_namespace,
447                        fs,
448                        no_handler, FALSE,
449                        fs->pool, scratch_pool));
450 
451   /* initialize node revision cache, if caching has been enabled */
452   SVN_ERR(create_cache(&(ffd->node_revision_cache),
453                        NULL,
454                        membuffer,
455                        32, 32, /* ~200 byte / entry; 1k entries total */
456                        svn_fs_x__serialize_node_revision,
457                        svn_fs_x__deserialize_node_revision,
458                        sizeof(svn_fs_x__pair_cache_key_t),
459                        apr_pstrcat(scratch_pool, prefix, "NODEREVS",
460                                    SVN_VA_NULL),
461                        SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
462                        has_namespace,
463                        fs,
464                        no_handler, FALSE,
465                        fs->pool, scratch_pool));
466 
467   /* initialize representation header cache, if caching has been enabled */
468   SVN_ERR(create_cache(&(ffd->rep_header_cache),
469                        NULL,
470                        membuffer,
471                        1, 1000, /* ~8 bytes / entry; 1k entries total */
472                        svn_fs_x__serialize_rep_header,
473                        svn_fs_x__deserialize_rep_header,
474                        sizeof(svn_fs_x__representation_cache_key_t),
475                        apr_pstrcat(scratch_pool, prefix, "REPHEADER",
476                                    SVN_VA_NULL),
477                        SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
478                        has_namespace,
479                        fs,
480                        no_handler, FALSE,
481                        fs->pool, scratch_pool));
482 
483   /* initialize node change list cache, if caching has been enabled */
484   SVN_ERR(create_cache(&(ffd->changes_cache),
485                        NULL,
486                        membuffer,
487                        1, 8, /* 1k / entry; 8 entries total, rarely used */
488                        svn_fs_x__serialize_changes,
489                        svn_fs_x__deserialize_changes,
490                        sizeof(svn_fs_x__pair_cache_key_t),
491                        apr_pstrcat(scratch_pool, prefix, "CHANGES",
492                                    SVN_VA_NULL),
493                        0,
494                        has_namespace,
495                        fs,
496                        no_handler, FALSE,
497                        fs->pool, scratch_pool));
498 
499   /* if enabled, cache fulltext and other derived information */
500   SVN_ERR(create_cache(&(ffd->fulltext_cache),
501                        ffd->memcache,
502                        membuffer,
503                        0, 0, /* Do not use inprocess cache */
504                        /* Values are svn_stringbuf_t */
505                        NULL, NULL,
506                        sizeof(svn_fs_x__pair_cache_key_t),
507                        apr_pstrcat(scratch_pool, prefix, "TEXT",
508                                    SVN_VA_NULL),
509                        SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
510                        has_namespace,
511                        fs,
512                        no_handler, !cache_fulltexts,
513                        fs->pool, scratch_pool));
514 
515   SVN_ERR(create_cache(&(ffd->properties_cache),
516                        NULL,
517                        membuffer,
518                        0, 0, /* Do not use inprocess cache */
519                        svn_fs_x__serialize_properties,
520                        svn_fs_x__deserialize_properties,
521                        sizeof(svn_fs_x__pair_cache_key_t),
522                        apr_pstrcat(scratch_pool, prefix, "PROP",
523                                    SVN_VA_NULL),
524                        SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
525                        has_namespace,
526                        fs,
527                        no_handler, !cache_nodeprops,
528                        fs->pool, scratch_pool));
529 
530   /* if enabled, cache revprops */
531   SVN_ERR(create_cache(&(ffd->revprop_cache),
532                        NULL,
533                        membuffer,
534                        0, 0, /* Do not use inprocess cache */
535                        svn_fs_x__serialize_properties,
536                        svn_fs_x__deserialize_properties,
537                        sizeof(svn_fs_x__pair_cache_key_t),
538                        apr_pstrcat(scratch_pool, prefix, "REVPROP",
539                                    SVN_VA_NULL),
540                        SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
541                        has_namespace,
542                        fs,
543                        no_handler, !cache_revprops,
544                        fs->pool, scratch_pool));
545 
546   /* if enabled, cache text deltas and their combinations */
547   SVN_ERR(create_cache(&(ffd->txdelta_window_cache),
548                        NULL,
549                        membuffer,
550                        0, 0, /* Do not use inprocess cache */
551                        svn_fs_x__serialize_txdelta_window,
552                        svn_fs_x__deserialize_txdelta_window,
553                        sizeof(svn_fs_x__window_cache_key_t),
554                        apr_pstrcat(scratch_pool, prefix, "TXDELTA_WINDOW",
555                                    SVN_VA_NULL),
556                        SVN_CACHE__MEMBUFFER_LOW_PRIORITY,
557                        has_namespace,
558                        fs,
559                        no_handler, !cache_txdeltas,
560                        fs->pool, scratch_pool));
561 
562   SVN_ERR(create_cache(&(ffd->combined_window_cache),
563                        NULL,
564                        membuffer,
565                        0, 0, /* Do not use inprocess cache */
566                        /* Values are svn_stringbuf_t */
567                        NULL, NULL,
568                        sizeof(svn_fs_x__window_cache_key_t),
569                        apr_pstrcat(scratch_pool, prefix, "COMBINED_WINDOW",
570                                    SVN_VA_NULL),
571                        SVN_CACHE__MEMBUFFER_LOW_PRIORITY,
572                        has_namespace,
573                        fs,
574                        no_handler, !cache_txdeltas,
575                        fs->pool, scratch_pool));
576 
577   /* Caches for our various container types. */
578   SVN_ERR(create_cache(&(ffd->noderevs_container_cache),
579                        NULL,
580                        membuffer,
581                        16, 4, /* Important, largish objects */
582                        svn_fs_x__serialize_noderevs_container,
583                        svn_fs_x__deserialize_noderevs_container,
584                        sizeof(svn_fs_x__pair_cache_key_t),
585                        apr_pstrcat(scratch_pool, prefix, "NODEREVSCNT",
586                                    SVN_VA_NULL),
587                        SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
588                        has_namespace,
589                        fs,
590                        no_handler, FALSE,
591                        fs->pool, scratch_pool));
592   SVN_ERR(create_cache(&(ffd->changes_container_cache),
593                        NULL,
594                        membuffer,
595                        0, 0, /* Do not use inprocess cache */
596                        svn_fs_x__serialize_changes_container,
597                        svn_fs_x__deserialize_changes_container,
598                        sizeof(svn_fs_x__pair_cache_key_t),
599                        apr_pstrcat(scratch_pool, prefix, "CHANGESCNT",
600                                    SVN_VA_NULL),
601                        0,
602                        has_namespace,
603                        fs,
604                        no_handler, FALSE,
605                        fs->pool, scratch_pool));
606   SVN_ERR(create_cache(&(ffd->reps_container_cache),
607                        NULL,
608                        membuffer,
609                        0, 0, /* Do not use inprocess cache */
610                        svn_fs_x__serialize_reps_container,
611                        svn_fs_x__deserialize_reps_container,
612                        sizeof(svn_fs_x__pair_cache_key_t),
613                        apr_pstrcat(scratch_pool, prefix, "REPSCNT",
614                                    SVN_VA_NULL),
615                        0,
616                        has_namespace,
617                        fs,
618                        no_handler, FALSE,
619                        fs->pool, scratch_pool));
620 
621   /* Cache index info. */
622   SVN_ERR(create_cache(&(ffd->l2p_header_cache),
623                        NULL,
624                        membuffer,
625                        64, 16, /* entry size varies but we must cover
626                                   a reasonable number of revisions (1k) */
627                        svn_fs_x__serialize_l2p_header,
628                        svn_fs_x__deserialize_l2p_header,
629                        sizeof(svn_fs_x__pair_cache_key_t),
630                        apr_pstrcat(scratch_pool, prefix, "L2P_HEADER",
631                                    SVN_VA_NULL),
632                        SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
633                        has_namespace,
634                        fs,
635                        no_handler, FALSE,
636                        fs->pool, scratch_pool));
637   SVN_ERR(create_cache(&(ffd->l2p_page_cache),
638                        NULL,
639                        membuffer,
640                        64, 16, /* entry size varies but we must cover
641                                   a reasonable number of revisions (1k) */
642                        svn_fs_x__serialize_l2p_page,
643                        svn_fs_x__deserialize_l2p_page,
644                        sizeof(svn_fs_x__page_cache_key_t),
645                        apr_pstrcat(scratch_pool, prefix, "L2P_PAGE",
646                                    SVN_VA_NULL),
647                        SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
648                        has_namespace,
649                        fs,
650                        no_handler, FALSE,
651                        fs->pool, scratch_pool));
652   SVN_ERR(create_cache(&(ffd->p2l_header_cache),
653                        NULL,
654                        membuffer,
655                        4, 1, /* Large entries. Rarely used. */
656                        svn_fs_x__serialize_p2l_header,
657                        svn_fs_x__deserialize_p2l_header,
658                        sizeof(svn_fs_x__pair_cache_key_t),
659                        apr_pstrcat(scratch_pool, prefix, "P2L_HEADER",
660                                    SVN_VA_NULL),
661                        SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
662                        has_namespace,
663                        fs,
664                        no_handler, FALSE,
665                        fs->pool, scratch_pool));
666   SVN_ERR(create_cache(&(ffd->p2l_page_cache),
667                        NULL,
668                        membuffer,
669                        4, 16, /* Variably sized entries. Rarely used. */
670                        svn_fs_x__serialize_p2l_page,
671                        svn_fs_x__deserialize_p2l_page,
672                        sizeof(svn_fs_x__page_cache_key_t),
673                        apr_pstrcat(scratch_pool, prefix, "P2L_PAGE",
674                                    SVN_VA_NULL),
675                        SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
676                        has_namespace,
677                        fs,
678                        no_handler, FALSE,
679                        fs->pool, scratch_pool));
680 
681   return SVN_NO_ERROR;
682 }
683