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 */ 47static const char * 48normalize_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. */ 77static svn_error_t * 78read_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 */ 157static svn_error_t * 158warn_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 */ 172static svn_error_t * 173warn_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, */ 184typedef 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. */ 195static apr_status_t 196dump_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 236static apr_status_t 237dump_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 */ 266static svn_error_t * 267init_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 * */ 314static svn_error_t * 315create_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 376svn_error_t * 377svn_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