summaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs_fs/caching.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs_fs/caching.c')
-rw-r--r--subversion/libsvn_fs_fs/caching.c347
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);