diff options
Diffstat (limited to 'subversion/libsvn_fs_fs/caching.c')
-rw-r--r-- | subversion/libsvn_fs_fs/caching.c | 347 |
1 files changed, 266 insertions, 81 deletions
diff --git a/subversion/libsvn_fs_fs/caching.c b/subversion/libsvn_fs_fs/caching.c index 4f2a34c..42898cb 100644 --- a/subversion/libsvn_fs_fs/caching.c +++ b/subversion/libsvn_fs_fs/caching.c @@ -24,6 +24,7 @@ #include "fs_fs.h" #include "id.h" #include "dag.h" +#include "tree.h" #include "temp_serializer.h" #include "../libsvn_fs/fs-loader.h" @@ -32,25 +33,79 @@ #include "svn_private_config.h" #include "svn_hash.h" +#include "svn_pools.h" + #include "private/svn_debug.h" +#include "private/svn_subr_private.h" + +/* Take the ORIGINAL string and replace all occurrences of ":" without + * limiting the key space. Allocate the result in POOL. + */ +static const char * +normalize_key_part(const char *original, + apr_pool_t *pool) +{ + apr_size_t i; + apr_size_t len = strlen(original); + svn_stringbuf_t *normalized = svn_stringbuf_create_ensure(len, pool); + + for (i = 0; i < len; ++i) + { + char c = original[i]; + switch (c) + { + case ':': svn_stringbuf_appendbytes(normalized, "%_", 2); + break; + case '%': svn_stringbuf_appendbytes(normalized, "%%", 2); + break; + default : svn_stringbuf_appendbyte(normalized, c); + } + } + + return normalized->data; +} /* Return a memcache in *MEMCACHE_P for FS if it's configured to use memcached, or NULL otherwise. Also, sets *FAIL_STOP to a boolean indicating whether cache errors should be returned to the caller or - just passed to the FS warning handler. Use FS->pool for allocating - the memcache, and POOL for temporary allocations. */ + just passed to the FS warning handler. + + *CACHE_TXDELTAS, *CACHE_FULLTEXTS and *CACHE_REVPROPS flags will be set + according to FS->CONFIG. *CACHE_NAMESPACE receives the cache prefix + to use. + + Use FS->pool for allocating the memcache and CACHE_NAMESPACE, and POOL + for temporary allocations. */ static svn_error_t * read_config(svn_memcache_t **memcache_p, svn_boolean_t *fail_stop, + const char **cache_namespace, svn_boolean_t *cache_txdeltas, svn_boolean_t *cache_fulltexts, + svn_boolean_t *cache_revprops, svn_fs_t *fs, apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; SVN_ERR(svn_cache__make_memcache_from_config(memcache_p, ffd->config, - fs->pool)); + fs->pool)); + + /* No cache namespace by default. I.e. all FS instances share the + * cached data. If you specify different namespaces, the data will + * share / compete for the same cache memory but keys will not match + * across namespaces and, thus, cached data will not be shared between + * namespaces. + * + * Since the namespace will be concatenated with other elements to form + * the complete key prefix, we must make sure that the resulting string + * is unique and cannot be created by any other combination of elements. + */ + *cache_namespace + = normalize_key_part(svn_hash__get_cstring(fs->config, + SVN_FS_CONFIG_FSFS_CACHE_NS, + ""), + pool); /* don't cache text deltas by default. * Once we reconstructed the fulltexts from the deltas, @@ -74,24 +129,44 @@ read_config(svn_memcache_t **memcache_p, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, TRUE); + /* For now, always disable revprop caching. + */ + *cache_revprops = FALSE; + return svn_config_get_bool(ffd->config, fail_stop, CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, FALSE); } -/* Implements svn_cache__error_handler_t */ +/* Implements svn_cache__error_handler_t + * This variant clears the error after logging it. + */ static svn_error_t * -warn_on_cache_errors(svn_error_t *err, - void *baton, - apr_pool_t *pool) +warn_and_continue_on_cache_errors(svn_error_t *err, + void *baton, + apr_pool_t *pool) { svn_fs_t *fs = baton; (fs->warning)(fs->warning_baton, err); svn_error_clear(err); + return SVN_NO_ERROR; } +/* Implements svn_cache__error_handler_t + * This variant logs the error and passes it on to the callers. + */ +static svn_error_t * +warn_and_fail_on_cache_errors(svn_error_t *err, + void *baton, + apr_pool_t *pool) +{ + svn_fs_t *fs = baton; + (fs->warning)(fs->warning_baton, err); + return err; +} + #ifdef SVN_DEBUG_CACHE_DUMP_STATS /* Baton to be used for the dump_cache_statistics() pool cleanup function, */ struct dump_cache_baton_t @@ -150,13 +225,12 @@ dump_cache_statistics(void *baton_void) * not transaction-specific CACHE object in FS, if CACHE is not NULL. * * All these svn_cache__t instances shall be handled uniformly. Unless - * NO_HANDLER is true, register an error handler that reports errors - * as warnings for the given CACHE. + * ERROR_HANDLER is NULL, register it for the given CACHE in FS. */ static svn_error_t * init_callbacks(svn_cache__t *cache, svn_fs_t *fs, - svn_boolean_t no_handler, + svn_cache__error_handler_t error_handler, apr_pool_t *pool) { if (cache != NULL) @@ -178,9 +252,9 @@ init_callbacks(svn_cache__t *cache, apr_pool_cleanup_null); #endif - if (! no_handler) + if (error_handler) SVN_ERR(svn_cache__set_error_handler(cache, - warn_on_cache_errors, + error_handler, fs, pool)); @@ -195,6 +269,9 @@ init_callbacks(svn_cache__t *cache, * MEMBUFFER are NULL and pages is non-zero. Sets *CACHE_P to NULL * otherwise. * + * Unless NO_HANDLER is true, register an error handler that reports errors + * as warnings to the FS warning callback. + * * Cache is allocated in POOL. * */ static svn_error_t * @@ -207,32 +284,43 @@ create_cache(svn_cache__t **cache_p, svn_cache__deserialize_func_t deserializer, apr_ssize_t klen, const char *prefix, + svn_fs_t *fs, + svn_boolean_t no_handler, apr_pool_t *pool) { - if (memcache) - { - SVN_ERR(svn_cache__create_memcache(cache_p, memcache, - serializer, deserializer, klen, - prefix, pool)); - } - else if (membuffer) - { - SVN_ERR(svn_cache__create_membuffer_cache( - cache_p, membuffer, serializer, deserializer, - klen, prefix, pool)); - } - else if (pages) - { - SVN_ERR(svn_cache__create_inprocess( - cache_p, serializer, deserializer, klen, pages, - items_per_page, FALSE, prefix, pool)); - } - else + svn_cache__error_handler_t error_handler = no_handler + ? NULL + : warn_and_fail_on_cache_errors; + + if (memcache) + { + SVN_ERR(svn_cache__create_memcache(cache_p, memcache, + serializer, deserializer, klen, + prefix, pool)); + error_handler = no_handler + ? NULL + : warn_and_continue_on_cache_errors; + } + else if (membuffer) + { + SVN_ERR(svn_cache__create_membuffer_cache( + cache_p, membuffer, serializer, deserializer, + klen, prefix, FALSE, pool)); + } + else if (pages) + { + SVN_ERR(svn_cache__create_inprocess( + cache_p, serializer, deserializer, klen, pages, + items_per_page, FALSE, prefix, pool)); + } + else { *cache_p = NULL; } - return SVN_NO_ERROR; + SVN_ERR(init_callbacks(*cache_p, fs, error_handler, pool)); + + return SVN_NO_ERROR; } svn_error_t * @@ -241,23 +329,30 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, { fs_fs_data_t *ffd = fs->fsap_data; const char *prefix = apr_pstrcat(pool, - "fsfs:", ffd->uuid, - "/", fs->path, ":", + "fsfs:", fs->uuid, + "/", normalize_key_part(fs->path, pool), + ":", (char *)NULL); svn_memcache_t *memcache; svn_membuffer_t *membuffer; svn_boolean_t no_handler; svn_boolean_t cache_txdeltas; svn_boolean_t cache_fulltexts; + svn_boolean_t cache_revprops; + const char *cache_namespace; /* Evaluating the cache configuration. */ SVN_ERR(read_config(&memcache, &no_handler, + &cache_namespace, &cache_txdeltas, &cache_fulltexts, + &cache_revprops, fs, pool)); + prefix = apr_pstrcat(pool, "ns:", cache_namespace, ":", prefix, NULL); + membuffer = svn_cache__get_global_membuffer_cache(); /* Make the cache for revision roots. For the vast majority of @@ -276,10 +371,10 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, svn_fs_fs__deserialize_id, sizeof(svn_revnum_t), apr_pstrcat(pool, prefix, "RRI", (char *)NULL), + fs, + no_handler, fs->pool)); - SVN_ERR(init_callbacks(ffd->rev_root_id_cache, fs, no_handler, pool)); - /* Rough estimate: revision DAG nodes have size around 320 bytes, so * let's put 16 on a page. */ SVN_ERR(create_cache(&(ffd->rev_node_cache), @@ -289,10 +384,13 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, svn_fs_fs__dag_serialize, svn_fs_fs__dag_deserialize, APR_HASH_KEY_STRING, - apr_pstrcat(pool, prefix, "DAG", NULL), + apr_pstrcat(pool, prefix, "DAG", (char *)NULL), + fs, + no_handler, fs->pool)); - SVN_ERR(init_callbacks(ffd->rev_node_cache, fs, no_handler, pool)); + /* 1st level DAG node cache */ + ffd->dag_node_cache = svn_fs_fs__create_dag_cache(pool); /* Very rough estimate: 1K per directory. */ SVN_ERR(create_cache(&(ffd->dir_cache), @@ -303,10 +401,10 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, svn_fs_fs__deserialize_dir_entries, APR_HASH_KEY_STRING, apr_pstrcat(pool, prefix, "DIR", (char *)NULL), + fs, + no_handler, fs->pool)); - SVN_ERR(init_callbacks(ffd->dir_cache, fs, no_handler, pool)); - /* Only 16 bytes per entry (a revision number + the corresponding offset). Since we want ~8k pages, that means 512 entries per page. */ SVN_ERR(create_cache(&(ffd->packed_offset_cache), @@ -318,28 +416,120 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, sizeof(svn_revnum_t), apr_pstrcat(pool, prefix, "PACK-MANIFEST", (char *)NULL), + fs, + no_handler, fs->pool)); - SVN_ERR(init_callbacks(ffd->packed_offset_cache, fs, no_handler, pool)); + /* initialize node revision cache, if caching has been enabled */ + SVN_ERR(create_cache(&(ffd->node_revision_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_fs__serialize_node_revision, + svn_fs_fs__deserialize_node_revision, + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "NODEREVS", (char *)NULL), + fs, + no_handler, + fs->pool)); + + /* initialize node change list cache, if caching has been enabled */ + SVN_ERR(create_cache(&(ffd->changes_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_fs__serialize_changes, + svn_fs_fs__deserialize_changes, + sizeof(svn_revnum_t), + apr_pstrcat(pool, prefix, "CHANGES", (char *)NULL), + fs, + no_handler, + fs->pool)); - /* initialize fulltext cache as configured */ - ffd->fulltext_cache = NULL; + /* if enabled, cache fulltext and other derived information */ if (cache_fulltexts) { SVN_ERR(create_cache(&(ffd->fulltext_cache), memcache, membuffer, 0, 0, /* Do not use inprocess cache */ - /* Values are svn_string_t */ + /* Values are svn_stringbuf_t */ NULL, NULL, - APR_HASH_KEY_STRING, + sizeof(pair_cache_key_t), apr_pstrcat(pool, prefix, "TEXT", (char *)NULL), + fs, + no_handler, + fs->pool)); + + SVN_ERR(create_cache(&(ffd->properties_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_fs__serialize_properties, + svn_fs_fs__deserialize_properties, + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "PROP", + (char *)NULL), + fs, + no_handler, + fs->pool)); + + SVN_ERR(create_cache(&(ffd->mergeinfo_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_fs__serialize_mergeinfo, + svn_fs_fs__deserialize_mergeinfo, + APR_HASH_KEY_STRING, + apr_pstrcat(pool, prefix, "MERGEINFO", + (char *)NULL), + fs, + no_handler, + fs->pool)); + + SVN_ERR(create_cache(&(ffd->mergeinfo_existence_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + /* Values are svn_stringbuf_t */ + NULL, NULL, + APR_HASH_KEY_STRING, + apr_pstrcat(pool, prefix, "HAS_MERGEINFO", + (char *)NULL), + fs, + no_handler, fs->pool)); } + else + { + ffd->fulltext_cache = NULL; + ffd->properties_cache = NULL; + ffd->mergeinfo_cache = NULL; + ffd->mergeinfo_existence_cache = NULL; + } - SVN_ERR(init_callbacks(ffd->fulltext_cache, fs, no_handler, pool)); + /* initialize revprop cache, if full-text caching has been enabled */ + if (cache_revprops) + { + SVN_ERR(create_cache(&(ffd->revprop_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_fs__serialize_properties, + svn_fs_fs__deserialize_properties, + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "REVPROP", + (char *)NULL), + fs, + no_handler, + fs->pool)); + } + else + { + ffd->revprop_cache = NULL; + } - /* initialize txdelta window cache, if that has been enabled */ + /* if enabled, cache text deltas and their combinations */ if (cache_txdeltas) { SVN_ERR(create_cache(&(ffd->txdelta_window_cache), @@ -351,28 +541,29 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, APR_HASH_KEY_STRING, apr_pstrcat(pool, prefix, "TXDELTA_WINDOW", (char *)NULL), + fs, + no_handler, + fs->pool)); + + SVN_ERR(create_cache(&(ffd->combined_window_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + /* Values are svn_stringbuf_t */ + NULL, NULL, + APR_HASH_KEY_STRING, + apr_pstrcat(pool, prefix, "COMBINED_WINDOW", + (char *)NULL), + fs, + no_handler, fs->pool)); } else { ffd->txdelta_window_cache = NULL; + ffd->combined_window_cache = NULL; } - SVN_ERR(init_callbacks(ffd->txdelta_window_cache, fs, no_handler, pool)); - - /* initialize node revision cache, if caching has been enabled */ - SVN_ERR(create_cache(&(ffd->node_revision_cache), - NULL, - membuffer, - 0, 0, /* Do not use inprocess cache */ - svn_fs_fs__serialize_node_revision, - svn_fs_fs__deserialize_node_revision, - APR_HASH_KEY_STRING, - apr_pstrcat(pool, prefix, "NODEREVS", (char *)NULL), - fs->pool)); - - SVN_ERR(init_callbacks(ffd->node_revision_cache, fs, no_handler, pool)); - return SVN_NO_ERROR; } @@ -440,7 +631,7 @@ svn_fs_fs__initialize_txn_caches(svn_fs_t *fs, to start a new transaction later that receives the same id. Therefore, throw in a uuid as well - just to be sure. */ const char *prefix = apr_pstrcat(pool, - "fsfs:", ffd->uuid, + "fsfs:", fs->uuid, "/", fs->path, ":", txn_id, ":", svn_uuid_generate(pool), ":", @@ -457,24 +648,18 @@ svn_fs_fs__initialize_txn_caches(svn_fs_t *fs, } /* create a txn-local directory cache */ - if (svn_cache__get_global_membuffer_cache()) - SVN_ERR(svn_cache__create_membuffer_cache(&(ffd->txn_dir_cache), - svn_cache__get_global_membuffer_cache(), - svn_fs_fs__serialize_dir_entries, - svn_fs_fs__deserialize_dir_entries, - APR_HASH_KEY_STRING, - apr_pstrcat(pool, prefix, "TXNDIR", - (char *)NULL), - pool)); - else - SVN_ERR(svn_cache__create_inprocess(&(ffd->txn_dir_cache), - svn_fs_fs__serialize_dir_entries, - svn_fs_fs__deserialize_dir_entries, - APR_HASH_KEY_STRING, - 1024, 8, FALSE, - apr_pstrcat(pool, prefix, "TXNDIR", - (char *)NULL), - pool)); + SVN_ERR(create_cache(&ffd->txn_dir_cache, + NULL, + svn_cache__get_global_membuffer_cache(), + 1024, 8, + svn_fs_fs__serialize_dir_entries, + svn_fs_fs__deserialize_dir_entries, + APR_HASH_KEY_STRING, + apr_pstrcat(pool, prefix, "TXNDIR", + (char *)NULL), + fs, + TRUE, + pool)); /* reset the transaction-specific cache if the pool gets cleaned up. */ init_txn_callbacks(&(ffd->txn_dir_cache), pool); |