Merge from trunk to in-memory-cache, perhaps for the last time.
git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/in-memory-cache@871452 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/Makefile.in b/Makefile.in
index 8acbeab..e7eaec2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -46,6 +46,7 @@
SVN_SERF_LIBS = @SVN_SERF_LIBS@
SVN_SASL_LIBS = @SVN_SASL_LIBS@
SVN_ZLIB_LIBS = @SVN_ZLIB_LIBS@
+SVN_APR_MEMCACHE_LIBS = @SVN_APR_MEMCACHE_LIBS@
LIBS = @LIBS@
@@ -108,7 +109,7 @@
INCLUDES = -I$(top_srcdir)/subversion/include -I$(top_builddir)/subversion \
@SVN_APR_INCLUDES@ @SVN_APRUTIL_INCLUDES@ @SVN_GNOME_KEYRING_INCLUDES@ \
@SVN_KWALLET_INCLUDES@ @SVN_NEON_INCLUDES@ @SVN_SASL_INCLUDES@ \
- @SVN_SERF_INCLUDES@ @SVN_ZLIB_INCLUDES@
+ @SVN_SERF_INCLUDES@ @SVN_ZLIB_INCLUDES@ @SVN_APR_MEMCACHE_INCLUDES@
APACHE_INCLUDES = @APACHE_INCLUDES@
APACHE_LIBEXECDIR = $(DESTDIR)@APACHE_LIBEXECDIR@
@@ -142,6 +143,9 @@
SVN_ZLIB_PREFIX = @SVN_ZLIB_PREFIX@
SVN_ZLIB_INCLUDES = @SVN_ZLIB_INCLUDES@
+SVN_APR_MEMCACHE_PREFIX = @SVN_APR_MEMCACHE_PREFIX@
+SVN_APR_MEMCACHE_INCLUDES = @SVN_APR_MEMCACHE_INCLUDES@
+
MKDIR = @MKDIR@
# The EXTRA_ parameters can be used to pass extra flags at 'make' time.
@@ -196,6 +200,7 @@
INSTALL_KWALLET_LIB = $(INSTALL_LIB)
INSTALL_NEON_LIB = $(INSTALL_LIB)
INSTALL_SERF_LIB = $(INSTALL_LIB)
+INSTALL_APR_MEMCACHE_LIB = $(INSTALL_LIB)
INSTALL_BIN = $(LIBTOOL) --mode=install $(INSTALL)
INSTALL_CONTRIB = $(LIBTOOL) --mode=install $(INSTALL)
INSTALL_TOOLS = $(LIBTOOL) --mode=install $(INSTALL)
@@ -395,7 +400,9 @@
if test "$(PARALLEL)" != ""; then \
flags="--parallel $$flags"; \
fi; \
- $(PYTHON) $(top_srcdir)/build/run_tests.py $$flags \
+ $(PYTHON) $(top_srcdir)/build/run_tests.py \
+ --config-file $(top_srcdir)/subversion/tests/tests.conf \
+ $$flags \
'$(abs_srcdir)' '$(abs_builddir)' $(TESTS); \
else \
echo "make check: Python 2.2 or greater is required,"; \
diff --git a/aclocal.m4 b/aclocal.m4
index d5485de..f902e54 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -20,6 +20,7 @@
sinclude(build/ac-macros/swig.m4)
sinclude(build/ac-macros/sasl.m4)
sinclude(build/ac-macros/zlib.m4)
+sinclude(build/ac-macros/apr_memcache.m4)
# Include the libtool macros
sinclude(build/libtool.m4)
diff --git a/build.conf b/build.conf
index 3c8a2b5..e48d4ab 100644
--- a/build.conf
+++ b/build.conf
@@ -340,9 +340,9 @@
type = lib
install = fsmod-lib
path = subversion/libsvn_subr
-libs = aprutil apriconv apr xml zlib
+libs = aprutil apriconv apr xml zlib apr_memcache
msvc-libs = advapi32.lib shfolder.lib ole32.lib
-msvc-export = svn_auth.h svn_base64.h svn_cmdline.h svn_compat.h svn_config.h svn_ctype.h svn_dso.h svn_error.h svn_hash.h svn_io.h svn_md5.h svn_nls.h svn_opt.h svn_mergeinfo.h svn_path.h svn_pools.h svn_props.h svn_quoprint.h svn_sorts.h svn_string.h svn_subst.h svn_time.h svn_types.h svn_user.h svn_utf.h svn_version.h svn_xml.h private\svn_atomic.h private\svn_log.h private\svn_mergeinfo_private.h svn_iter.h private\svn_opt_private.h
+msvc-export = svn_auth.h svn_base64.h svn_cmdline.h svn_compat.h svn_config.h svn_ctype.h svn_dso.h svn_error.h svn_hash.h svn_io.h svn_md5.h svn_nls.h svn_opt.h svn_mergeinfo.h svn_path.h svn_pools.h svn_props.h svn_quoprint.h svn_sorts.h svn_string.h svn_subst.h svn_time.h svn_types.h svn_user.h svn_utf.h svn_version.h svn_xml.h private\svn_atomic.h private\svn_log.h private\svn_mergeinfo_private.h svn_iter.h private\svn_opt_private.h svn_cache.h
# Working copy management lib
[libsvn_wc]
@@ -639,6 +639,14 @@
# ----------------------------------------------------------------------------
# Tests for libsvn_subr
+[cache-test]
+description = Test in-memory cache
+type = exe
+path = subversion/tests/libsvn_subr
+sources = cache-test.c
+install = test
+libs = libsvn_test libsvn_subr apr
+
[compat-test]
description = Test compatibility functions
type = exe
@@ -870,6 +878,10 @@
msvc-libs = ws2_32.lib
msvc-static = yes
+[apr_memcache]
+type = lib
+external-lib = $(SVN_APR_MEMCACHE_LIBS)
+
[serf]
type = lib
external-lib = $(SVN_SERF_LIBS)
@@ -904,7 +916,7 @@
fs-test fs-base-test skel-test key-test strings-reps-test changes-test locks-test
repos-test
compat-test config-test hashdump-test mergeinfo-test opt-test path-test stream-test
- string-test time-test utf-test target-test error-test
+ string-test time-test utf-test target-test error-test cache-test
revision-test
translate-test
random-test
diff --git a/build/ac-macros/apr_memcache.m4 b/build/ac-macros/apr_memcache.m4
new file mode 100644
index 0000000..c99cf13
--- /dev/null
+++ b/build/ac-macros/apr_memcache.m4
@@ -0,0 +1,84 @@
+dnl
+dnl SVN_LIB_APR_MEMCACHE
+dnl
+dnl Check configure options and assign variables related to
+dnl the apr_memcache client library.
+dnl Sets svn_lib_apr_memcache to "yes" if memcache code is accessible
+dnl either from the standalone apr_memcache library or from apr-util.
+dnl
+
+AC_DEFUN(SVN_LIB_APR_MEMCACHE,
+[
+ apr_memcache_found=no
+
+ AC_ARG_WITH(apr_memcache,AC_HELP_STRING([--with-apr_memcache=PREFIX],
+ [Standalone apr_memcache client library]),
+ [
+ if test "$withval" = "yes" ; then
+ AC_MSG_ERROR([--with-apr_memcache requires an argument.])
+ else
+ AC_MSG_NOTICE([looking for separate apr_memcache package])
+ apr_memcache_prefix=$withval
+ save_cppflags="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES $SVN_APRUTIL_INCLUDES -I$apr_memcache_prefix/include/apr_memcache-0"
+ AC_CHECK_HEADER(apr_memcache.h,[
+ save_ldflags="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -L$apr_memcache_prefix/lib"
+ AC_CHECK_LIB(apr_memcache, apr_memcache_create,
+ [apr_memcache_found="standalone"])
+ LDFLAGS="$save_ldflags"])
+ CPPFLAGS="$save_cppflags"
+ fi
+ ], [
+ if test -d "$srcdir/apr_memcache"; then
+ apr_memcache_found=reconfig
+ else
+dnl Try just looking in apr-util (>= 1.3 has it already).
+ AC_MSG_NOTICE([looking for apr_memcache as part of apr-util])
+ save_cppflags="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $SVN_APR_INCLUDES $SVN_APRUTIL_INCLUDES"
+ AC_CHECK_HEADER(apr_memcache.h,[
+ save_ldflags="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $SVN_APRUTIL_EXPORT_LIBS"
+ AC_CHECK_LIB(aprutil-1, apr_memcache_create,
+ [apr_memcache_found="aprutil"])
+ LDFLAGS="$save_ldflags"])
+ CPPFLAGS="$save_cppflags"
+
+ fi
+ ])
+
+
+ if test $apr_memcache_found = "reconfig"; then
+ SVN_EXTERNAL_PROJECT([apr_memcache], [--with-apr=$apr_config --with-apr-util=$apu_config])
+ apr_memcache_prefix=$prefix
+ SVN_APR_MEMCACHE_PREFIX="$apr_memcache_prefix"
+ SVN_APR_MEMCACHE_INCLUDES="-I$srcdir/memcache"
+ SVN_APR_MEMCACHE_LIBS="$abs_builddir/memcache/libapr_memcache.la"
+ SVN_APR_MEMCACHE_EXPORT_LIBS="-L$apr_memcache_prefix/lib -lapr_memcache"
+ fi
+
+ if test $apr_memcache_found = "standalone"; then
+ SVN_APR_MEMCACHE_PREFIX="$apr_memcache_prefix"
+ SVN_APR_MEMCACHE_INCLUDES="-I$apr_memcache_prefix/include/apr_memcache-0"
+ SVN_APR_MEMCACHE_LIBS="$apr_memcache_prefix/lib/libapr_memcache.la"
+ SVN_APR_MEMCACHE_EXPORT_LIBS="-L$apr_memcache_prefix/lib -lapr_memcache"
+ svn_lib_apr_memcache=yes
+ elif test $apr_memcache_found = "aprutil"; then
+dnl We are already linking apr-util everywhere, so no special treatement needed.
+ SVN_APR_MEMCACHE_PREFIX=""
+ SVN_APR_MEMCACHE_INCLUDES=""
+ SVN_APR_MEMCACHE_LIBS=""
+ SVN_APR_MEMCACHE_EXPORT_LIBS=""
+ svn_lib_apr_memcache=yes
+ elif test $apr_memcache_found = "reconfig"; then
+ svn_lib_apr_memcache=yes
+ else
+ svn_lib_apr_memcache=no
+ fi
+
+ AC_SUBST(SVN_APR_MEMCACHE_PREFIX)
+ AC_SUBST(SVN_APR_MEMCACHE_INCLUDES)
+ AC_SUBST(SVN_APR_MEMCACHE_LIBS)
+ AC_SUBST(SVN_APR_MEMCACHE_EXPORT_LIBS)
+])
diff --git a/build/run_tests.py b/build/run_tests.py
index 9cee4c9..4bcbe8a 100755
--- a/build/run_tests.py
+++ b/build/run_tests.py
@@ -6,13 +6,13 @@
'''usage: python run_tests.py [--url=<base-url>] [--fs-type=<fs-type>]
[--verbose] [--cleanup] [--enable-sasl] [--parallel]
[--http-library=<http-library>]
+ [--config-file=<file>]
[--server-minor-version=<version>] <abs_srcdir> <abs_builddir>
<prog ...>
-The optional base-url, fs-type, http-library, server-minor-version,
-verbose, parallel, enable-sasl, and cleanup options, and the first two
-parameters are passed unchanged to the TestHarness constructor. All
-other parameters are names of test programs.
+The optional flags and the first two parameters are passed unchanged
+to the TestHarness constructor. All other parameters are names of
+test programs.
'''
import os, sys
@@ -30,8 +30,8 @@
def __init__(self, abs_srcdir, abs_builddir, logfile,
base_url=None, fs_type=None, http_library=None,
server_minor_version=None, verbose=None,
- cleanup=None, enable_sasl=None, parallel=None, list_tests=None,
- svn_bin=None):
+ cleanup=None, enable_sasl=None, parallel=None, config_file=None,
+ list_tests=None, svn_bin=None):
'''Construct a TestHarness instance.
ABS_SRCDIR and ABS_BUILDDIR are the source and build directories.
@@ -53,6 +53,9 @@
self.cleanup = cleanup
self.enable_sasl = enable_sasl
self.parallel = parallel
+ self.config_file = None
+ if config_file is not None:
+ self.config_file = os.path.abspath(config_file)
self.list_tests = list_tests
self.svn_bin = svn_bin
self.log = None
@@ -114,10 +117,14 @@
cmdline.append('--enable-sasl')
if self.parallel is not None:
cmdline.append('--parallel')
+ if self.config_file is not None:
+ cmdline.append(quote('--config-file=' + self.config_file))
elif os.access(prog, os.X_OK):
progname = './' + progbase
cmdline = [quote(progname),
quote('--srcdir=' + os.path.join(self.srcdir, progdir))]
+ if self.config_file is not None:
+ cmdline.append(quote('--config-file=' + self.config_file))
else:
print 'Don\'t know what to do about ' + progbase
sys.exit(1)
@@ -190,7 +197,7 @@
opts, args = my_getopt(sys.argv[1:], 'u:f:vc',
['url=', 'fs-type=', 'verbose', 'cleanup',
'http-library=', 'server-minor-version=',
- 'enable-sasl', 'parallel'])
+ 'enable-sasl', 'parallel', 'config-file='])
except getopt.GetoptError:
args = []
@@ -199,8 +206,8 @@
sys.exit(2)
base_url, fs_type, verbose, cleanup, enable_sasl, http_library, \
- server_minor_version, parallel = \
- None, None, None, None, None, None, None, None
+ server_minor_version, parallel, config_file = \
+ None, None, None, None, None, None, None, None, None
for opt, val in opts:
if opt in ['-u', '--url']:
base_url = val
@@ -218,13 +225,15 @@
enable_sasl = 1
elif opt in ['--parallel']:
parallel = 1
+ elif opt in ['--config-file']:
+ config_file = val
else:
raise getopt.GetoptError
th = TestHarness(args[0], args[1],
os.path.abspath('tests.log'),
base_url, fs_type, http_library, server_minor_version,
- verbose, cleanup, enable_sasl, parallel)
+ verbose, cleanup, enable_sasl, parallel, config_file)
failed = th.run(args[2:])
if failed:
diff --git a/configure.ac b/configure.ac
index 30b817f..bb1cc02 100644
--- a/configure.ac
+++ b/configure.ac
@@ -89,6 +89,14 @@
dnl Search for serf as an alternative to neon
SVN_LIB_SERF
+dnl Search for apr_memcache (only affects fs_fs)
+SVN_LIB_APR_MEMCACHE
+
+if test "$svn_lib_apr_memcache" = "yes"; then
+ AC_DEFINE(SVN_HAVE_MEMCACHE, 1,
+ [Defined if apr_memcache (standalone or in apr-util) is present])
+fi
+
dnl Set up a number of directories ---------------------
dnl Create SVN_BINDIR for proper substitution
diff --git a/gen-make.py b/gen-make.py
index 6fe722c..66a5d8b 100755
--- a/gen-make.py
+++ b/gen-make.py
@@ -178,6 +178,9 @@
print " --vsnet-version=VER"
print " generate for VS.NET version VER (2002, 2003, 2005 or 2008)"
print " [only valid in combination with '-t vcproj']"
+ print
+ print " --with-apr_memcache=DIR"
+ print " the apr_memcache sources are in DIR"
sys.exit(0)
@@ -214,6 +217,7 @@
'with-junit=',
'with-swig=',
'with-sasl=',
+ 'with-apr_memcache=',
'enable-pool-debug',
'enable-purify',
'enable-quantify',
diff --git a/subversion/bindings/swig/python/tests/mergeinfo.py b/subversion/bindings/swig/python/tests/mergeinfo.py
index d59d4ed..ecd9a10 100644
--- a/subversion/bindings/swig/python/tests/mergeinfo.py
+++ b/subversion/bindings/swig/python/tests/mergeinfo.py
@@ -39,7 +39,7 @@
self.tearDown()
self.repos = repos.svn_repos_create(REPOS_PATH, '', '', None, None)
repos.svn_repos_load_fs2(self.repos, dumpfile, StringIO(),
- repos.svn_repos_load_uuid_default, '',
+ repos.svn_repos_load_uuid_ignore, '',
0, 0, None)
self.fs = repos.fs(self.repos)
self.rev = fs.youngest_rev(self.fs)
diff --git a/subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py b/subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py
index 30b4e88..877876b 100644
--- a/subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py
+++ b/subversion/bindings/swig/python/tests/trac/versioncontrol/tests/svn_fs.py
@@ -54,7 +54,7 @@
r = repos.svn_repos_create(REPOS_PATH, '', '', None, None)
repos.svn_repos_load_fs2(r, dumpfile, StringIO(),
- repos.svn_repos_load_uuid_default, '',
+ repos.svn_repos_load_uuid_ignore, '',
0, 0, None)
def tearDown(self):
diff --git a/subversion/include/svn_base64.h b/subversion/include/svn_base64.h
index 679addc..ceb5e24 100644
--- a/subversion/include/svn_base64.h
+++ b/subversion/include/svn_base64.h
@@ -55,7 +55,22 @@
/** Encode an @c svn_stringbuf_t into base64.
*
* A simple interface for encoding base64 data assuming we have all of
- * it present at once. The returned string will be allocated from @c pool.
+ * it present at once. If @a break_lines is true, newlines will be
+ * inserted periodically; otherwise the string will only consist of
+ * base64 encoding characters. The returned string will be allocated
+ * from @c pool.
+ *
+ * @since New in 1.6.
+ */
+const svn_string_t *svn_base64_encode_string2(const svn_string_t *str,
+ svn_boolean_t break_lines,
+ apr_pool_t *pool);
+
+/**
+ * Same as svn_base64_encode_string2, but with @a break_lines always
+ * TRUE.
+ *
+ * @deprecated Provided for backward compatibility with the 1.5 API.
*/
const svn_string_t *svn_base64_encode_string(const svn_string_t *str,
apr_pool_t *pool);
@@ -63,7 +78,9 @@
/** Decode an @c svn_stringbuf_t from base64.
*
* A simple interface for decoding base64 data assuming we have all of
- * it present at once. The returned string will be allocated from @c pool.
+ * it present at once. The returned string will be allocated from @c
+ * pool.
+ *
*/
const svn_string_t *svn_base64_decode_string(const svn_string_t *str,
apr_pool_t *pool);
diff --git a/subversion/include/svn_cache.h b/subversion/include/svn_cache.h
new file mode 100644
index 0000000..df4b77c
--- /dev/null
+++ b/subversion/include/svn_cache.h
@@ -0,0 +1,277 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Copyright (c) 2008 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file svn_cache.h
+ * @brief In-memory cache implementation.
+ */
+
+
+#ifndef SVN_CACHE_H
+#define SVN_CACHE_H
+
+#include <apr_pools.h>
+#include <apr_hash.h>
+
+#include "svn_types.h"
+#include "svn_error.h"
+#include "svn_iter.h"
+#include "svn_config.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+
+/**
+ * @defgroup svn_cache_support In-memory caching
+ * @{
+ */
+
+/**
+ * A function type for copying an object @a in into a different pool @pool
+ * and returning the result in @a *out.
+ *
+ * @since New in 1.6.
+*/
+typedef svn_error_t *(svn_cache_dup_func_t)(void **out,
+ void *in,
+ apr_pool_t *pool);
+
+/**
+ * A function type for deserializing an object @a *out from the string
+ * @a data of length @a data_len in the pool @pool.
+ *
+ * @since New in 1.6.
+*/
+typedef svn_error_t *(svn_cache_deserialize_func_t)(void **out,
+ const char *data,
+ apr_size_t data_len,
+ apr_pool_t *pool);
+
+/**
+ * A function type for serializing an object @a in into bytes. The
+ * function should allocate the serialized value in @a pool, set @a
+ * *data to the serialized value, and set *data_len to its length.
+ *
+ * @since New in 1.6.
+*/
+typedef svn_error_t *(svn_cache_serialize_func_t)(char **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool);
+
+/**
+ * A function type for transforming or ignoring errors. @a pool may
+ * be used for temporary allocations.
+ *
+ * @since New in 1.6.
+ */
+typedef svn_error_t *(svn_cache_error_handler_t)(svn_error_t *err,
+ void *baton,
+ apr_pool_t *pool);
+
+/**
+ * A wrapper around apr_memcache_t, provided essentially so that the
+ * Subversion public API doesn't depend on whether or not you have
+ * access to the APR memcache libraries.
+ *
+ * @since New in 1.6.
+ */
+typedef struct svn_memcache_t svn_memcache_t;
+
+/**
+ * Opaque type for an in-memory cache.
+ *
+ * @since New in 1.6.
+ */
+typedef struct svn_cache_t svn_cache_t;
+
+/**
+ * Creates a new cache in @a *cache_p. This cache will use @a pool
+ * for all of its storage needs. The elements in the cache will be
+ * indexed by keys of length @a klen, which may be APR_HASH_KEY_STRING
+ * if they are strings. Cached values will be copied in and out of
+ * the cache using @a dup_func.
+ *
+ * The cache stores up to @a pages * @a items_per_page items at a
+ * time. The exact cache invalidation strategy is not defined here,
+ * but in general, a lower value for @a items_per_page means more
+ * memory overhead for the same number of items, but a higher value
+ * for @a items_per_page means more items are cleared at once. Both
+ * @a pages and @a items_per_page must be positive (though they both
+ * may certainly be 1).
+ *
+ * If @a thread_safe is true, and APR is compiled with threads, all
+ * accesses to the cache will be protected with a mutex.
+ *
+ * Note that NULL is a legitimate value for cache entries (and @a dup_func
+ * will not be called on it).
+ *
+ * It is not safe for @a dup_func to interact with the cache itself.
+ *
+ * @since New in 1.6.
+ */
+svn_error_t *
+svn_cache_create_inprocess(svn_cache_t **cache_p,
+ svn_cache_dup_func_t *dup_func,
+ apr_ssize_t klen,
+ apr_int64_t pages,
+ apr_int64_t items_per_page,
+ svn_boolean_t thread_safe,
+ apr_pool_t *pool);
+/**
+ * Creates a new cache in @a *cache_p, communicating to a memcached
+ * process via @a memcache. The elements in the cache will be indexed
+ * by keys of length @a klen, which may be APR_HASH_KEY_STRING if they
+ * are strings. Values will be serialized for memcached using @a
+ * serialize_func and deserialized using @a deserialize_func. Because
+ * the same memcached server may cache many different kinds of values,
+ * @a prefix should be specified to differentiate this cache from
+ * other caches. @a *cache_p will be allocated in @a pool.
+ *
+ * If @a deserialize_func is NULL, then the data is returned as an
+ * svn_string_t; if @a serialize_func is NULL, then the data is
+ * assumed to be an svn_stringbuf_t.
+ *
+ * These caches are always thread safe.
+ *
+ * These caches do not support svn_cache_iter.
+ *
+ * If Subversion was not built with apr_memcache support, always
+ * raises SVN_ERR_NO_APR_MEMCACHE.
+ *
+ * @since New in 1.6.
+ */
+svn_error_t *
+svn_cache_create_memcache(svn_cache_t **cache_p,
+ svn_memcache_t *memcache,
+ svn_cache_serialize_func_t *serialize_func,
+ svn_cache_deserialize_func_t *deserialize_func,
+ apr_ssize_t klen,
+ const char *prefix,
+ apr_pool_t *pool);
+
+/**
+ * Given @a config, returns an APR memcached interface in @a
+ * *memcache_p allocated in @a pool if @a config contains entries in
+ * the SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS section describing
+ * memcached servers; otherwise, sets @a *memcache_p to NULL.
+ *
+ * If Subversion was not built with apr_memcache_support, then raises
+ * SVN_ERR_NO_APR_MEMCACHE if and only if @a config is configured to
+ * use memcache.
+ *
+ * @since New in 1.6.
+ */
+svn_error_t *
+svn_cache_make_memcache_from_config(svn_memcache_t **memcache_p,
+ svn_config_t *config,
+ apr_pool_t *pool);
+
+/**
+ * Sets @a handler to be @a cache's error handling routine. If any
+ * error is returned from a call to svn_cache_get or svn_cache_set, @a
+ * handler will be called with @a baton and the error, and the
+ * original function will return whatever error @a handler returns
+ * instead (possibly SVN_NO_ERROR); @a handler will receive the pool
+ * passed to the svn_cache_* function. @a pool is used for temporary
+ * allocations.
+ *
+ * @since New in 1.6.
+ */
+svn_error_t *
+svn_cache_set_error_handler(svn_cache_t *cache,
+ svn_cache_error_handler_t *handler,
+ void *baton,
+ apr_pool_t *pool);
+
+
+#define SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "memcached-servers"
+
+/**
+ * Fetches a value indexed by @a key from @a cache into @a *value,
+ * setting @a *found to TRUE iff it is in the cache and FALSE if it is
+ * not found. The value is copied into @a pool using the copy
+ * function provided to the cache's constructor.
+ *
+ * @since New in 1.6.
+ */
+svn_error_t *
+svn_cache_get(void **value,
+ svn_boolean_t *found,
+ svn_cache_t *cache,
+ const void *key,
+ apr_pool_t *pool);
+
+/**
+ * Stores the value @value under the key @a key in @a cache. @a pool
+ * is used only for temporary allocations. The cache makes copies of
+ * @a key and @a value if necessary (that is, @a key and @a value may
+ * have shorter lifetimes than the cache).
+ *
+ * If there is already a value for @a key, this will replace it. Bear
+ * in mind that in some circumstances this may leak memory (that is,
+ * the cache's copy of the previous value may not be immediately
+ * cleared); it is only guaranteed to not leak for caches created with
+ * @a items_per_page equal to 1.
+ *
+ * @since New in 1.6.
+ */
+svn_error_t *
+svn_cache_set(svn_cache_t *cache,
+ const void *key,
+ void *value,
+ apr_pool_t *pool);
+
+/**
+ * Iterates over the elements currently in @a cache, calling @a func
+ * for each one until there are no more elements or @a func returns an
+ * error. Uses @a pool for temporary allocations.
+ *
+ * If @a completed is not NULL, then on return - if @a func returns no
+ * errors - @a *completed will be set to @c TRUE.
+ *
+ * If @a func returns an error other than @c SVN_ERR_ITER_BREAK, that
+ * error is returned. When @a func returns @c SVN_ERR_ITER_BREAK,
+ * iteration is interrupted, but no error is returned and @a
+ * *completed is set to @c FALSE. (The error handler set by
+ * svn_cache_set_error_handler is not used for svn_cache_iter.)
+ *
+ * It is not legal to perform any other cache operations on @a cache
+ * inside @a func.
+ *
+ * svn_cache_iter is not supported by all cache implementations; see
+ * the svn_cache_create_* function for details.
+ *
+ * @since New in 1.6.
+ */
+svn_error_t *
+svn_cache_iter(svn_boolean_t *completed,
+ svn_cache_t *cache,
+ svn_iter_apr_hash_cb_t func,
+ void *baton,
+ apr_pool_t *pool);
+/** @} */
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_CACHE_H */
diff --git a/subversion/include/svn_error_codes.h b/subversion/include/svn_error_codes.h
index 42f830e..b386ac6 100644
--- a/subversion/include/svn_error_codes.h
+++ b/subversion/include/svn_error_codes.h
@@ -171,6 +171,10 @@
SVN_ERR_BAD_CATEGORY_START + 4,
"Bogus mime-type")
+ SVN_ERRDEF(SVN_ERR_BAD_SERVER_SPECIFICATION,
+ SVN_ERR_BAD_CATEGORY_START + 5,
+ "Bogus server specification")
+
/** @since New in 1.5.
*
* Note that there was an unused slot sitting here at
@@ -1153,6 +1157,16 @@
SVN_ERR_MISC_CATEGORY_START + 26,
"Inquiry about unknown capability")
+ /** @since New in 1.6. */
+ SVN_ERRDEF(SVN_ERR_TEST_SKIPPED,
+ SVN_ERR_MISC_CATEGORY_START + 27,
+ "Test skipped")
+
+ /** @since New in 1.6. */
+ SVN_ERRDEF(SVN_ERR_NO_APR_MEMCACHE,
+ SVN_ERR_MISC_CATEGORY_START + 28,
+ "apr memcache library not available")
+
/* command-line client errors */
SVN_ERRDEF(SVN_ERR_CL_ARG_PARSING_ERROR,
diff --git a/subversion/include/svn_io.h b/subversion/include/svn_io.h
index 57f28d7..d7f5b91 100644
--- a/subversion/include/svn_io.h
+++ b/subversion/include/svn_io.h
@@ -628,6 +628,12 @@
svn_stream_t *svn_stream_from_stringbuf(svn_stringbuf_t *str,
apr_pool_t *pool);
+/** Return a generic read-only stream connected to string @a str.
+ * Allocate the stream in @a pool.
+ */
+svn_stream_t *svn_stream_from_string(svn_string_t *str,
+ apr_pool_t *pool);
+
/** Return a stream that decompresses all data read and compresses all
* data written. The stream @a stream is used to read and write all
* compressed data. All compression data structures are allocated on
diff --git a/subversion/include/svn_path.h b/subversion/include/svn_path.h
index d456c2f..09b5cea 100644
--- a/subversion/include/svn_path.h
+++ b/subversion/include/svn_path.h
@@ -32,6 +32,7 @@
* - @c svn_path_canonicalize()
* - @c svn_path_is_canonical()
* - @c svn_path_internal_style()
+ * - @c svn_path_uri_encode()
*
* For the most part, we mean what most anyone would mean when talking
* about canonical paths, but to be on the safe side, you must run
@@ -459,7 +460,9 @@
/** Return @c TRUE iff @a path is URI-safe, @c FALSE otherwise. */
svn_boolean_t svn_path_is_uri_safe(const char *path);
-/** Return a URI-encoded copy of @a path, allocated in @a pool. */
+/** Return a URI-encoded copy of @a path, allocated in @a pool. (@a
+ path can be an arbitrary UTF-8 string and does not have to be a
+ canonical path.) */
const char *svn_path_uri_encode(const char *path, apr_pool_t *pool);
/** Return a URI-decoded copy of @a path, allocated in @a pool. */
diff --git a/subversion/libsvn_fs_fs/caching.c b/subversion/libsvn_fs_fs/caching.c
new file mode 100644
index 0000000..ca6fcd6
--- /dev/null
+++ b/subversion/libsvn_fs_fs/caching.c
@@ -0,0 +1,241 @@
+/* caching.c : in-memory caching
+ *
+ * ====================================================================
+ * Copyright (c) 2008 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#include "fs.h"
+#include "fs_fs.h"
+#include "id.h"
+#include "dag.h"
+#include "../libsvn_fs/fs-loader.h"
+
+#include "svn_config.h"
+
+#include "svn_private_config.h"
+
+/*** Dup/serialize/deserialize functions. ***/
+
+
+/** Caching SVN_FS_ID_T values. **/
+static svn_cache_dup_func_t dup_id;
+static svn_error_t *
+dup_id(void **out,
+ void *in,
+ apr_pool_t *pool)
+{
+ svn_fs_id_t *id = in;
+ *out = svn_fs_fs__id_copy(id, pool);
+ return SVN_NO_ERROR;
+}
+
+static svn_cache_serialize_func_t serialize_id;
+static svn_error_t *
+serialize_id(char **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool)
+{
+ svn_fs_id_t *id = in;
+ svn_string_t *id_str = svn_fs_fs__id_unparse(id, pool);
+ *data = (char *) id_str->data;
+ *data_len = id_str->len;
+
+ return SVN_NO_ERROR;
+}
+
+
+static svn_cache_deserialize_func_t deserialize_id;
+static svn_error_t *
+deserialize_id(void **out,
+ const char *data,
+ apr_size_t data_len,
+ apr_pool_t *pool)
+{
+ svn_fs_id_t *id = svn_fs_fs__id_parse(data, data_len, pool);
+ if (id == NULL)
+ {
+ return svn_error_create(SVN_ERR_FS_NOT_ID, NULL,
+ _("Bad ID in cache"));
+ }
+
+ *out = id;
+ return SVN_NO_ERROR;
+}
+
+
+/** Caching directory listings. **/
+static svn_cache_dup_func_t dup_dir_listing;
+static svn_error_t *
+dup_dir_listing(void **out,
+ void *in,
+ apr_pool_t *pool)
+{
+ apr_hash_t *new_entries = apr_hash_make(pool), *entries = in;
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
+ {
+ void *val;
+ svn_fs_dirent_t *dirent, *new_dirent;
+
+ apr_hash_this(hi, NULL, NULL, &val);
+ dirent = val;
+ new_dirent = apr_palloc(pool, sizeof(*new_dirent));
+ new_dirent->name = apr_pstrdup(pool, dirent->name);
+ new_dirent->kind = dirent->kind;
+ new_dirent->id = svn_fs_fs__id_copy(dirent->id, pool);
+ apr_hash_set(new_entries, new_dirent->name, APR_HASH_KEY_STRING,
+ new_dirent);
+ }
+
+ *out = new_entries;
+ return SVN_NO_ERROR;
+}
+
+/* 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. */
+svn_error_t *
+read_config(svn_memcache_t **memcache_p,
+ svn_boolean_t *fail_stop,
+ svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ svn_config_t *config;
+
+ SVN_ERR(svn_fs_fs__get_config(&config, fs, pool));
+ SVN_ERR(svn_cache_make_memcache_from_config(memcache_p, config,
+ fs->pool));
+ SVN_ERR(svn_config_get_bool(config, fail_stop,
+ CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP,
+ FALSE));
+
+ return SVN_NO_ERROR;
+
+}
+
+
+static svn_cache_error_handler_t warn_on_cache_errors;
+static svn_error_t *
+warn_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;
+}
+
+
+svn_error_t *
+svn_fs_fs__initialize_caches(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ const char *prefix = apr_pstrcat(pool,
+ "fsfs:", ffd->uuid,
+ "/", fs->path, ":",
+ NULL);
+ svn_memcache_t *memcache;
+ svn_boolean_t no_handler;
+
+ SVN_ERR(read_config(&memcache, &no_handler, fs, pool));
+
+ /* Make the cache for revision roots. For the vast majority of
+ * commands, this is only going to contain a few entries (svnadmin
+ * dump/verify is an exception here), so to reduce overhead let's
+ * try to keep it to just one page. I estimate each entry has about
+ * 72 bytes of overhead (svn_revnum_t key, svn_fs_id_t +
+ * id_private_t + 3 strings for value, and the cache_entry); the
+ * default pool size is 8192, so about a hundred should fit
+ * comfortably. */
+ if (memcache)
+ SVN_ERR(svn_cache_create_memcache(&(ffd->rev_root_id_cache),
+ memcache,
+ serialize_id,
+ deserialize_id,
+ sizeof(svn_revnum_t),
+ apr_pstrcat(pool, prefix, "RRI",
+ NULL),
+ fs->pool));
+ else
+ SVN_ERR(svn_cache_create_inprocess(&(ffd->rev_root_id_cache),
+ dup_id, sizeof(svn_revnum_t),
+ 1, 100, FALSE, fs->pool));
+ if (! no_handler)
+ SVN_ERR(svn_cache_set_error_handler(ffd->rev_root_id_cache,
+ warn_on_cache_errors, fs, pool));
+
+
+ /* Rough estimate: revision DAG nodes have size around 320 bytes, so
+ * let's put 16 on a page. */
+ if (memcache)
+ SVN_ERR(svn_cache_create_memcache(&(ffd->rev_node_cache),
+ memcache,
+ svn_fs_fs__dag_serialize,
+ svn_fs_fs__dag_deserialize,
+ APR_HASH_KEY_STRING,
+ apr_pstrcat(pool, prefix, "DAG",
+ NULL),
+ fs->pool));
+ else
+ SVN_ERR(svn_cache_create_inprocess(&(ffd->rev_node_cache),
+ svn_fs_fs__dag_dup_for_cache,
+ APR_HASH_KEY_STRING,
+ 1024, 16, FALSE, fs->pool));
+ if (! no_handler)
+ SVN_ERR(svn_cache_set_error_handler(ffd->rev_node_cache,
+ warn_on_cache_errors, fs, pool));
+
+
+ /* Very rough estimate: 1K per directory. */
+ if (memcache)
+ SVN_ERR(svn_cache_create_memcache(&(ffd->dir_cache),
+ memcache,
+ svn_fs_fs__dir_entries_serialize,
+ svn_fs_fs__dir_entries_deserialize,
+ APR_HASH_KEY_STRING,
+ apr_pstrcat(pool, prefix, "DIR",
+ NULL),
+ fs->pool));
+ else
+ SVN_ERR(svn_cache_create_inprocess(&(ffd->dir_cache),
+ dup_dir_listing, APR_HASH_KEY_STRING,
+ 1024, 8, FALSE, fs->pool));
+
+ if (! no_handler)
+ SVN_ERR(svn_cache_set_error_handler(ffd->dir_cache,
+ warn_on_cache_errors, fs, pool));
+
+ if (memcache)
+ SVN_ERR(svn_cache_create_memcache(&(ffd->fulltext_cache),
+ memcache,
+ NULL, NULL, /* Values are svn_string_t */
+ APR_HASH_KEY_STRING,
+ apr_pstrcat(pool, prefix, "TEXT",
+ NULL),
+ fs->pool));
+ else
+ ffd->fulltext_cache = NULL;
+
+ if (! no_handler)
+ SVN_ERR(svn_cache_set_error_handler(ffd->fulltext_cache,
+ warn_on_cache_errors, fs, pool));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_fs_fs/dag.c b/subversion/libsvn_fs_fs/dag.c
index 5e17e46..fa60a90 100644
--- a/subversion/libsvn_fs_fs/dag.c
+++ b/subversion/libsvn_fs_fs/dag.c
@@ -98,6 +98,12 @@
return node->fs;
}
+void
+svn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs)
+{
+ node->fs = fs;
+}
+
/* Dup NODEREV and all associated data into POOL.
Leaves the id and is_fresh_txn_root fields as zero bytes. */
@@ -1073,6 +1079,137 @@
return new_node;
}
+svn_error_t *
+svn_fs_fs__dag_dup_for_cache(void **out,
+ void *in,
+ apr_pool_t *pool)
+{
+ dag_node_t *in_node = in, *out_node;
+ out_node = svn_fs_fs__dag_dup(in_node, pool);
+ out_node->fs = NULL;
+ *out = out_node;
+ return SVN_NO_ERROR;
+}
+
+/* The cache serialization format is:
+ *
+ * - For mutable nodes: the character 'M', then 'F' for files or 'D'
+ * for directories, then the ID, then '\n', then the created path.
+ *
+ * - For immutable nodes: the character 'I' followed by the noderev
+ * hash dump (the other fields can be reconstructed from this). (We
+ * assume that, once constructed, immutable nodes always contain
+ * their noderev.)
+ */
+
+svn_error_t *
+svn_fs_fs__dag_serialize(char **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool)
+{
+ dag_node_t *node = in;
+ svn_stringbuf_t *buf = svn_stringbuf_create("", pool);
+
+ if (svn_fs_fs__dag_check_mutable(node))
+ {
+ svn_stringbuf_appendcstr(buf, "M");
+ svn_stringbuf_appendcstr(buf, (node->kind == svn_node_file ? "F" : "D"));
+ svn_stringbuf_appendcstr(buf, svn_fs_fs__id_unparse(node->id,
+ pool)->data);
+ svn_stringbuf_appendcstr(buf, "\n");
+ svn_stringbuf_appendcstr(buf, node->created_path);
+ }
+ else
+ {
+ svn_stringbuf_appendcstr(buf, "I");
+ SVN_ERR(svn_fs_fs__write_noderev(svn_stream_from_stringbuf(buf, pool),
+ node->node_revision, TRUE, pool));
+ }
+
+ *data = buf->data;
+ *data_len = buf->len;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__dag_deserialize(void **out,
+ const char *data,
+ apr_size_t data_len,
+ apr_pool_t *pool)
+{
+ dag_node_t *node = apr_pcalloc(pool, sizeof(*node));
+
+ if (data_len == 0)
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Empty noderev in cache"));
+
+ if (*data == 'M')
+ {
+ const char *newline;
+ int id_len;
+
+ data++; data_len--;
+ if (data_len == 0)
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Kindless noderev in cache"));
+ if (*data == 'F')
+ node->kind = svn_node_file;
+ else if (*data == 'D')
+ node->kind = svn_node_dir;
+ else
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Unknown kind for noderev in cache: '%c'"),
+ *data);
+
+ data++; data_len--;
+ newline = memchr(data, '\n', data_len);
+ if (!newline)
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Unterminated ID in cache"));
+ id_len = newline - 1 - data;
+ node->id = svn_fs_fs__id_parse(data, id_len, pool);
+ if (! node->id)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Bogus ID '%s' in cache"),
+ apr_pstrndup(pool, data, id_len));
+
+ data += id_len; data_len -= id_len;
+ data++; data_len--;
+ if (data_len == 0)
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("No created path"));
+ node->created_path = apr_pstrndup(pool, data, data_len);
+ }
+ else if (*data == 'I')
+ {
+ node_revision_t *noderev;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ svn_stream_t *stream =
+ svn_stream_from_stringbuf(svn_stringbuf_ncreate(data + 1,
+ data_len - 1,
+ subpool),
+ subpool);
+ SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, pool));
+ node->kind = noderev->kind;
+ node->id = svn_fs_fs__id_copy(noderev->id, pool);
+ node->created_path = apr_pstrdup(pool, noderev->created_path);
+
+ if (noderev->is_fresh_txn_root)
+ node->fresh_root_predecessor_id = noderev->predecessor_id;
+
+ node->node_revision = noderev;
+
+ svn_pool_destroy(subpool);
+ }
+ else
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Unknown node type in cache: '%c'"), *data);
+
+ *out = node;
+
+ return SVN_NO_ERROR;
+}
svn_error_t *
svn_fs_fs__dag_open(dag_node_t **child_p,
diff --git a/subversion/libsvn_fs_fs/dag.h b/subversion/libsvn_fs_fs/dag.h
index 7a2c4a4..79741c3 100644
--- a/subversion/libsvn_fs_fs/dag.h
+++ b/subversion/libsvn_fs_fs/dag.h
@@ -20,8 +20,7 @@
#include "svn_fs.h"
#include "svn_delta.h"
-
-#include "fs.h"
+#include "svn_cache.h"
#ifdef __cplusplus
extern "C" {
@@ -55,6 +54,7 @@
/* Generic DAG node stuff. */
+typedef struct dag_node_t dag_node_t;
/* Fill *NODE with a dag_node_t representing node revision ID in FS,
allocating in POOL. */
@@ -72,10 +72,23 @@
dag_node_t *svn_fs_fs__dag_dup(dag_node_t *node,
apr_pool_t *pool);
+/* Like svn_fs_fs__dag_dup, but implementing the svn_cache_dup_func_t
+ prototype, and NULLing the FS field. */
+svn_cache_dup_func_t svn_fs_fs__dag_dup_for_cache;
+
+/* Serialize a DAG node. */
+svn_cache_serialize_func_t svn_fs_fs__dag_serialize;
+
+/* Deserialize a DAG node. */
+svn_cache_deserialize_func_t svn_fs_fs__dag_deserialize;
/* Return the filesystem containing NODE. */
svn_fs_t *svn_fs_fs__dag_get_fs(dag_node_t *node);
+/* Changes the filesystem containing NODE to FS. (Used when pulling
+ nodes out of a shared cache, say.) */
+void svn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs);
+
/* Set *REV to NODE's revision number, allocating in POOL. If NODE
has never been committed as part of a revision, set *REV to
diff --git a/subversion/libsvn_fs_fs/fs.c b/subversion/libsvn_fs_fs/fs.c
index d3cf428..afef0e7 100644
--- a/subversion/libsvn_fs_fs/fs.c
+++ b/subversion/libsvn_fs_fs/fs.c
@@ -33,6 +33,7 @@
#include "fs_fs.h"
#include "tree.h"
#include "lock.h"
+#include "id.h"
#include "svn_private_config.h"
#include "private/svn_fs_util.h"
@@ -158,19 +159,13 @@
/* Creating a new filesystem. */
/* Set up vtable and fsap_data fields in FS. */
-static void
+static svn_error_t *
initialize_fs_struct(svn_fs_t *fs)
{
fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
fs->vtable = &fs_vtable;
fs->fsap_data = ffd;
-
- ffd->rev_root_id_cache_pool = svn_pool_create(fs->pool);
- ffd->rev_root_id_cache = apr_hash_make(ffd->rev_root_id_cache_pool);
-
- ffd->rev_node_cache = apr_hash_make(fs->pool);
- ffd->rev_node_list.prev = &ffd->rev_node_list;
- ffd->rev_node_list.next = &ffd->rev_node_list;
+ return SVN_NO_ERROR;
}
/* This implements the fs_library_vtable_t.create() API. Create a new
@@ -183,9 +178,11 @@
{
SVN_ERR(svn_fs__check_fs(fs, FALSE));
- initialize_fs_struct(fs);
+ SVN_ERR(initialize_fs_struct(fs));
SVN_ERR(svn_fs_fs__create(fs, path, pool));
+
+ SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
return fs_serialized_init(fs, common_pool, pool);
}
@@ -201,9 +198,11 @@
fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
apr_pool_t *common_pool)
{
- initialize_fs_struct(fs);
+ SVN_ERR(initialize_fs_struct(fs));
SVN_ERR(svn_fs_fs__open(fs, path, pool));
+
+ SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
return fs_serialized_init(fs, common_pool, pool);
}
@@ -242,8 +241,9 @@
apr_pool_t *common_pool)
{
SVN_ERR(svn_fs__check_fs(fs, FALSE));
- initialize_fs_struct(fs);
+ SVN_ERR(initialize_fs_struct(fs));
SVN_ERR(svn_fs_fs__open(fs, path, pool));
+ SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
SVN_ERR(fs_serialized_init(fs, common_pool, pool));
return svn_fs_fs__upgrade(fs, pool);
}
diff --git a/subversion/libsvn_fs_fs/fs.h b/subversion/libsvn_fs_fs/fs.h
index 421be41..adaab5c 100644
--- a/subversion/libsvn_fs_fs/fs.h
+++ b/subversion/libsvn_fs_fs/fs.h
@@ -25,6 +25,8 @@
#include <apr_network_io.h>
#include "svn_fs.h"
+#include "svn_cache.h"
+#include "svn_config.h"
#include "private/svn_fs_private.h"
#ifdef __cplusplus
@@ -50,6 +52,8 @@
#define PATH_TXN_CURRENT "txn-current" /* File with next txn key */
#define PATH_TXN_CURRENT_LOCK "txn-current-lock" /* Lock for txn-current */
#define PATH_LOCKS_DIR "locks" /* Directory of locks */
+/* If you change this, look at tests/svn_test_fs.c(maybe_install_fsfs_conf) */
+#define PATH_CONFIG "fsfs.conf" /* Configuration */
/* Names of special files and file extensions for transactions */
#define PATH_CHANGES "changes" /* Records changes made so far */
@@ -65,6 +69,10 @@
#define PATH_REV "rev" /* Proto rev file */
#define PATH_REV_LOCK "rev-lock" /* Proto rev (write) lock file */
+/* Names of sections and options in fsfs.conf. */
+#define CONFIG_SECTION_CACHES "caches"
+#define CONFIG_OPTION_FAIL_STOP "fail-stop"
+
/* The format number of this filesystem.
This is independent of the repository format number, and
independent of any other FS back ends. */
@@ -91,15 +99,6 @@
noderev fields. */
#define SVN_FS_FS__MIN_MERGEINFO_FORMAT 3
-/* Maximum number of directories to cache dirents for.
- This *must* be a power of 2 for DIR_CACHE_ENTRIES_MASK
- to work. */
-#define NUM_DIR_CACHE_ENTRIES 128
-#define DIR_CACHE_ENTRIES_MASK(x) ((x) & (NUM_DIR_CACHE_ENTRIES - 1))
-
-/* Maximum number of revroot ids to cache dirents for at a time. */
-#define NUM_RRI_CACHE_ENTRIES 4096
-
/* Private FSFS-specific data shared between all svn_txn_t objects that
relate to a particular transaction in a filesystem (as identified
by transaction id and filesystem UUID). Objects of this type are
@@ -164,32 +163,9 @@
apr_pool_t *common_pool;
} fs_fs_shared_data_t;
-typedef struct dag_node_t dag_node_t;
-
-/* Structure for DAG-node cache. Cache items are arranged in a
- circular LRU list with a dummy entry, and also indexed with a hash
- table. Transaction nodes are cached within the individual txn
- roots; revision nodes are cached together within the FS object. */
-typedef struct dag_node_cache_t
-{
- const char *key; /* Lookup key for cached node: path
- for txns; rev catenated with path
- for revs */
- dag_node_t *node; /* Cached node */
- struct dag_node_cache_t *prev; /* Next node in LRU list */
- struct dag_node_cache_t *next; /* Previous node in LRU list */
- apr_pool_t *pool; /* Pool in which node is allocated */
-} dag_node_cache_t;
-
-
/* Private (non-shared) FSFS-specific data for each svn_fs_t object. */
typedef struct
{
- /* A cache of the last directory opened within the filesystem. */
- svn_fs_id_t *dir_cache_id[NUM_DIR_CACHE_ENTRIES];
- apr_hash_t *dir_cache[NUM_DIR_CACHE_ENTRIES];
- apr_pool_t *dir_cache_pool[NUM_DIR_CACHE_ENTRIES];
-
/* The format number of this FS. */
int format;
/* The maximum number of files to store per directory (for sharded
@@ -202,23 +178,27 @@
/* The revision that was youngest, last time we checked. */
svn_revnum_t youngest_rev_cache;
- /* Caches of immutable data.
-
- Both of these could be moved to fs_fs_shared_data_t to make them
- last longer; on the other hand, this would require adding mutexes
- for threaded builds.
- */
+ /* The fsfs.conf file, parsed. Allocated in FS->pool. */
+ svn_config_t *config;
- /* A cache of revision root IDs, allocated in this subpool. (IDs
- are so small that one pool per ID would be overkill;
- unfortunately, this means the only way we expire cache entries is
- by wiping the whole cache.) */
- apr_hash_t *rev_root_id_cache;
- apr_pool_t *rev_root_id_cache_pool;
+ /* Caches of immutable data. (Note that if these are created with
+ svn_cache_create_memcache, the data can be shared between
+ multiple svn_fs_t's for the same filesystem.) */
+
+ /* A cache of revision root IDs, mapping from (svn_revnum_t *) to
+ (svn_fs_id_t *). (Not threadsafe.) */
+ svn_cache_t *rev_root_id_cache;
/* DAG node cache for immutable nodes */
- dag_node_cache_t rev_node_list;
- apr_hash_t *rev_node_cache;
+ svn_cache_t *rev_node_cache;
+
+ /* A cache of the contents of immutable directories; maps from
+ unparsed FS ID to ###x. */
+ svn_cache_t *dir_cache;
+
+ /* Fulltext cache; currently only used with memcached. Maps from
+ rep key to svn_string_t. */
+ svn_cache_t *fulltext_cache;
/* Data shared between all svn_fs_t objects for a given filesystem. */
fs_fs_shared_data_t *shared;
diff --git a/subversion/libsvn_fs_fs/fs_fs.c b/subversion/libsvn_fs_fs/fs_fs.c
index b59ef06..8b42699 100644
--- a/subversion/libsvn_fs_fs/fs_fs.c
+++ b/subversion/libsvn_fs_fs/fs_fs.c
@@ -39,6 +39,7 @@
#include "svn_sorts.h"
#include "svn_time.h"
#include "svn_mergeinfo.h"
+#include "svn_config.h"
#include "fs.h"
#include "err.h"
@@ -982,6 +983,58 @@
return ffd->format >= SVN_FS_FS__MIN_MERGEINFO_FORMAT;
}
+svn_error_t *
+svn_fs_fs__get_config(svn_config_t **config,
+ svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ if (! ffd->config)
+ SVN_ERR(svn_config_read(&(ffd->config),
+ svn_path_join(fs->path, PATH_CONFIG, pool),
+ FALSE,
+ fs->pool));
+
+ *config = ffd->config;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+write_config(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+#define NL APR_EOL_STR
+ static const char * const fsfs_conf_contents =
+"### This file controls the configuration of the FSFS filesystem." NL
+"" NL
+"[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]" NL
+"### These options name memcached servers used to cache internal FSFS" NL
+"### data. See http://www.danga.com/memcached/ for more information on" NL
+"### memcached. To use memcached with FSFS, run one or more memcached" NL
+"### servers, and specify each of them as an option like so:" NL
+"# first-server = 127.0.0.1:11211" NL
+"# remote-memcached = mymemcached.corp.example.com:11212" NL
+"### The option name is ignored; the value is of the form HOST:PORT." NL
+"### memcached servers can be shared between multiple repositories;" NL
+"### however, if you do this, you *must* ensure that repositories have" NL
+"### distinct UUIDs and paths, or else cached data from one repository" NL
+"### might be used by another accidentally. Note also that memcached has" NL
+"### no authentication for reads or writes, so you must sure that your" NL
+"### memcached servers are only accessible by trusted users." NL
+"" NL
+"[" CONFIG_SECTION_CACHES "]" NL
+"### When a cache-related error occurs, normally Subversion ignores it" NL
+"### and continues, logging an error if the server is appropriately" NL
+"### configured (and ignoring it with file:// access). To make" NL
+"### Subversion never ignore cache errors, uncomment this line." NL
+"# " CONFIG_OPTION_FAIL_STOP " = true" NL
+;
+#undef NL
+ return svn_io_file_create(svn_path_join(fs->path, PATH_CONFIG, pool),
+ fsfs_conf_contents, pool);
+}
+
static svn_error_t *
get_youngest(svn_revnum_t *youngest_p, const char *fs_path, apr_pool_t *pool);
@@ -1364,39 +1417,31 @@
return SVN_NO_ERROR;
}
-/* HEADER_CPATH lines need to be long enough to hold FSFS_MAX_PATH_LEN
- * bytes plus the stuff around them. */
-#define MAX_HEADERS_STR_LEN FSFS_MAX_PATH_LEN + sizeof(HEADER_CPATH ": \n") - 1
-
/* Given a revision file FILE that has been pre-positioned at the
beginning of a Node-Rev header block, read in that header block and
store it in the apr_hash_t HEADERS. All allocations will be from
POOL. */
static svn_error_t * read_header_block(apr_hash_t **headers,
- apr_file_t *file,
+ svn_stream_t *stream,
apr_pool_t *pool)
{
*headers = apr_hash_make(pool);
while (1)
{
- char header_str[MAX_HEADERS_STR_LEN];
+ svn_stringbuf_t *header_str;
const char *name, *value;
- apr_size_t i = 0, header_len;
- apr_size_t limit;
- char *local_name, *local_value;
+ apr_size_t i = 0;
+ svn_boolean_t eof;
- limit = sizeof(header_str);
- SVN_ERR(svn_io_read_length_line(file, header_str, &limit, pool));
+ SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, pool));
- if (strlen(header_str) == 0)
+ if (eof || header_str->len == 0)
break; /* end of header block */
- header_len = strlen(header_str);
-
- while (header_str[i] != ':')
+ while (header_str->data[i] != ':')
{
- if (header_str[i] == '\0')
+ if (header_str->data[i] == '\0')
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Found malformed header in "
"revision file"));
@@ -1404,23 +1449,22 @@
}
/* Create a 'name' string and point to it. */
- header_str[i] = '\0';
- name = header_str;
+ header_str->data[i] = '\0';
+ name = header_str->data;
/* Skip over the NULL byte and the space following it. */
i += 2;
- if (i > header_len)
+ if (i > header_str->len)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Found malformed header in "
"revision file"));
- value = header_str + i;
+ value = header_str->data + i;
- local_name = apr_pstrdup(pool, name);
- local_value = apr_pstrdup(pool, value);
-
- apr_hash_set(*headers, local_name, APR_HASH_KEY_STRING, local_value);
+ /* header_str is safely in our pool, so we can use bits of it as
+ key and value. */
+ apr_hash_set(*headers, name, APR_HASH_KEY_STRING, value);
}
return SVN_NO_ERROR;
@@ -1622,9 +1666,6 @@
apr_pool_t *pool)
{
apr_file_t *revision_file;
- apr_hash_t *headers;
- node_revision_t *noderev;
- char *value;
svn_error_t *err;
if (svn_fs_fs__id_txn_id(id))
@@ -1653,7 +1694,22 @@
return err;
}
- SVN_ERR(read_header_block(&headers, revision_file, pool) );
+ return svn_fs_fs__read_noderev(noderev_p,
+ svn_stream_from_aprfile2(revision_file, FALSE,
+ pool),
+ pool);
+}
+
+svn_error_t *
+svn_fs_fs__read_noderev(node_revision_t **noderev_p,
+ svn_stream_t *stream,
+ apr_pool_t *pool)
+{
+ apr_hash_t *headers;
+ node_revision_t *noderev;
+ char *value;
+
+ SVN_ERR(read_header_block(&headers, stream, pool));
noderev = apr_pcalloc(pool, sizeof(*noderev));
@@ -1663,7 +1719,7 @@
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Missing id field in node-rev"));
- SVN_ERR(svn_io_file_close(revision_file, pool));
+ SVN_ERR(svn_stream_close(stream));
noderev->id = svn_fs_fs__id_parse(value, strlen(value), pool);
@@ -1687,7 +1743,7 @@
if (value)
{
SVN_ERR(read_rep_offsets(&noderev->prop_rep, value,
- svn_fs_fs__id_txn_id(id), TRUE, pool));
+ svn_fs_fs__id_txn_id(noderev->id), TRUE, pool));
}
/* Get the data location. */
@@ -1695,7 +1751,7 @@
if (value)
{
SVN_ERR(read_rep_offsets(&noderev->data_rep, value,
- svn_fs_fs__id_txn_id(id),
+ svn_fs_fs__id_txn_id(noderev->id),
(noderev->kind == svn_node_dir), pool));
}
@@ -1819,19 +1875,13 @@
pool));
}
-/* Write the node-revision NODEREV into the file FILE. Only write
- mergeinfo-related metadata if INCLUDE_MERGEINFO is true. Temporary
- allocations are from POOL. */
-static svn_error_t *
-write_noderev_txn(apr_file_t *file,
- node_revision_t *noderev,
- svn_boolean_t include_mergeinfo,
- apr_pool_t *pool)
+
+svn_error_t *
+svn_fs_fs__write_noderev(svn_stream_t *outfile,
+ node_revision_t *noderev,
+ svn_boolean_t include_mergeinfo,
+ apr_pool_t *pool)
{
- svn_stream_t *outfile;
-
- outfile = svn_stream_from_aprfile(file, pool);
-
SVN_ERR(svn_stream_printf(outfile, pool, HEADER_ID ": %s\n",
svn_fs_fs__id_unparse(noderev->id,
pool)->data));
@@ -1915,9 +1965,10 @@
APR_WRITE | APR_CREATE | APR_TRUNCATE
| APR_BUFFERED, APR_OS_DEFAULT, pool));
- SVN_ERR(write_noderev_txn(noderev_file, noderev,
- svn_fs_fs__fs_supports_mergeinfo(fs),
- pool));
+ SVN_ERR(svn_fs_fs__write_noderev(svn_stream_from_aprfile(noderev_file, pool),
+ noderev,
+ svn_fs_fs__fs_supports_mergeinfo(fs),
+ pool));
SVN_ERR(svn_io_file_close(noderev_file, pool));
@@ -2013,7 +2064,8 @@
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
- SVN_ERR(read_header_block(&headers, rev_file, pool));
+ SVN_ERR(read_header_block(&headers, svn_stream_from_aprfile(rev_file, pool),
+ pool));
node_id_str = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
@@ -2120,21 +2172,14 @@
apr_off_t root_offset;
svn_fs_id_t *root_id;
svn_error_t *err;
- const char *rev_str = apr_psprintf(pool, "%ld", rev);
- svn_fs_id_t *cached_id;
+ svn_boolean_t is_cached;
SVN_ERR(ensure_revision_exists(fs, rev, pool));
- /* Calculate an index into the revroot id cache */
- cached_id = apr_hash_get(ffd->rev_root_id_cache,
- rev_str,
- APR_HASH_KEY_STRING);
-
- if (cached_id)
- {
- *root_id_p = svn_fs_fs__id_copy(cached_id, pool);
- return SVN_NO_ERROR;
- }
+ SVN_ERR(svn_cache_get((void **) root_id_p, &is_cached, ffd->rev_root_id_cache,
+ &rev, pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
err = svn_io_file_open(&revision_file, svn_fs_fs__path_rev(fs, rev, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
@@ -2154,21 +2199,7 @@
SVN_ERR(svn_io_file_close(revision_file, pool));
- /* Make sure our cache size doesn't grow without bounds. */
- if (apr_hash_count(ffd->rev_root_id_cache) >= NUM_RRI_CACHE_ENTRIES)
- {
- /* In order to only use one pool for the whole cache, we need to
- * completely wipe it to expire entries! */
- svn_pool_clear(ffd->rev_root_id_cache_pool);
- ffd->rev_root_id_cache = apr_hash_make(ffd->rev_root_id_cache_pool);
- }
-
- /* Cache the answer, copying both the key and value into the cache's
- pool. */
- apr_hash_set(ffd->rev_root_id_cache,
- apr_pstrdup(ffd->rev_root_id_cache_pool, rev_str),
- APR_HASH_KEY_STRING,
- svn_fs_fs__id_copy(root_id, ffd->rev_root_id_cache_pool));
+ SVN_ERR(svn_cache_set(ffd->rev_root_id_cache, &rev, root_id, pool));
*root_id_p = root_id;
@@ -2419,6 +2450,12 @@
svn_filesize_t len;
svn_filesize_t off;
+ /* The key for the fulltext cache for this rep, if there is a
+ fulltext cache. */
+ const char *fulltext_cache_key;
+ /* The text we've been reading, if we're going to cache it. */
+ svn_stringbuf_t *current_fulltext;
+
/* Used for temporary allocations during the read. */
apr_pool_t *pool;
@@ -2428,12 +2465,15 @@
};
/* Create a rep_read_baton structure for node revision NODEREV in
- filesystem FS and store it in *RB_P. Perform all allocations in
+ filesystem FS and store it in *RB_P. If FULLTEXT_CACHE_KEY is not
+ NULL, it is the rep's key in the fulltext cache, and a stringbuf
+ must be allocated to store the text. Perform all allocations in
POOL. If rep is mutable, it must be for file contents. */
static svn_error_t *
rep_read_get_baton(struct rep_read_baton **rb_p,
svn_fs_t *fs,
representation_t *rep,
+ const char *fulltext_cache_key,
apr_pool_t *pool)
{
struct rep_read_baton *b;
@@ -2447,9 +2487,15 @@
memcpy(b->checksum, rep->checksum, sizeof(b->checksum));
b->len = rep->expanded_size;
b->off = 0;
+ b->fulltext_cache_key = fulltext_cache_key;
b->pool = svn_pool_create(pool);
b->filehandle_pool = svn_pool_create(pool);
+ if (fulltext_cache_key)
+ b->current_fulltext = svn_stringbuf_create("", b->filehandle_pool);
+ else
+ b->current_fulltext = NULL;
+
SVN_ERR(build_rep_list(&b->rs_list, &b->src_state, fs, rep,
b->filehandle_pool));
@@ -2707,6 +2753,9 @@
/* Get the next block of data. */
SVN_ERR(get_contents(rb, buf, len));
+ if (rb->current_fulltext)
+ svn_stringbuf_appendbytes(rb->current_fulltext, buf, *len);
+
/* Perform checksumming. We want to check the checksum as soon as
the last byte of data is read, in case the caller never performs
a short read, but we don't want to finalize the MD5 context
@@ -2731,9 +2780,31 @@
svn_md5_digest_to_cstring_display(checksum, rb->pool));
}
}
+
+ if (rb->off == rb->len && rb->current_fulltext)
+ {
+ fs_fs_data_t *ffd = rb->fs->fsap_data;
+ SVN_ERR(svn_cache_set(ffd->fulltext_cache, rb->fulltext_cache_key,
+ rb->current_fulltext, rb->pool));
+ rb->current_fulltext = NULL;
+ }
+
return SVN_NO_ERROR;
}
+
+/* Returns whether or not the expanded fulltext of the file is
+ * cacheable based on its size SIZE. Specifically, if it will fit
+ * into a memcached value. The memcached cutoff seems to be a bit
+ * (header length?) under a megabyte; we round down a little to be
+ * safe.
+ */
+static svn_boolean_t
+fulltext_size_is_cacheable(svn_filesize_t size)
+{
+ return size < 1000000;
+}
+
/* Return a stream in *CONTENTS_P that will read the contents of a
representation stored at the location given by REP. Appropriate
for any kind of immutable representation, but only for file
@@ -2749,15 +2820,34 @@
representation_t *rep,
apr_pool_t *pool)
{
- struct rep_read_baton *rb;
-
if (! rep)
{
*contents_p = svn_stream_empty(pool);
}
else
{
- SVN_ERR(rep_read_get_baton(&rb, fs, rep, pool));
+ fs_fs_data_t *ffd = fs->fsap_data;
+ const char *fulltext_key = NULL;
+ struct rep_read_baton *rb;
+
+ if (ffd->fulltext_cache && SVN_IS_VALID_REVNUM(rep->revision)
+ && fulltext_size_is_cacheable(rep->expanded_size))
+ {
+ svn_string_t *fulltext;
+ svn_boolean_t is_cached;
+ fulltext_key = apr_psprintf(pool, "%ld/%" APR_OFF_T_FMT,
+ rep->revision, rep->offset);
+ SVN_ERR(svn_cache_get((void **) &fulltext, &is_cached,
+ ffd->fulltext_cache, fulltext_key, pool));
+ if (is_cached)
+ {
+ *contents_p = svn_stream_from_string(fulltext, pool);
+ return SVN_NO_ERROR;
+ }
+ }
+
+ SVN_ERR(rep_read_get_baton(&rb, fs, rep, fulltext_key, pool));
+
*contents_p = svn_stream_create(rb, pool);
svn_stream_set_read(*contents_p, rep_read_contents);
svn_stream_set_close(*contents_p, rep_read_contents_close);
@@ -2896,76 +2986,95 @@
return SVN_NO_ERROR;
}
-/* Return a copy of the directory hash ENTRIES in POOL. */
-static apr_hash_t *
-copy_dir_entries(apr_hash_t *entries,
- apr_pool_t *pool)
+
+static const char *
+unparse_dir_entry(svn_node_kind_t kind, const svn_fs_id_t *id,
+ apr_pool_t *pool)
{
- apr_hash_t *new_entries = apr_hash_make(pool);
+ return apr_psprintf(pool, "%s %s",
+ (kind == svn_node_file) ? KIND_FILE : KIND_DIR,
+ svn_fs_fs__id_unparse(id, pool)->data);
+}
+
+/* Given a hash ENTRIES of dirent structions, return a hash in
+ *STR_ENTRIES_P, that has svn_string_t as the values in the format
+ specified by the fs_fs directory contents file. Perform
+ allocations in POOL. */
+static svn_error_t *
+unparse_dir_entries(apr_hash_t **str_entries_p,
+ apr_hash_t *entries,
+ apr_pool_t *pool)
+{
apr_hash_index_t *hi;
+ *str_entries_p = apr_hash_make(pool);
+
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
+ const void *key;
+ apr_ssize_t klen;
void *val;
- svn_fs_dirent_t *dirent, *new_dirent;
+ svn_fs_dirent_t *dirent;
+ const char *new_val;
- apr_hash_this(hi, NULL, NULL, &val);
+ apr_hash_this(hi, &key, &klen, &val);
dirent = val;
- new_dirent = apr_palloc(pool, sizeof(*new_dirent));
- new_dirent->name = apr_pstrdup(pool, dirent->name);
- new_dirent->kind = dirent->kind;
- new_dirent->id = svn_fs_fs__id_copy(dirent->id, pool);
- apr_hash_set(new_entries, new_dirent->name, APR_HASH_KEY_STRING,
- new_dirent);
+ new_val = unparse_dir_entry(dirent->kind, dirent->id, pool);
+ apr_hash_set(*str_entries_p, key, klen,
+ svn_string_create(new_val, pool));
}
- return new_entries;
+
+ return SVN_NO_ERROR;
}
svn_error_t *
-svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
- svn_fs_t *fs,
- node_revision_t *noderev,
- apr_pool_t *pool)
+svn_fs_fs__dir_entries_serialize(char **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool)
{
- fs_fs_data_t *ffd = fs->fsap_data;
- apr_hash_t *unparsed_entries, *parsed_entries;
+ apr_hash_t *entries = in;
+ svn_stringbuf_t *buf = svn_stringbuf_create("", pool);
+ svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool);
+
+ SVN_ERR(unparse_dir_entries(&entries, entries, pool));
+ SVN_ERR(svn_hash_write2(entries, stream, SVN_HASH_TERMINATOR, pool));
+
+ *data = buf->data;
+ *data_len = buf->len;
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Given a hash STR_ENTRIES with values as svn_string_t as specified
+ in an FSFS directory contents listing, return a hash of dirents in
+ *ENTRIES_P. Perform allocations in POOL. */
+static svn_error_t *
+parse_dir_entries(apr_hash_t **entries_p,
+ apr_hash_t *str_entries,
+ apr_pool_t *pool)
+{
apr_hash_index_t *hi;
- unsigned int hid;
- /* Calculate an index into the dir entries cache. This should be
- completely ignored if this is a mutable noderev. */
- hid = DIR_CACHE_ENTRIES_MASK(svn_fs_fs__id_rev(noderev->id));
-
- /* If we have this directory cached, return it. */
- if (! svn_fs_fs__id_txn_id(noderev->id) &&
- ffd->dir_cache_id[hid] && svn_fs_fs__id_eq(ffd->dir_cache_id[hid],
- noderev->id))
- {
- *entries_p = copy_dir_entries(ffd->dir_cache[hid], pool);
- return SVN_NO_ERROR;
- }
-
- /* Read in the directory hash. */
- unparsed_entries = apr_hash_make(pool);
- SVN_ERR(get_dir_contents(unparsed_entries, fs, noderev, pool));
-
- parsed_entries = apr_hash_make(pool);
+ *entries_p = apr_hash_make(pool);
/* Translate the string dir entries into real entries. */
- for (hi = apr_hash_first(pool, unparsed_entries); hi; hi = apr_hash_next(hi))
+ for (hi = apr_hash_first(pool, str_entries); hi; hi = apr_hash_next(hi))
{
const void *key;
void *val;
- char *str_val;
+ svn_string_t *str_val;
char *str, *last_str;
svn_fs_dirent_t *dirent = apr_pcalloc(pool, sizeof(*dirent));
apr_hash_this(hi, &key, NULL, &val);
- str_val = apr_pstrdup(pool, *((char **)val));
+ str_val = val;
+ str = apr_pstrdup(pool, str_val->data);
dirent->name = apr_pstrdup(pool, key);
- str = apr_strtok(str_val, " ", &last_str);
+ str = apr_strtok(str, " ", &last_str);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Directory entry corrupt"));
@@ -2991,26 +3100,61 @@
dirent->id = svn_fs_fs__id_parse(str, strlen(str), pool);
- apr_hash_set(parsed_entries, dirent->name, APR_HASH_KEY_STRING, dirent);
+ apr_hash_set(*entries_p, dirent->name, APR_HASH_KEY_STRING, dirent);
}
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__dir_entries_deserialize(void **out,
+ const char *data,
+ apr_size_t data_len,
+ apr_pool_t *pool)
+{
+ apr_hash_t *entries = apr_hash_make(pool);
+ svn_stringbuf_t *buf = svn_stringbuf_ncreate(data, data_len, pool);
+ svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool);
+
+ SVN_ERR(svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool));
+ SVN_ERR(parse_dir_entries(&entries, entries, pool));
+
+ *out = entries;
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
+ svn_fs_t *fs,
+ node_revision_t *noderev,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ const char *unparsed_id;
+ apr_hash_t *unparsed_entries, *parsed_entries;
+
+ /* Are we looking for an immutable directory? We could try the
+ * cache. */
+ if (! svn_fs_fs__id_txn_id(noderev->id))
+ {
+ svn_boolean_t found;
+
+ unparsed_id = svn_fs_fs__id_unparse(noderev->id, pool)->data;
+ SVN_ERR(svn_cache_get((void **) entries_p, &found, ffd->dir_cache,
+ unparsed_id, pool));
+ if (found)
+ return SVN_NO_ERROR;
+ }
+
+ /* Read in the directory hash. */
+ unparsed_entries = apr_hash_make(pool);
+ SVN_ERR(get_dir_contents(unparsed_entries, fs, noderev, pool));
+ SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries, pool));
+
/* If this is an immutable directory, let's cache the contents. */
if (! svn_fs_fs__id_txn_id(noderev->id))
- {
- /* Start by NULLing the ID field, so that we never leave the
- cache in an illegal state. */
- ffd->dir_cache_id[hid] = NULL;
-
- if (ffd->dir_cache_pool[hid])
- svn_pool_clear(ffd->dir_cache_pool[hid]);
- else
- ffd->dir_cache_pool[hid] = svn_pool_create(fs->pool);
-
- ffd->dir_cache[hid] = copy_dir_entries(parsed_entries,
- ffd->dir_cache_pool[hid]);
- ffd->dir_cache_id[hid] = svn_fs_fs__id_copy(noderev->id,
- ffd->dir_cache_pool[hid]);
- }
+ SVN_ERR(svn_cache_set(ffd->dir_cache, unparsed_id, parsed_entries, pool));
*entries_p = parsed_entries;
return SVN_NO_ERROR;
@@ -4076,15 +4220,8 @@
svn_fs_fs__abort_txn(svn_fs_txn_t *txn,
apr_pool_t *pool)
{
- fs_fs_data_t *ffd;
-
SVN_ERR(svn_fs__check_fs(txn->fs, TRUE));
- /* Clean out the directory cache. */
- ffd = txn->fs->fsap_data;
- memset(&ffd->dir_cache_id, 0,
- sizeof(svn_fs_id_t *) * NUM_DIR_CACHE_ENTRIES);
-
/* Now, purge the transaction. */
SVN_ERR_W(svn_fs_fs__purge_txn(txn->fs, txn->id, pool),
_("Transaction cleanup failed"));
@@ -4093,47 +4230,6 @@
}
-static const char *
-unparse_dir_entry(svn_node_kind_t kind, const svn_fs_id_t *id,
- apr_pool_t *pool)
-{
- return apr_psprintf(pool, "%s %s",
- (kind == svn_node_file) ? KIND_FILE : KIND_DIR,
- svn_fs_fs__id_unparse(id, pool)->data);
-}
-
-/* Given a hash ENTRIES of dirent structions, return a hash in
- *STR_ENTRIES_P, that has svn_string_t as the values in the format
- specified by the fs_fs directory contents file. Perform
- allocations in POOL. */
-static svn_error_t *
-unparse_dir_entries(apr_hash_t **str_entries_p,
- apr_hash_t *entries,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
-
- *str_entries_p = apr_hash_make(pool);
-
- for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- apr_ssize_t klen;
- void *val;
- svn_fs_dirent_t *dirent;
- const char *new_val;
-
- apr_hash_this(hi, &key, &klen, &val);
- dirent = val;
- new_val = unparse_dir_entry(dirent->kind, dirent->id, pool);
- apr_hash_set(*str_entries_p, key, klen,
- svn_string_create(new_val, pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-
svn_error_t *
svn_fs_fs__set_entry(svn_fs_t *fs,
const char *txn_id,
@@ -4868,9 +4964,10 @@
noderev->id = new_id;
/* Write out our new node-revision. */
- SVN_ERR(write_noderev_txn(file, noderev,
- svn_fs_fs__fs_supports_mergeinfo(fs),
- pool));
+ SVN_ERR(svn_fs_fs__write_noderev(svn_stream_from_aprfile(file, pool),
+ noderev,
+ svn_fs_fs__fs_supports_mergeinfo(fs),
+ pool));
/* Return our ID that references the revision file. */
*new_id_p = noderev->id;
@@ -5462,6 +5559,8 @@
"", pool));
}
+ SVN_ERR(write_config(fs, pool));
+
/* This filesystem is ready. Stamp it with a format number. */
SVN_ERR(write_format(path_format(fs, pool),
ffd->format, ffd->max_files_per_dir, FALSE, pool));
@@ -5582,7 +5681,8 @@
apr_pool_t *iterpool;
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
- SVN_ERR(read_header_block(&headers, rev_file, pool));
+ SVN_ERR(read_header_block(&headers, svn_stream_from_aprfile(rev_file, pool),
+ pool));
/* We're going to populate a skeletal noderev - just the id and data_rep. */
value = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
diff --git a/subversion/libsvn_fs_fs/fs_fs.h b/subversion/libsvn_fs_fs/fs_fs.h
index 1f0f17c..1b37e86 100644
--- a/subversion/libsvn_fs_fs/fs_fs.h
+++ b/subversion/libsvn_fs_fs/fs_fs.h
@@ -60,6 +60,23 @@
svn_boolean_t fresh_txn_root,
apr_pool_t *pool);
+/* Write the node-revision NODEREV into the stream OUTFILE. Only write
+ mergeinfo-related metadata if INCLUDE_MERGEINFO is true. Temporary
+ allocations are from POOL. */
+svn_error_t *
+svn_fs_fs__write_noderev(svn_stream_t *outfile,
+ node_revision_t *noderev,
+ svn_boolean_t include_mergeinfo,
+ apr_pool_t *pool);
+
+/* Reads the node-revision *NODEREV from the stream STREAM. Temporary
+ allocations are from POOL. */
+svn_error_t *
+svn_fs_fs__read_noderev(node_revision_t **noderev,
+ svn_stream_t *stream,
+ apr_pool_t *pool);
+
+
/* Set *YOUNGEST to the youngest revision in filesystem FS. Do any
temporary allocation in POOL. */
svn_error_t *svn_fs_fs__youngest_rev(svn_revnum_t *youngest,
@@ -73,6 +90,12 @@
svn_revnum_t rev,
apr_pool_t *pool);
+/* Serialize a directory contents hash. */
+svn_cache_serialize_func_t svn_fs_fs__dir_entries_serialize;
+
+/* Deserialize a directory contents hash. */
+svn_cache_deserialize_func_t svn_fs_fs__dir_entries_deserialize;
+
/* Set *ENTRIES to an apr_hash_t of dirent structs that contain the
directory entries of node-revision NODEREV in filesystem FS. The
returned table (and its keys and values) is allocated in POOL,
@@ -180,6 +203,14 @@
/* Return whether or not the given FS supports mergeinfo metadata. */
svn_boolean_t svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs);
+/* Sets *CONFIG to the parsed version of FS's fsfs.conf, allocated
+ in FS->pool. POOL is used for temporary allocations. */
+svn_error_t *
+svn_fs_fs__get_config(svn_config_t **config,
+ svn_fs_t *fs,
+ apr_pool_t *pool);
+
+
/* Store a transaction record in *TXN_P for the transaction identified
by TXN_ID in filesystem FS. Allocate everything from POOL. */
svn_error_t *svn_fs_fs__get_txn(transaction_t **txn_p,
@@ -469,4 +500,10 @@
apr_pool_t *pool);
+/* Sets up the svn_cache_t structures in FS. POOL is used for
+ temporary allocations. */
+svn_error_t *
+svn_fs_fs__initialize_caches(svn_fs_t *fs, apr_pool_t *pool);
+
+
#endif
diff --git a/subversion/libsvn_fs_fs/structure b/subversion/libsvn_fs_fs/structure
index 3ff4b8f..daef181 100644
--- a/subversion/libsvn_fs_fs/structure
+++ b/subversion/libsvn_fs_fs/structure
@@ -54,6 +54,7 @@
txn-current-lock Empty file, locked to serialise 'txn-current'
uuid File containing the UUID of the repository
format File containing the format number of this filesystem
+ fsfs.conf Configuration file
Files in the revprops directory are in the hash dump format used by
svn_hash_write.
@@ -86,6 +87,10 @@
performs on this file is "get and increment"; the "txn-current-lock"
file is locked during this operation.
+"fsfs.conf" is a configuration file in the standard Subversion/Python
+config format. It is automatically generated when you create a new
+repository; read the generated file for details on what it controls.
+
Filesystem formats
------------------
diff --git a/subversion/libsvn_fs_fs/tree.c b/subversion/libsvn_fs_fs/tree.c
index bb15649..f9524a3 100644
--- a/subversion/libsvn_fs_fs/tree.c
+++ b/subversion/libsvn_fs_fs/tree.c
@@ -76,10 +76,6 @@
to the database (which also generates more log-files). */
#define WRITE_BUFFER_SIZE 512000
-/* The maximum number of cache items to maintain in the node cache. */
-#define TXN_NODE_CACHE_MAX_KEYS 32
-#define REV_NODE_CACHE_MAX_KEYS 128
-
/* The root structures.
@@ -111,9 +107,9 @@
typedef struct
{
- /* Dummy entry for circular LRU cache, and associated hash table. */
- dag_node_cache_t txn_node_list;
- apr_hash_t *txn_node_cache;
+ /* Cache of txn DAG nodes (without their nested noderevs, because
+ * it's mutable). */
+ svn_cache_t *txn_node_cache;
} fs_txn_root_data_t;
/* Declared here to resolve the circular dependencies. */
@@ -124,16 +120,18 @@
dag_node_t *root_dir,
apr_pool_t *pool);
-static svn_fs_root_t *make_txn_root(svn_fs_t *fs, const char *txn,
- svn_revnum_t base_rev, apr_uint32_t flags,
- apr_pool_t *pool);
+static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
+ svn_fs_t *fs, const char *txn,
+ svn_revnum_t base_rev, apr_uint32_t flags,
+ apr_pool_t *pool);
/*** Node Caching ***/
+/* Find and return the DAG node cache for ROOT and the key that
+ should be used for PATH. */
static void
-locate_cache(dag_node_cache_t **node_list,
- apr_hash_t **node_cache,
+locate_cache(svn_cache_t **cache,
const char **key,
svn_fs_root_t *root,
const char *path,
@@ -142,148 +140,130 @@
if (root->is_txn_root)
{
fs_txn_root_data_t *frd = root->fsap_data;
- *node_list = &frd->txn_node_list;
- *node_cache = frd->txn_node_cache;
- *key = path;
+ if (cache) *cache = frd->txn_node_cache;
+ if (key && path) *key = path;
}
else
{
fs_fs_data_t *ffd = root->fs->fsap_data;
- *node_list = &ffd->rev_node_list;
- *node_cache = ffd->rev_node_cache;
- *key = apr_psprintf(pool, "%ld%s",
- root->rev, path);
+ if (cache) *cache = ffd->rev_node_cache;
+ if (key && path) *key = apr_psprintf(pool, "%ld%s",
+ root->rev, path);
}
}
/* Return NODE for PATH from ROOT's node cache, or NULL if the node
- isn't cached. */
-static dag_node_t *
-dag_node_cache_get(svn_fs_root_t *root,
+ isn't cached; the node is copied into POOL. */
+static svn_error_t *
+dag_node_cache_get(dag_node_t **node_p,
+ svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
- dag_node_cache_t *item, *node_list;
- apr_hash_t *node_cache;
+ svn_boolean_t found;
+ dag_node_t *node;
+ svn_cache_t *cache;
const char *key;
/* Assert valid input. */
assert(*path == '/');
- locate_cache(&node_list, &node_cache, &key,
- root, path, pool);
+ locate_cache(&cache, &key, root, path, pool);
- /* Look in the cache for our desired item. */
- item = apr_hash_get(node_cache, key, APR_HASH_KEY_STRING);
- if (item && item->node)
+ SVN_ERR(svn_cache_get((void **) &node, &found, cache, key, pool));
+ if (found && node)
{
- /* Move this cache item to the front of the LRU list. */
- item->prev->next = item->next;
- item->next->prev = item->prev;
- item->prev = node_list;
- item->next = node_list->next;
- item->prev->next = item;
- item->next->prev = item;
-
- /* Return the cached node. */
- return svn_fs_fs__dag_dup(item->node, pool);
+ /* Patch up the FS, since this might have come from an old FS
+ * object. */
+ svn_fs_fs__dag_set_fs(node, root->fs);
+ *node_p = node;
}
-
- return NULL;
+ else
+ *node_p = NULL;
+ return SVN_NO_ERROR;
}
/* Add the NODE for PATH to ROOT's node cache. */
-static void
+static svn_error_t *
dag_node_cache_set(svn_fs_root_t *root,
const char *path,
dag_node_t *node,
- apr_pool_t *temp_pool)
+ apr_pool_t *pool)
{
- dag_node_cache_t *item, *node_list;
- apr_hash_t *node_cache;
+ svn_cache_t *cache;
const char *key;
- apr_pool_t *pool;
- int max_keys = root->is_txn_root
- ? TXN_NODE_CACHE_MAX_KEYS : REV_NODE_CACHE_MAX_KEYS;
-
- /* The pool passed to this function can *only* be used for
- short-term calculations, not for the actual cache value!
-
- To ensure that our cache values live as long as the svn_fs_root_t
- in which they are ultimately stored, and to allow us to free()
- them individually without harming the rest, they are each
- allocated from a subpool of ROOT's pool. We'll keep one subpool
- around for each cache slot -- as we start expiring stuff
- to make room for more entries, we'll re-use the expired thing's
- pool. */
/* Assert valid input and state. */
assert(*path == '/');
- locate_cache(&node_list, &node_cache, &key,
- root, path, temp_pool);
+ locate_cache(&cache, &key, root, path, pool);
- /* If we have an existing entry for this path, reuse it. */
- item = apr_hash_get(node_cache, key, APR_HASH_KEY_STRING);
-
- /* Otherwise, if the cache is full, reuse the tail of the LRU list. */
- if (!item && apr_hash_count(node_cache) == max_keys)
- item = node_list->prev;
-
- if (item)
- {
- /* Remove the existing item from the cache and reuse its pool. */
- item->prev->next = item->next;
- item->next->prev = item->prev;
- apr_hash_set(node_cache, item->key, APR_HASH_KEY_STRING, NULL);
- pool = item->pool;
- svn_pool_clear(pool);
- }
- else
- {
- /* Allocate a new pool. */
- apr_pool_t *parent_pool = root->is_txn_root ? root->pool : root->fs->pool;
- pool = svn_pool_create(parent_pool);
- }
-
- /* Create and fill in the cache item. */
- item = apr_palloc(pool, sizeof(*item));
- item->key = apr_pstrdup(pool, key);
- item->node = svn_fs_fs__dag_dup(node, pool);
- item->pool = pool;
-
- /* Link it into the head of the LRU list and hash table. */
- item->prev = node_list;
- item->next = node_list->next;
- item->prev->next = item;
- item->next->prev = item;
- apr_hash_set(node_cache, item->key, APR_HASH_KEY_STRING, item);
+ return svn_cache_set(cache, key, node, pool);
}
-/* Invalidate cache entries for PATH and any of its children. */
-static void
-dag_node_cache_invalidate(svn_fs_root_t *root,
- const char *path)
+/* Baton for find_descendents_in_cache. */
+struct fdic_baton {
+ const char *path;
+ apr_array_header_t *list;
+ apr_pool_t *pool;
+};
+
+/* If the given item is a descendent of BATON->PATH, push
+ * it onto BATON->LIST (copying into BATON->POOL). Implements
+ * the svn_iter_apr_hash_cb_t prototype. */
+static svn_error_t *
+find_descendents_in_cache(void *baton,
+ const void *key,
+ apr_ssize_t klen,
+ void *val,
+ apr_pool_t *pool)
{
- fs_txn_root_data_t *frd;
- apr_size_t len = strlen(path);
- const char *key;
- dag_node_cache_t *item;
+ struct fdic_baton *b = baton;
+ const char *item_path = key;
+
+ if (svn_path_is_ancestor(b->path, item_path))
+ APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
+
+ return SVN_NO_ERROR;
+}
+
+/* Invalidate cache entries for PATH and any of its children. This
+ should *only* be called on a transaction root! */
+static svn_error_t *
+dag_node_cache_invalidate(svn_fs_root_t *root,
+ const char *path,
+ apr_pool_t *pool)
+{
+ struct fdic_baton b;
+ svn_cache_t *cache;
+ apr_pool_t *iterpool;
+ int i;
+
+ b.path = path;
+ b.pool = svn_pool_create(pool);
+ b.list = apr_array_make(b.pool, 1, sizeof(const char *));
assert(root->is_txn_root);
+ locate_cache(&cache, NULL, root, NULL, b.pool);
- frd = root->fsap_data;
- for (item = frd->txn_node_list.next;
- item != &frd->txn_node_list;
- item = item->next)
+ SVN_ERR(svn_cache_iter(NULL, cache, find_descendents_in_cache,
+ &b, b.pool));
+
+ iterpool = svn_pool_create(b.pool);
+
+ for (i = 0; i < b.list->nelts; i++)
{
- key = item->key;
- if (strncmp(key, path, len) == 0 && (key[len] == '/' || !key[len]))
- item->node = NULL;
+ const char *descendent = APR_ARRAY_IDX(b.list, i, const char *);
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_cache_set(cache, descendent, NULL, iterpool));
}
+
+ svn_pool_destroy(iterpool);
+ svn_pool_destroy(b.pool);
+ return SVN_NO_ERROR;
}
@@ -295,7 +275,6 @@
svn_fs_txn_t *txn,
apr_pool_t *pool)
{
- svn_fs_root_t *root;
apr_uint32_t flags = 0;
apr_hash_t *txnprops;
@@ -312,11 +291,7 @@
flags |= SVN_FS_TXN_CHECK_LOCKS;
}
- root = make_txn_root(txn->fs, txn->id, txn->base_rev, flags, pool);
-
- *root_p = root;
-
- return SVN_NO_ERROR;
+ return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
}
@@ -661,7 +636,7 @@
/* If we found a directory entry, follow it. First, we
check our node cache, and, failing that, we hit the DAG
layer. */
- cached_node = dag_node_cache_get(root, path_so_far, pool);
+ SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
if (cached_node)
child = cached_node;
else
@@ -706,7 +681,7 @@
/* Cache the node we found (if it wasn't already cached). */
if (! cached_node)
- dag_node_cache_set(root, path_so_far, child, pool);
+ SVN_ERR(dag_node_cache_set(root, path_so_far, child, pool));
}
/* Are we finished traversing the path? */
@@ -808,8 +783,8 @@
pool));
/* Update the path cache. */
- dag_node_cache_set(root, parent_path_path(parent_path, pool), clone,
- pool);
+ SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
+ clone, pool));
}
else
{
@@ -834,13 +809,13 @@
apr_pool_t *pool)
{
parent_path_t *parent_path;
- dag_node_t *node = NULL;
+ dag_node_t *node;
/* Canonicalize the input PATH. */
path = svn_fs__canonicalize_abspath(path, pool);
- /* If ROOT is a revision root, we'll look for the DAG in our cache. */
- node = dag_node_cache_get(root, path, pool);
+ /* First we look for the DAG in our cache. */
+ SVN_ERR(dag_node_cache_get(&node, root, path, pool));
if (! node)
{
/* Call open_path with no flags, as we want this to return an error
@@ -1907,7 +1882,8 @@
pool));
/* Add this directory to the path cache. */
- dag_node_cache_set(root, parent_path_path(parent_path, pool), sub_dir, pool);
+ SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
+ sub_dir, pool));
/* Make a record of this modification in the changes table. */
SVN_ERR(add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
@@ -1956,7 +1932,8 @@
txn_id, pool));
/* Remove this node and any children from the path cache. */
- dag_node_cache_invalidate(root, parent_path_path(parent_path, pool));
+ SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
+ pool));
/* Update mergeinfo counts for parents */
if (svn_fs_fs__fs_supports_mergeinfo(root->fs) && mergeinfo_count > 0)
@@ -2095,8 +2072,9 @@
txn_id, pool));
if (kind == svn_fs_path_change_replace)
- dag_node_cache_invalidate(to_root, parent_path_path(to_parent_path,
- pool));
+ SVN_ERR(dag_node_cache_invalidate(to_root,
+ parent_path_path(to_parent_path,
+ pool), pool));
if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
&& mergeinfo_start != mergeinfo_end)
@@ -2256,7 +2234,8 @@
pool));
/* Add this file to the path cache. */
- dag_node_cache_set(root, parent_path_path(parent_path, pool), child, pool);
+ SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
+ pool));
/* Make a record of this modification in the changes table. */
SVN_ERR(add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
@@ -3786,8 +3765,9 @@
/* Construct a root object referring to the root of the transaction
named TXN and based on revision BASE_REV in FS, with FLAGS to
describe transaction's behavior. Create the new root in POOL. */
-static svn_fs_root_t *
-make_txn_root(svn_fs_t *fs,
+static svn_error_t *
+make_txn_root(svn_fs_root_t **root_p,
+ svn_fs_t *fs,
const char *txn,
svn_revnum_t base_rev,
apr_uint32_t flags,
@@ -3801,11 +3781,18 @@
root->txn_flags = flags;
root->rev = base_rev;
- frd->txn_node_cache = apr_hash_make(root->pool);
- frd->txn_node_list.prev = &frd->txn_node_list;
- frd->txn_node_list.next = &frd->txn_node_list;
+ /* Because this cache actually tries to invalidate elements, keep
+ the number of elements per page down.
+
+ Note that since dag_node_cache_invalidate uses svn_cache_iter,
+ this *cannot* be a memcache-based cache. */
+ SVN_ERR(svn_cache_create_inprocess(&(frd->txn_node_cache),
+ svn_fs_fs__dag_dup_for_cache,
+ APR_HASH_KEY_STRING,
+ 32, 20, FALSE, root->pool));
root->fsap_data = frd;
- return root;
+ *root_p = root;
+ return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_local/ra_plugin.c b/subversion/libsvn_ra_local/ra_plugin.c
index 6d855f4..d2786f8 100644
--- a/subversion/libsvn_ra_local/ra_plugin.c
+++ b/subversion/libsvn_ra_local/ra_plugin.c
@@ -408,6 +408,28 @@
return schemes;
}
+/* Do nothing.
+ *
+ * Why is this acceptable? As of now, FS warnings are used for only
+ * two things: failures to close BDB repositories and failures to
+ * interact with memcached in FSFS (new in 1.6). In 1.5 and earlier,
+ * we did not call svn_fs_set_warning_func in ra_local, which means
+ * that any BDB-closing failure would have led to abort()s; the fact
+ * that this hasn't led to huge hues and cries makes it seem likely
+ * that this just doesn't happen that often, at least not through
+ * ra_local. And as far as memcached goes, it seems unlikely that
+ * somebody is going to go through the trouble of setting up and
+ * running memcached servers but then use ra_local access. So we
+ * ignore errors here, so that memcached can use the FS warnings API
+ * without crashing ra_local.
+ */
+static void
+ignore_warnings(void *baton,
+ svn_error_t *err)
+{
+ return;
+}
+
static svn_error_t *
svn_ra_local__open(svn_ra_session_t *session,
const char *repos_URL,
@@ -439,6 +461,9 @@
convenience. */
sess->fs = svn_repos_fs(sess->repos);
+ /* Ignore FS warnings. */
+ svn_fs_set_warning_func(sess->fs, ignore_warnings, NULL);
+
/* Cache the repository UUID as well */
SVN_ERR(svn_fs_get_uuid(sess->fs, &sess->uuid, session->pool));
diff --git a/subversion/libsvn_subr/cache-inprocess.c b/subversion/libsvn_subr/cache-inprocess.c
new file mode 100644
index 0000000..cef78cd
--- /dev/null
+++ b/subversion/libsvn_subr/cache-inprocess.c
@@ -0,0 +1,461 @@
+/*
+ * cache-inprocess.c: in-memory caching for Subversion
+ *
+ * ====================================================================
+ * Copyright (c) 2008 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#include <assert.h>
+
+#include <apr_thread_mutex.h>
+
+#include "svn_pools.h"
+
+#include "svn_private_config.h"
+
+#include "cache.h"
+
+/* The (internal) cache object. */
+typedef struct {
+ /* Maps from a key (of size CACHE->KLEN) to a struct cache_entry. */
+ apr_hash_t *hash;
+ apr_ssize_t klen;
+
+ /* Used to copy values in and out of the cache. */
+ svn_cache_dup_func_t *dup_func;
+
+ /* The number of pages we're allowed to allocate before having to
+ * try to reuse one. */
+ apr_int64_t unallocated_pages;
+ /* Number of cache entries stored on each page. Must be at least 1. */
+ apr_int64_t items_per_page;
+
+ /* A dummy cache_page serving as the head of a circular doubly
+ * linked list of cache_pages. SENTINEL->NEXT is the most recently
+ * used page, and SENTINEL->PREV is the least recently used page.
+ * All pages in this list are "full"; the page currently being
+ * filled (PARTIAL_PAGE) is not in the list. */
+ struct cache_page *sentinel;
+
+ /* A page currently being filled with entries, or NULL if there's no
+ * partially-filled page. This page is not in SENTINEL's list. */
+ struct cache_page *partial_page;
+ /* If PARTIAL_PAGE is not null, this is the number of entries
+ * currently on PARTIAL_PAGE. */
+ apr_int64_t partial_page_number_filled;
+
+ /* The pool that the svn_cache_t itself, HASH, and all pages are
+ * allocated in; subpools of this pool are used for the cache_entry
+ * structs, as well as the dup'd values and hash keys.
+ */
+ apr_pool_t *cache_pool;
+
+#if APR_HAS_THREADS
+ /* A lock for intra-process synchronization to the cache, or NULL if
+ * the cache's creator doesn't feel the cache needs to be
+ * thread-safe. */
+ apr_thread_mutex_t *mutex;
+#endif
+} inprocess_cache_t;
+
+/* A cache page; all items on the page are allocated from the same
+ * pool. */
+struct cache_page {
+ /* Pointers for the LRU list anchored at the cache's SENTINEL.
+ * (NULL for the PARTIAL_PAGE.) */
+ struct cache_page *prev;
+ struct cache_page *next;
+
+ /* The pool in which cache_entry structs, hash keys, and dup'd
+ * values are allocated. */
+ apr_pool_t *page_pool;
+
+ /* A singly linked list of the entries on this page; used to remove
+ * them from the cache's HASH before reusing the page. */
+ struct cache_entry *first_entry;
+};
+
+/* An cache entry. */
+struct cache_entry {
+ const void *key;
+ void *value;
+
+ /* The page it's on (needed so that the LRU list can be
+ * maintained). */
+ struct cache_page *page;
+
+ /* Next entry on the page. */
+ struct cache_entry *next_entry;
+};
+
+
+/* Removes PAGE from the doubly-linked list it is in (leaving its PREV
+ * and NEXT fields undefined). */
+static void
+remove_page_from_list(struct cache_page *page)
+{
+ page->prev->next = page->next;
+ page->next->prev = page->prev;
+}
+
+/* Inserts PAGE after CACHE's sentinel. */
+static void
+insert_page(inprocess_cache_t *cache,
+ struct cache_page *page)
+{
+ struct cache_page *pred = cache->sentinel;
+
+ page->prev = pred;
+ page->next = pred->next;
+ page->prev->next = page;
+ page->next->prev = page;
+}
+
+/* If PAGE is in the circularly linked list (eg, its NEXT isn't NULL),
+ * move it to the front of the list. */
+static void
+move_page_to_front(inprocess_cache_t *cache,
+ struct cache_page *page)
+{
+ assert(page != cache->sentinel);
+
+ if (! page->next)
+ return;
+
+ remove_page_from_list(page);
+ insert_page(cache, page);
+}
+
+/* Uses CACHE->dup_func to copy VALUE into *VALUE_P inside POOL, or
+ just sets *VALUE_P to NULL if VALUE is NULL. */
+static svn_error_t *
+duplicate_value(void **value_p,
+ inprocess_cache_t *cache,
+ void *value,
+ apr_pool_t *pool)
+{
+ if (value)
+ SVN_ERR((cache->dup_func)(value_p, value, pool));
+ else
+ *value_p = NULL;
+ return SVN_NO_ERROR;
+}
+
+/* Return a copy of KEY inside POOL, using CACHE->KLEN to figure out
+ * how. */
+static const void *
+duplicate_key(inprocess_cache_t *cache,
+ const void *key,
+ apr_pool_t *pool)
+{
+ if (cache->klen == APR_HASH_KEY_STRING)
+ return apr_pstrdup(pool, key);
+ else
+ return apr_pmemdup(pool, key, cache->klen);
+}
+
+/* If applicable, locks CACHE's mutex. */
+static svn_error_t *
+lock_cache(inprocess_cache_t *cache)
+{
+#if APR_HAS_THREADS
+ apr_status_t status;
+ if (! cache->mutex)
+ return SVN_NO_ERROR;
+
+ status = apr_thread_mutex_lock(cache->mutex);
+ if (status)
+ return svn_error_wrap_apr(status, _("Can't lock cache mutex"));
+#endif
+
+ return SVN_NO_ERROR;
+}
+
+/* If applicable, unlocks CACHE's mutex, then returns ERR. */
+static svn_error_t *
+unlock_cache(inprocess_cache_t *cache,
+ svn_error_t *err)
+{
+#if APR_HAS_THREADS
+ apr_status_t status;
+ if (! cache->mutex)
+ return err;
+
+ status = apr_thread_mutex_unlock(cache->mutex);
+ if (status && !err)
+ return svn_error_wrap_apr(status, _("Can't unlock cache mutex"));
+#endif
+
+ return err;
+}
+
+svn_error_t *
+inprocess_cache_get(void **value_p,
+ svn_boolean_t *found,
+ void *cache_void,
+ const void *key,
+ apr_pool_t *pool)
+{
+ inprocess_cache_t *cache = cache_void;
+ struct cache_entry *entry;
+ svn_error_t *err;
+
+ SVN_ERR(lock_cache(cache));
+
+ entry = apr_hash_get(cache->hash, key, cache->klen);
+ if (! entry)
+ {
+ *found = FALSE;
+ return unlock_cache(cache, SVN_NO_ERROR);
+ }
+
+ move_page_to_front(cache, entry->page);
+
+ *found = TRUE;
+ err = duplicate_value(value_p, cache, entry->value, pool);
+ return unlock_cache(cache, err);
+}
+
+/* Removes PAGE from the LRU list, removes all of its entries from
+ * CACHE's hash, clears its pool, and sets its entry pointer to NULL.
+ * Finally, puts it in the "partial page" slot in the cache and sets
+ * partial_page_number_filled to 0. Must be called on a page actually
+ * in the list. */
+static void
+erase_page(inprocess_cache_t *cache,
+ struct cache_page *page)
+{
+ struct cache_entry *e;
+
+ remove_page_from_list(page);
+
+ for (e = page->first_entry;
+ e;
+ e = e->next_entry)
+ {
+ apr_hash_set(cache->hash, e->key, cache->klen, NULL);
+ }
+
+ svn_pool_clear(page->page_pool);
+
+ page->first_entry = NULL;
+ page->prev = NULL;
+ page->next = NULL;
+
+ cache->partial_page = page;
+ cache->partial_page_number_filled = 0;
+}
+
+
+svn_error_t *
+inprocess_cache_set(void *cache_void,
+ const void *key,
+ void *value,
+ apr_pool_t *pool)
+{
+ inprocess_cache_t *cache = cache_void;
+ struct cache_entry *existing_entry;
+ svn_error_t *err = SVN_NO_ERROR;
+
+ SVN_ERR(lock_cache(cache));
+
+ existing_entry = apr_hash_get(cache->hash, key, cache->klen);
+
+ /* Is it already here, but we can do the one-item-per-page
+ * optimization? */
+ if (existing_entry && cache->items_per_page == 1)
+ {
+ /* Special case! ENTRY is the *only* entry on this page, so
+ * why not wipe it (so as not to leak the previous value).
+ */
+ struct cache_page *page = existing_entry->page;
+
+ /* This can't be the partial page: items_per_page == 1
+ * *never* has a partial page (except for in the temporary state
+ * that we're about to fake). */
+ assert(page->next != NULL);
+ assert(cache->partial_page == NULL);
+
+ erase_page(cache, page);
+ existing_entry = NULL;
+ }
+
+ /* Is it already here, and we just have to leak the old value? */
+ if (existing_entry)
+ {
+ struct cache_page *page = existing_entry->page;
+
+ move_page_to_front(cache, page);
+ err = duplicate_value(&(existing_entry->value), cache,
+ value, page->page_pool);
+ goto cleanup;
+ }
+
+ /* Do we not have a partial page to put it on, but we are allowed to
+ * allocate more? */
+ if (cache->partial_page == NULL && cache->unallocated_pages > 0)
+ {
+ cache->partial_page = apr_pcalloc(cache->cache_pool,
+ sizeof(*(cache->partial_page)));
+ cache->partial_page->page_pool = svn_pool_create(cache->cache_pool);
+ cache->partial_page_number_filled = 0;
+ (cache->unallocated_pages)--;
+ }
+
+ /* Do we really not have a partial page to put it on, even after the
+ * one-item-per-page optimization and checking the unallocated page
+ * count? */
+ if (cache->partial_page == NULL)
+ {
+ struct cache_page *oldest_page = cache->sentinel->prev;
+
+ assert(oldest_page != cache->sentinel);
+
+ /* Erase the page and put it in cache->partial_page. */
+ erase_page(cache, oldest_page);
+ }
+
+ assert(cache->partial_page != NULL);
+
+ {
+ struct cache_page *page = cache->partial_page;
+ struct cache_entry *new_entry = apr_pcalloc(page->page_pool,
+ sizeof(*new_entry));
+
+ /* Copy the key and value into the page's pool. */
+ new_entry->key = duplicate_key(cache, key, page->page_pool);
+ err = duplicate_value(&(new_entry->value), cache, value,
+ page->page_pool);
+ if (err)
+ goto cleanup;
+
+ /* Add the entry to the page's list. */
+ new_entry->page = page;
+ new_entry->next_entry = page->first_entry;
+ page->first_entry = new_entry;
+
+ /* Add the entry to the hash, using the *entry's* copy of the
+ * key. */
+ apr_hash_set(cache->hash, new_entry->key, cache->klen, new_entry);
+
+ /* We've added something else to the partial page. */
+ (cache->partial_page_number_filled)++;
+
+ /* Is it full? */
+ if (cache->partial_page_number_filled >= cache->items_per_page)
+ {
+ insert_page(cache, page);
+ cache->partial_page = NULL;
+ }
+ }
+
+ cleanup:
+ return unlock_cache(cache, err);
+}
+
+/* Baton type for svn_cache_iter. */
+struct cache_iter_baton {
+ svn_iter_apr_hash_cb_t user_cb;
+ void *user_baton;
+};
+
+/* Call the user's callback with the actual value, not the
+ cache_entry. Implements the svn_iter_apr_hash_cb_t
+ prototype. */
+static svn_error_t *
+iter_cb(void *baton,
+ const void *key,
+ apr_ssize_t klen,
+ void *val,
+ apr_pool_t *pool)
+{
+ struct cache_iter_baton *b = baton;
+ struct cache_entry *entry = val;
+ return (b->user_cb)(b->user_baton, key, klen, entry->value, pool);
+}
+
+svn_error_t *
+inprocess_cache_iter(svn_boolean_t *completed,
+ void *cache_void,
+ svn_iter_apr_hash_cb_t user_cb,
+ void *user_baton,
+ apr_pool_t *pool)
+{
+ inprocess_cache_t *cache = cache_void;
+ struct cache_iter_baton b;
+ b.user_cb = user_cb;
+ b.user_baton = user_baton;
+
+ SVN_ERR(lock_cache(cache));
+ return unlock_cache(cache,
+ svn_iter_apr_hash(completed, cache->hash, iter_cb, &b,
+ pool));
+
+}
+
+static svn_cache__vtable_t inprocess_cache_vtable = {
+ inprocess_cache_get,
+ inprocess_cache_set,
+ inprocess_cache_iter
+};
+
+svn_error_t *
+svn_cache_create_inprocess(svn_cache_t **cache_p,
+ svn_cache_dup_func_t *dup_func,
+ apr_ssize_t klen,
+ apr_int64_t pages,
+ apr_int64_t items_per_page,
+ svn_boolean_t thread_safe,
+ apr_pool_t *pool)
+{
+ svn_cache_t *wrapper = apr_pcalloc(pool, sizeof(*wrapper));
+ inprocess_cache_t *cache = apr_pcalloc(pool, sizeof(*cache));
+
+ cache->hash = apr_hash_make(pool);
+ cache->klen = klen;
+
+ cache->dup_func = dup_func;
+
+ assert(pages >= 1);
+ cache->unallocated_pages = pages;
+ assert(items_per_page >= 1);
+ cache->items_per_page = items_per_page;
+
+ cache->sentinel = apr_pcalloc(pool, sizeof(*(cache->sentinel)));
+ cache->sentinel->prev = cache->sentinel;
+ cache->sentinel->next = cache->sentinel;
+ /* The sentinel doesn't need a pool. (We're happy to crash if we
+ * accidentally try to treat it like a real page.) */
+
+#if APR_HAS_THREADS
+ if (thread_safe)
+ {
+ apr_status_t status = apr_thread_mutex_create(&(cache->mutex),
+ APR_THREAD_MUTEX_DEFAULT,
+ pool);
+ if (status)
+ return svn_error_wrap_apr(status,
+ _("Can't create cache mutex"));
+ }
+#endif
+
+ cache->cache_pool = pool;
+
+ wrapper->vtable = &inprocess_cache_vtable;
+ wrapper->cache_internal = cache;
+ wrapper->error_handler = wrapper->error_baton = NULL;
+
+ *cache_p = wrapper;
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_subr/cache-memcache.c b/subversion/libsvn_subr/cache-memcache.c
new file mode 100644
index 0000000..bf0af81
--- /dev/null
+++ b/subversion/libsvn_subr/cache-memcache.c
@@ -0,0 +1,417 @@
+/*
+ * cache-memcache.c: memcached caching for Subversion
+ *
+ * ====================================================================
+ * Copyright (c) 2008 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#include <assert.h>
+
+#include "svn_cache.h"
+#include "svn_pools.h"
+#include "svn_base64.h"
+#include "svn_md5.h"
+#include "svn_path.h"
+
+#include "svn_private_config.h"
+
+#include "cache.h"
+
+#if SVN_HAVE_MEMCACHE
+
+#include <apr_memcache.h>
+
+/* A note on thread safety:
+
+ The apr_memcache_t object does its own mutex handling, and nothing
+ else in memcache_t is ever modified, so this implementation should
+ be fully thread-safe.
+*/
+
+/* The (internal) cache object. */
+typedef struct {
+ /* The memcached server set we're using. */
+ apr_memcache_t *memcache;
+
+ /* A prefix used to differentiate our data from any other data in
+ * the memcached (URI-encoded). */
+ const char *prefix;
+
+ /* The size of the key: either a fixed number of bytes or
+ * APR_HASH_KEY_STRING. */
+ apr_ssize_t klen;
+
+
+ /* Used to marshal values in and out of the cache. */
+ svn_cache_serialize_func_t *serialize_func;
+ svn_cache_deserialize_func_t *deserialize_func;
+} memcache_t;
+
+/* The wrapper around apr_memcache_t. */
+struct svn_memcache_t {
+ apr_memcache_t *c;
+};
+
+
+/* The memcached protocol says the maximum key length is 250. Let's
+ just say 249, to be safe. */
+#define MAX_MEMCACHED_KEY_LEN 249
+#define MEMCACHED_KEY_UNHASHED_LEN (MAX_MEMCACHED_KEY_LEN - \
+ 2 * APR_MD5_DIGESTSIZE)
+
+
+/* Returns a memcache key for the given key KEY for CACHE, allocated
+ in POOL. */
+const char *
+build_key(memcache_t *cache,
+ const void *raw_key,
+ apr_pool_t *pool)
+{
+ const char *encoded_suffix;
+ const char *long_key;
+ apr_size_t long_key_len;
+
+ if (cache->klen == APR_HASH_KEY_STRING)
+ encoded_suffix = svn_path_uri_encode(raw_key, pool);
+ else
+ {
+ const svn_string_t *raw = svn_string_ncreate(raw_key, cache->klen, pool);
+ const svn_string_t *encoded = svn_base64_encode_string2(raw, FALSE,
+ pool);
+ encoded_suffix = encoded->data;
+ }
+
+ long_key = apr_pstrcat(pool, "SVN:", cache->prefix, ":", encoded_suffix,
+ NULL);
+ long_key_len = strlen(long_key);
+
+ /* We don't want to have a key that's too big. If it was going to
+ be too big, we MD5 the entire string, then replace the last bit
+ with the checksum. Note that APR_MD5_DIGESTSIZE is for the pure
+ binary digest; we have to double that when we convert to hex.
+
+ Every key we use will either be at most
+ MEMCACHED_KEY_UNHASHED_LEN bytes long, or be exactly
+ MAX_MEMCACHED_KEY_LEN bytes long. */
+ if (long_key_len > MEMCACHED_KEY_UNHASHED_LEN)
+ {
+ unsigned char digest[APR_MD5_DIGESTSIZE];
+ apr_md5(digest, long_key, long_key_len);
+
+ long_key = apr_pstrcat(pool,
+ apr_pstrmemdup(pool, long_key,
+ MEMCACHED_KEY_UNHASHED_LEN),
+ svn_md5_digest_to_cstring_display(digest, pool),
+ NULL);
+ }
+
+ return long_key;
+}
+
+
+svn_error_t *
+memcache_get(void **value_p,
+ svn_boolean_t *found,
+ void *cache_void,
+ const void *key,
+ apr_pool_t *pool)
+{
+ memcache_t *cache = cache_void;
+ apr_status_t apr_err;
+ char *data;
+ const char *mc_key;
+ apr_size_t data_len;
+ apr_pool_t *subpool = svn_pool_create(pool);
+
+ mc_key = build_key(cache, key, subpool);
+
+ apr_err = apr_memcache_getp(cache->memcache,
+ (cache->deserialize_func ? subpool : pool),
+ mc_key,
+ &data,
+ &data_len,
+ NULL /* ignore flags */);
+ if (apr_err == APR_NOTFOUND)
+ {
+ *found = FALSE;
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+ }
+ else if (apr_err != APR_SUCCESS || !data)
+ return svn_error_wrap_apr(apr_err,
+ _("Unknown memcached error while reading"));
+
+ /* We found it! */
+ if (cache->deserialize_func)
+ {
+ SVN_ERR((cache->deserialize_func)(value_p, data, data_len, pool));
+ }
+ else
+ {
+ svn_string_t *value = apr_pcalloc(pool, sizeof(*value));
+ value->data = data;
+ value->len = data_len;
+ *value_p = value;
+ }
+ *found = TRUE;
+
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+memcache_set(void *cache_void,
+ const void *key,
+ void *value,
+ apr_pool_t *pool)
+{
+ memcache_t *cache = cache_void;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ char *data;
+ const char *mc_key = build_key(cache, key, subpool);
+ apr_size_t data_len;
+ apr_status_t apr_err;
+
+ if (cache->serialize_func)
+ {
+ SVN_ERR((cache->serialize_func)(&data, &data_len, value, subpool));
+ }
+ else
+ {
+ svn_stringbuf_t *value_str = value;
+ data = value_str->data;
+ data_len = value_str->len;
+ }
+
+ apr_err = apr_memcache_set(cache->memcache, mc_key, data, data_len, 0, 0);
+
+ /* ### Maybe write failures should be ignored (but logged)? */
+ if (apr_err != APR_SUCCESS)
+ return svn_error_wrap_apr(apr_err,
+ _("Unknown memcached error while writing"));
+
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+memcache_iter(svn_boolean_t *completed,
+ void *cache_void,
+ svn_iter_apr_hash_cb_t user_cb,
+ void *user_baton,
+ apr_pool_t *pool)
+{
+ return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("Can't iterate a memcached cache."));
+}
+
+static svn_cache__vtable_t memcache_vtable = {
+ memcache_get,
+ memcache_set,
+ memcache_iter
+};
+
+svn_error_t *
+svn_cache_create_memcache(svn_cache_t **cache_p,
+ svn_memcache_t *memcache,
+ svn_cache_serialize_func_t *serialize_func,
+ svn_cache_deserialize_func_t *deserialize_func,
+ apr_ssize_t klen,
+ const char *prefix,
+ apr_pool_t *pool)
+{
+ svn_cache_t *wrapper = apr_pcalloc(pool, sizeof(*wrapper));
+ memcache_t *cache = apr_pcalloc(pool, sizeof(*cache));
+
+ cache->serialize_func = serialize_func;
+ cache->deserialize_func = deserialize_func;
+ cache->klen = klen;
+ cache->prefix = svn_path_uri_encode(prefix, pool);
+ cache->memcache = memcache->c;
+
+ wrapper->vtable = &memcache_vtable;
+ wrapper->cache_internal = cache;
+ wrapper->error_handler = wrapper->error_baton = NULL;
+
+ *cache_p = wrapper;
+ return SVN_NO_ERROR;
+}
+
+
+/*** Creating apr_memcache_t from svn_config_t. ***/
+
+/* Baton for add_memcache_server. */
+struct ams_baton {
+ apr_memcache_t *memcache;
+ apr_pool_t *memcache_pool;
+ svn_error_t *err;
+};
+
+/* Implements svn_config_enumerator2_t. */
+static svn_boolean_t
+add_memcache_server(const char *name,
+ const char *value,
+ void *baton,
+ apr_pool_t *pool)
+{
+ struct ams_baton *b = baton;
+ char *host, *scope;
+ apr_port_t port;
+ apr_status_t apr_err;
+ apr_memcache_server_t *server;
+
+ apr_err = apr_parse_addr_port(&host, &scope, &port,
+ value, pool);
+ if (apr_err != APR_SUCCESS)
+ {
+ b->err = svn_error_wrap_apr(apr_err,
+ _("Error parsing memcache server '%s'"),
+ name);
+ return FALSE;
+ }
+
+ if (scope)
+ {
+ b->err = svn_error_createf(SVN_ERR_BAD_SERVER_SPECIFICATION, NULL,
+ _("Scope not allowed in memcache server "
+ "'%s'"),
+ name);
+ return FALSE;
+ }
+ if (!host || !port)
+ {
+ b->err = svn_error_createf(SVN_ERR_BAD_SERVER_SPECIFICATION, NULL,
+ _("Must specify host and port for memcache "
+ "server '%s'"),
+ name);
+ return FALSE;
+ }
+
+ /* Note: the four numbers here are only relevant when an
+ apr_memcache_t is being shared by multiple threads. */
+ apr_err = apr_memcache_server_create(b->memcache_pool,
+ host,
+ port,
+ 0, /* min connections */
+ 5, /* soft max connections */
+ 10, /* hard max connections */
+ 50, /* connection time to live (secs) */
+ &server);
+ if (apr_err != APR_SUCCESS)
+ {
+ b->err = svn_error_wrap_apr(apr_err,
+ _("Unknown error creating memcache server"));
+ return FALSE;
+ }
+
+ apr_err = apr_memcache_add_server(b->memcache, server);
+ if (apr_err != APR_SUCCESS)
+ {
+ b->err = svn_error_wrap_apr(apr_err,
+ _("Unknown error adding server to memcache"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#else /* ! SVN_HAVE_MEMCACHE */
+
+/* Stubs for no apr memcache library. */
+
+struct svn_memcache_t {
+ void *unused; /* Let's not have a size-zero struct. */
+};
+
+svn_error_t *
+svn_cache_create_memcache(svn_cache_t **cache_p,
+ svn_memcache_t *memcache,
+ svn_cache_serialize_func_t *serialize_func,
+ svn_cache_deserialize_func_t *deserialize_func,
+ apr_ssize_t klen,
+ const char *prefix,
+ apr_pool_t *pool)
+{
+ return svn_error_create(SVN_ERR_NO_APR_MEMCACHE, NULL, NULL);
+}
+
+#endif /* SVN_HAVE_MEMCACHE */
+
+/* Implements svn_config_enumerator2_t. Just used for the
+ entry-counting return value of svn_config_enumerate2. */
+static svn_boolean_t
+nop_enumerator(const char *name,
+ const char *value,
+ void *baton,
+ apr_pool_t *pool)
+{
+ return TRUE;
+}
+
+svn_error_t *
+svn_cache_make_memcache_from_config(svn_memcache_t **memcache_p,
+ svn_config_t *config,
+ apr_pool_t *pool)
+{
+ apr_uint16_t server_count;
+ apr_pool_t *subpool = svn_pool_create(pool);
+
+ server_count =
+ svn_config_enumerate2(config,
+ SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS,
+ nop_enumerator, NULL, subpool);
+
+ if (server_count == 0)
+ {
+ *memcache_p = NULL;
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+ }
+
+#if SVN_HAVE_MEMCACHE
+ {
+ struct ams_baton b;
+ svn_memcache_t *memcache = apr_pcalloc(pool, sizeof(*memcache));
+ apr_status_t apr_err = apr_memcache_create(pool,
+ server_count,
+ 0, /* flags */
+ &(memcache->c));
+ if (apr_err != APR_SUCCESS)
+ return svn_error_wrap_apr(apr_err,
+ _("Unknown error creating apr_memcache_t"));
+
+ b.memcache = memcache->c;
+ b.memcache_pool = pool;
+ b.err = SVN_NO_ERROR;
+ svn_config_enumerate2(config,
+ SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS,
+ add_memcache_server, &b,
+ subpool);
+
+ if (b.err)
+ return b.err;
+
+ *memcache_p = memcache;
+
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+ }
+#else /* ! SVN_HAVE_MEMCACHE */
+ {
+ return svn_error_create(SVN_ERR_NO_APR_MEMCACHE, NULL, NULL);
+ }
+#endif /* SVN_HAVE_MEMCACHE */
+}
diff --git a/subversion/libsvn_subr/cache.c b/subversion/libsvn_subr/cache.c
new file mode 100644
index 0000000..3aed9e1
--- /dev/null
+++ b/subversion/libsvn_subr/cache.c
@@ -0,0 +1,93 @@
+/*
+ * cache.c: cache interface for Subversion
+ *
+ * ====================================================================
+ * Copyright (c) 2008 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#include "cache.h"
+
+svn_error_t *
+svn_cache_set_error_handler(svn_cache_t *cache,
+ svn_cache_error_handler_t *handler,
+ void *baton,
+ apr_pool_t *pool)
+{
+ cache->error_handler = handler;
+ cache->error_baton = baton;
+ return SVN_NO_ERROR;
+}
+
+
+/* Give the error handler callback a chance to replace or ignore the
+ error. */
+static svn_error_t *
+handle_error(svn_cache_t *cache,
+ svn_error_t *err,
+ apr_pool_t *pool)
+{
+ if (err && cache->error_handler)
+ err = (cache->error_handler)(err, cache->error_baton, pool);
+ return err;
+}
+
+
+svn_error_t *
+svn_cache_get(void **value_p,
+ svn_boolean_t *found,
+ svn_cache_t *cache,
+ const void *key,
+ apr_pool_t *pool)
+{
+ /* In case any errors happen and are quelched, make sure we start
+ out with FOUND set to false. */
+ *found = FALSE;
+ return handle_error(cache,
+ (cache->vtable->get)(value_p,
+ found,
+ cache->cache_internal,
+ key,
+ pool),
+ pool);
+}
+
+svn_error_t *
+svn_cache_set(svn_cache_t *cache,
+ const void *key,
+ void *value,
+ apr_pool_t *pool)
+{
+ return handle_error(cache,
+ (cache->vtable->set)(cache->cache_internal,
+ key,
+ value,
+ pool),
+ pool);
+}
+
+
+svn_error_t *
+svn_cache_iter(svn_boolean_t *completed,
+ svn_cache_t *cache,
+ svn_iter_apr_hash_cb_t user_cb,
+ void *user_baton,
+ apr_pool_t *pool)
+{
+ return (cache->vtable->iter)(completed,
+ cache->cache_internal,
+ user_cb,
+ user_baton,
+ pool);
+}
+
diff --git a/subversion/libsvn_subr/cache.h b/subversion/libsvn_subr/cache.h
new file mode 100644
index 0000000..b39b236
--- /dev/null
+++ b/subversion/libsvn_subr/cache.h
@@ -0,0 +1,59 @@
+/*
+ * cache.h: cache vtable interface
+ *
+ * ====================================================================
+ * Copyright (c) 2008 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#ifndef SVN_LIBSVN_SUBR_CACHE_H
+#define SVN_LIBSVN_SUBR_CACHE_H
+
+#include "svn_cache.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct {
+ svn_error_t *(*get)(void **value,
+ svn_boolean_t *found,
+ void *cache_implementation,
+ const void *key,
+ apr_pool_t *pool);
+
+ svn_error_t *(*set)(void *cache_implementation,
+ const void *key,
+ void *value,
+ apr_pool_t *pool);
+
+ svn_error_t *(*iter)(svn_boolean_t *completed,
+ void *cache_implementation,
+ svn_iter_apr_hash_cb_t func,
+ void *baton,
+ apr_pool_t *pool);
+} svn_cache__vtable_t;
+
+struct svn_cache_t {
+ const svn_cache__vtable_t *vtable;
+ svn_cache_error_handler_t *error_handler;
+ void *error_baton;
+ void *cache_internal;
+};
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_LIBSVN_SUBR_CACHE_H */
diff --git a/subversion/libsvn_subr/stream.c b/subversion/libsvn_subr/stream.c
index 532c41e..5680b7a 100644
--- a/subversion/libsvn_subr/stream.c
+++ b/subversion/libsvn_subr/stream.c
@@ -820,13 +820,59 @@
/* Miscellaneous stream functions. */
-struct string_stream_baton
+struct stringbuf_stream_baton
{
svn_stringbuf_t *str;
apr_size_t amt_read;
};
static svn_error_t *
+read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)
+{
+ struct stringbuf_stream_baton *btn = baton;
+ apr_size_t left_to_read = btn->str->len - btn->amt_read;
+
+ *len = (*len > left_to_read) ? left_to_read : *len;
+ memcpy(buffer, btn->str->data + btn->amt_read, *len);
+ btn->amt_read += *len;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+write_handler_stringbuf(void *baton, const char *data, apr_size_t *len)
+{
+ struct stringbuf_stream_baton *btn = baton;
+
+ svn_stringbuf_appendbytes(btn->str, data, *len);
+ return SVN_NO_ERROR;
+}
+
+svn_stream_t *
+svn_stream_from_stringbuf(svn_stringbuf_t *str,
+ apr_pool_t *pool)
+{
+ svn_stream_t *stream;
+ struct stringbuf_stream_baton *baton;
+
+ if (! str)
+ return svn_stream_empty(pool);
+
+ baton = apr_palloc(pool, sizeof(*baton));
+ baton->str = str;
+ baton->amt_read = 0;
+ stream = svn_stream_create(baton, pool);
+ svn_stream_set_read(stream, read_handler_stringbuf);
+ svn_stream_set_write(stream, write_handler_stringbuf);
+ return stream;
+}
+
+struct string_stream_baton
+{
+ svn_string_t *str;
+ apr_size_t amt_read;
+};
+
+static svn_error_t *
read_handler_string(void *baton, char *buffer, apr_size_t *len)
{
struct string_stream_baton *btn = baton;
@@ -838,18 +884,9 @@
return SVN_NO_ERROR;
}
-static svn_error_t *
-write_handler_string(void *baton, const char *data, apr_size_t *len)
-{
- struct string_stream_baton *btn = baton;
-
- svn_stringbuf_appendbytes(btn->str, data, *len);
- return SVN_NO_ERROR;
-}
-
svn_stream_t *
-svn_stream_from_stringbuf(svn_stringbuf_t *str,
- apr_pool_t *pool)
+svn_stream_from_string(svn_string_t *str,
+ apr_pool_t *pool)
{
svn_stream_t *stream;
struct string_stream_baton *baton;
@@ -862,7 +899,6 @@
baton->amt_read = 0;
stream = svn_stream_create(baton, pool);
svn_stream_set_read(stream, read_handler_string);
- svn_stream_set_write(stream, write_handler_string);
return stream;
}
diff --git a/subversion/libsvn_subr/svn_base64.c b/subversion/libsvn_subr/svn_base64.c
index 3886221..45d49b1 100644
--- a/subversion/libsvn_subr/svn_base64.c
+++ b/subversion/libsvn_subr/svn_base64.c
@@ -66,10 +66,11 @@
data from call to call, and *LINELEN carries the length of the
current output line. Make INBUF have room for three characters and
initialize *INBUFLEN and *LINELEN to 0. Output will be appended to
- STR. */
+ STR. Include newlines every so often if BREAK_LINES is true. */
static void
encode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len,
- unsigned char *inbuf, int *inbuflen, int *linelen)
+ unsigned char *inbuf, int *inbuflen, int *linelen,
+ svn_boolean_t break_lines)
{
char group[4];
const char *p = data, *end = data + len;
@@ -83,7 +84,7 @@
svn_stringbuf_appendbytes(str, group, 4);
*inbuflen = 0;
*linelen += 4;
- if (*linelen == BASE64_LINELEN)
+ if (break_lines && *linelen == BASE64_LINELEN)
{
svn_stringbuf_appendcstr(str, "\n");
*linelen = 0;
@@ -96,11 +97,12 @@
}
-/* Encode leftover data, if any, and possibly a final newline,
- appending to STR. LEN must be in the range 0..2. */
+/* Encode leftover data, if any, and possibly a final newline (if
+ there has been any data and BREAK_LINES is set), appending to STR.
+ LEN must be in the range 0..2. */
static void
encode_partial_group(svn_stringbuf_t *str, const unsigned char *extra,
- int len, int linelen)
+ int len, int linelen, svn_boolean_t break_lines)
{
unsigned char ingroup[3];
char outgroup[4];
@@ -114,7 +116,7 @@
svn_stringbuf_appendbytes(str, outgroup, 4);
linelen += 4;
}
- if (linelen > 0)
+ if (break_lines && linelen > 0)
svn_stringbuf_appendcstr(str, "\n");
}
@@ -130,7 +132,7 @@
svn_error_t *err = SVN_NO_ERROR;
/* Encode this block of data and write it out. */
- encode_bytes(encoded, data, *len, eb->buf, &eb->buflen, &eb->linelen);
+ encode_bytes(encoded, data, *len, eb->buf, &eb->buflen, &eb->linelen, TRUE);
enclen = encoded->len;
if (enclen != 0)
err = svn_stream_write(eb->output, encoded->data, &enclen);
@@ -149,7 +151,7 @@
svn_error_t *err = SVN_NO_ERROR;
/* Encode a partial group at the end if necessary, and write it out. */
- encode_partial_group(encoded, eb->buf, eb->buflen, eb->linelen);
+ encode_partial_group(encoded, eb->buf, eb->buflen, eb->linelen, TRUE);
enclen = encoded->len;
if (enclen != 0)
err = svn_stream_write(eb->output, encoded->data, &enclen);
@@ -181,20 +183,30 @@
const svn_string_t *
-svn_base64_encode_string(const svn_string_t *str, apr_pool_t *pool)
+svn_base64_encode_string2(const svn_string_t *str,
+ svn_boolean_t break_lines,
+ apr_pool_t *pool)
{
svn_stringbuf_t *encoded = svn_stringbuf_create("", pool);
svn_string_t *retval = apr_pcalloc(pool, sizeof(*retval));
unsigned char ingroup[3];
int ingrouplen = 0, linelen = 0;
- encode_bytes(encoded, str->data, str->len, ingroup, &ingrouplen, &linelen);
- encode_partial_group(encoded, ingroup, ingrouplen, linelen);
+ encode_bytes(encoded, str->data, str->len, ingroup, &ingrouplen, &linelen,
+ break_lines);
+ encode_partial_group(encoded, ingroup, ingrouplen, linelen,
+ break_lines);
retval->data = encoded->data;
retval->len = encoded->len;
return retval;
}
+const svn_string_t *
+svn_base64_encode_string(const svn_string_t *str, apr_pool_t *pool)
+{
+ return svn_base64_encode_string2(str, TRUE, pool);
+}
+
/* Base64-encoded input --> binary output */
@@ -373,8 +385,8 @@
* does an implicit unsigned char * cast.
*/
encode_bytes(md5str, (char*)digest, APR_MD5_DIGESTSIZE, ingroup,
- &ingrouplen, &linelen);
- encode_partial_group(md5str, ingroup, ingrouplen, linelen);
+ &ingrouplen, &linelen, TRUE);
+ encode_partial_group(md5str, ingroup, ingrouplen, linelen, TRUE);
/* Our base64-encoding routines append a final newline if any data
was created at all, so let's hack that off. */
diff --git a/subversion/svnserve/serve.c b/subversion/svnserve/serve.c
index d812326..69dc6ad 100644
--- a/subversion/svnserve/serve.c
+++ b/subversion/svnserve/serve.c
@@ -2889,6 +2889,7 @@
return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
"No access allowed to this repository",
b, conn, pool);
+
return SVN_NO_ERROR;
}
diff --git a/subversion/tests/cmdline/svndumpfilter_tests.py b/subversion/tests/cmdline/svndumpfilter_tests.py
index 3893355..0d394fa 100755
--- a/subversion/tests/cmdline/svndumpfilter_tests.py
+++ b/subversion/tests/cmdline/svndumpfilter_tests.py
@@ -72,7 +72,8 @@
"--skip-missing-merge-sources",
"--drop-empty-revs",
"--renumber-revs", "--quiet")
- load_and_verify_dumpstream(sbox, [], [], None, filtered_out)
+ load_and_verify_dumpstream(sbox, [], [], None, filtered_out,
+ "--ignore-uuid")
# Verify the svn:mergeinfo properties
svntest.actions.run_and_verify_svn(None,
@@ -91,7 +92,8 @@
"--skip-missing-merge-sources",
"--drop-empty-revs",
"--renumber-revs", "--quiet")
- load_and_verify_dumpstream(sbox, [], [], None, filtered_out)
+ load_and_verify_dumpstream(sbox, [], [], None, filtered_out,
+ "--ignore-uuid")
# Verify the svn:mergeinfo properties
svntest.actions.run_and_verify_svn(None,
diff --git a/subversion/tests/cmdline/svntest/main.py b/subversion/tests/cmdline/svntest/main.py
index da28863..17e845f 100644
--- a/subversion/tests/cmdline/svntest/main.py
+++ b/subversion/tests/cmdline/svntest/main.py
@@ -153,6 +153,9 @@
# ('neon', 'serf')
http_library = None
+# Configuration file (copied into FSFS fsfs.conf).
+config_file = None
+
# Global variable indicating what the minor version of the server
# tested against is (4 for 1.4.x, for example).
server_minor_version = 5
@@ -304,6 +307,11 @@
return os.path.join(repo_dir, "conf", "svnserve.conf")
+def get_fsfs_conf_file_path(repo_dir):
+ "Return the path of the fsfs.conf file in REPO_DIR."
+
+ return os.path.join(repo_dir, "db", "fsfs.conf")
+
# Run any binary, logging the command line and return code
def run_command(command, error_expected, binary_mode=0, *varargs):
"""Run COMMAND with VARARGS; return exit code as int; stdout, stderr
@@ -625,6 +633,10 @@
file_append(get_svnserve_conf_file_path(path), "password-db = passwd\n")
file_append(os.path.join(path, "conf", "passwd"),
"[users]\njrandom = rayjandom\njconstant = rayjandom\n");
+
+ if config_file is not None and (fs_type is None or fs_type == 'fsfs'):
+ shutil.copy(config_file, get_fsfs_conf_file_path(path))
+
# make the repos world-writeable, for mod_dav_svn's sake.
chmod_tree(path, 0666, 0666)
@@ -1241,6 +1253,7 @@
" useful during test development!"
print " --server-minor-version Set the minor version for the server.\n" \
" Supports version 4 or 5."
+ print " --config-file Configuration file for tests."
print " --help This information"
@@ -1270,6 +1283,7 @@
global svnversion_binary
global command_line_parsed
global http_library
+ global config_file
global server_minor_version
testnums = []
@@ -1279,13 +1293,14 @@
parallel = 0
svn_bin = None
use_jsvn = False
+ config_file = None
try:
opts, args = my_getopt(sys.argv[1:], 'vqhpc',
['url=', 'fs-type=', 'verbose', 'quiet', 'cleanup',
'list', 'enable-sasl', 'help', 'parallel',
- 'bin=', 'http-library=', 'server-minor-version=',
- 'use-jsvn', 'development'])
+ 'bin=', 'http-library=', 'server-minor-version=',
+ 'use-jsvn', 'development', 'config-file='])
except getopt.GetoptError, e:
print "ERROR: %s\n" % e
usage()
@@ -1355,6 +1370,9 @@
elif opt == '--development':
setup_development_mode()
+ elif opt == '--config-file':
+ config_file = val
+
if test_area_url[-1:] == '/': # Normalize url to have no trailing slash
test_area_url = test_area_url[:-1]
diff --git a/subversion/tests/libsvn_fs/fs-test.c b/subversion/tests/libsvn_fs/fs-test.c
index 23aa5a2..8859830 100644
--- a/subversion/tests/libsvn_fs/fs-test.c
+++ b/subversion/tests/libsvn_fs/fs-test.c
@@ -133,7 +133,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_fs(&fs, "test-repo-trivial-txn",
- opts->fs_type, pool));
+ opts, pool));
/* Begin a new transaction that is based on revision 0. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
@@ -189,7 +189,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_fs(&fs, "test-repo-reopen-trivial-txn",
- opts->fs_type, pool));
+ opts, pool));
/* Begin a new transaction that is based on revision 0. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
@@ -228,7 +228,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_fs(&fs, "test-repo-create-file-txn",
- opts->fs_type, pool));
+ opts, pool));
/* Begin a new transaction that is based on revision 0. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
@@ -262,7 +262,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_fs(&fs, "test-repo-verify-txn-list",
- opts->fs_type, pool));
+ opts, pool));
/* Begin a new transaction, get its name (in the top pool), close it. */
subpool = svn_pool_create(pool);
@@ -390,7 +390,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_fs(&fs, "test-repo-txn-names-are-not-reused",
- opts->fs_type, pool));
+ opts, pool));
subpool = svn_pool_create(pool);
@@ -431,7 +431,7 @@
wstring = svn_stringbuf_create("Wicki wild, wicki wicki wild.", pool);
SVN_ERR(svn_test__create_fs(&fs, "test-repo-read-and-write-file",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -473,7 +473,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_fs(&fs, "test-repo-create-mini-tree-txn",
- opts->fs_type, pool));
+ opts, pool));
/* Begin a new transaction that is based on revision 0. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
@@ -512,7 +512,7 @@
/* Prepare a txn to receive the greek tree. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-create-greek-tree-txn",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -577,7 +577,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_fs(&fs, "test-repo-list-dir",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -659,7 +659,7 @@
/* Open the fs */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-rev-props",
- opts->fs_type, pool));
+ opts, pool));
/* Set some properties on the revision. */
for (i = 0; i < 4; i++)
@@ -762,7 +762,7 @@
/* Open the fs */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-txn-props",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
/* Set some properties on the revision. */
@@ -915,7 +915,7 @@
/* Open the fs and transaction */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-props",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -1071,7 +1071,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_fs(&fs, "test-repo-youngest-rev",
- opts->fs_type, pool));
+ opts, pool));
/* Get youngest revision of brand spankin' new filesystem. */
SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
@@ -1122,7 +1122,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-basic-commit",
- opts->fs_type, pool));
+ opts, pool));
/* Save the current youngest revision. */
SVN_ERR(svn_fs_youngest_rev(&before_rev, fs, pool));
@@ -1181,7 +1181,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-validate-tree-entries",
- opts->fs_type, pool));
+ opts, pool));
/* In a txn, create the greek tree. */
subpool = svn_pool_create(pool);
@@ -1314,7 +1314,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-merging-commit",
- opts->fs_type, pool));
+ opts, pool));
/* Initialize our revision number stuffs. */
for (i = 0;
@@ -2095,7 +2095,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-copy-test",
- opts->fs_type, pool));
+ opts, pool));
/* In first txn, create and commit the greek tree. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
@@ -2398,7 +2398,7 @@
/* Prepare a txn to receive the greek tree. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-del-from-dir",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -2554,7 +2554,7 @@
/* Prepare a txn to receive the greek tree. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-del-tree",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -2910,7 +2910,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-commit-date",
- opts->fs_type, pool));
+ opts, pool));
before_commit = apr_time_now();
@@ -2966,7 +2966,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-old-revisions",
- opts->fs_type, pool));
+ opts, pool));
/* Commit a greek tree. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
@@ -3332,7 +3332,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-all-revisions",
- opts->fs_type, pool));
+ opts, pool));
/***********************************************************************/
/* REVISION 0 */
@@ -3650,7 +3650,7 @@
static svn_error_t *
file_integrity_helper(apr_size_t filesize, apr_uint32_t *seed,
- const char *fs_type, const char *fs_name,
+ svn_test_opts_t *opts, const char *fs_name,
apr_pool_t *pool)
{
svn_fs_t *fs;
@@ -3667,7 +3667,7 @@
svn_revnum_t j;
/* Create a filesystem and repository. */
- SVN_ERR(svn_test__create_fs(&fs, fs_name, fs_type, pool));
+ SVN_ERR(svn_test__create_fs(&fs, fs_name, opts, pool));
/* Set up our file contents string buffer. */
content_buffer = apr_palloc(pool, filesize);
@@ -3799,7 +3799,7 @@
/* Being no larger than the standard delta window size affects
deltification internally, so test that. */
- return file_integrity_helper(SVN_DELTA_WINDOW_SIZE, &seed, opts->fs_type,
+ return file_integrity_helper(SVN_DELTA_WINDOW_SIZE, &seed, opts,
"test-repo-medium-file-integrity", pool);
}
@@ -3820,7 +3820,7 @@
/* Being larger than the standard delta window size affects
deltification internally, so test that. */
- return file_integrity_helper(SVN_DELTA_WINDOW_SIZE + 1, &seed, opts->fs_type,
+ return file_integrity_helper(SVN_DELTA_WINDOW_SIZE + 1, &seed, opts,
"test-repo-large-file-integrity", pool);
}
@@ -3845,7 +3845,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-root-revision",
- opts->fs_type, pool));
+ opts, pool));
/* Create and commit the greek tree. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
@@ -3971,7 +3971,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-created-rev",
- opts->fs_type, pool));
+ opts, pool));
/* Created the greek tree in revision 1. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
@@ -4086,7 +4086,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-check-related",
- opts->fs_type, pool));
+ opts, pool));
/*** Step I: Build up some state in our repository through a series
of commits */
@@ -4296,7 +4296,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-branch-test",
- opts->fs_type, pool));
+ opts, pool));
/*** Revision 1: Create the greek tree in revision. ***/
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
@@ -4382,7 +4382,7 @@
apr_md5(expected_digest, str->data, str->len);
SVN_ERR(svn_test__create_fs(&fs, "test-repo-verify-checksum",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
SVN_ERR(svn_fs_make_file(txn_root, "fact", pool));
@@ -4474,7 +4474,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-closest-copy",
- opts->fs_type, pool));
+ opts, pool));
/* In first txn, create and commit the greek tree. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, spool));
@@ -4575,7 +4575,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-root-revisions",
- opts->fs_type, pool));
+ opts, pool));
/* In first txn, create and commit the greek tree. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, spool));
@@ -4651,7 +4651,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-unordered-txn-dirprops",
- opts->fs_type, pool));
+ opts, pool));
/* Create and commit the greek tree. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
@@ -4727,7 +4727,7 @@
/* Prepare a filesystem. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-set-uuid",
- opts->fs_type, pool));
+ opts, pool));
/* Set the repository UUID to something fixed. */
SVN_ERR(svn_fs_set_uuid(fs, fixed_uuid, pool));
@@ -4778,7 +4778,7 @@
/* Create the repository. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-origin-rev",
- opts->fs_type, pool));
+ opts, pool));
/* Revision 1: Create the Greek tree. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
diff --git a/subversion/tests/libsvn_fs/locks-test.c b/subversion/tests/libsvn_fs/locks-test.c
index 5965224..7e0007c 100644
--- a/subversion/tests/libsvn_fs/locks-test.c
+++ b/subversion/tests/libsvn_fs/locks-test.c
@@ -112,7 +112,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-lock-only",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -158,7 +158,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-lookup-lock-by-path",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -208,7 +208,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-attach-lock",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -267,7 +267,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-get-locks",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -402,7 +402,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-basic-lock",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -460,7 +460,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-lock-credentials",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -557,7 +557,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-final-lock-check",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -621,7 +621,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-lock-dir-propchange",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -678,7 +678,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-lock-name-reservation",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -737,7 +737,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-directory-locks-kinda",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -815,7 +815,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-lock-expiration",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -906,7 +906,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-steal-refresh",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -993,7 +993,7 @@
/* Prepare a filesystem and a new txn. */
SVN_ERR(svn_test__create_fs(&fs, "test-repo-lock-out-of-date",
- opts->fs_type, pool));
+ opts, pool));
SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
diff --git a/subversion/tests/libsvn_fs_base/changes-test.c b/subversion/tests/libsvn_fs_base/changes-test.c
index 5d34ddf..e77c437 100644
--- a/subversion/tests/libsvn_fs_base/changes-test.c
+++ b/subversion/tests/libsvn_fs_base/changes-test.c
@@ -173,8 +173,8 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-changes-add",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-add",
+ pool));
/* Add the standard slew of changes. */
SVN_ERR(add_standard_changes(fs, pool));
@@ -201,8 +201,8 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-changes-fetch",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-fetch",
+ pool));
/* First, verify that we can request changes for an arbitrary key
without error. */
@@ -309,8 +309,8 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-changes-delete",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-delete",
+ pool));
/* Add the standard slew of changes. */
SVN_ERR(add_standard_changes(fs, pool));
@@ -505,8 +505,8 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-changes-fetch",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-fetch",
+ pool));
/* First, verify that we can request changes for an arbitrary key
without error. */
@@ -575,9 +575,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-changes-fetch-ordering",
- "bdb", pool));
+ pool));
/*** REVISION 1: Make some files and dirs. ***/
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
diff --git a/subversion/tests/libsvn_fs_base/fs-base-test.c b/subversion/tests/libsvn_fs_base/fs-base-test.c
index 5a362a0..340466f 100644
--- a/subversion/tests/libsvn_fs_base/fs-base-test.c
+++ b/subversion/tests/libsvn_fs_base/fs-base-test.c
@@ -58,8 +58,8 @@
return SVN_NO_ERROR;
/* Create and close a repository. */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-create-berkeley",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-create-berkeley",
+ pool));
return SVN_NO_ERROR;
}
@@ -88,8 +88,8 @@
return SVN_NO_ERROR;
/* Create and close a repository (using fs). */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-open-berkeley",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-open-berkeley",
+ pool));
/* Create a different fs object, and use it to re-open the
repository again. */
@@ -278,8 +278,8 @@
return SVN_NO_ERROR;
/* Prepare two txns to receive the Greek tree. */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-abort-txn",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-abort-txn",
+ pool));
SVN_ERR(svn_fs_begin_txn(&txn1, fs, 0, pool));
SVN_ERR(svn_fs_begin_txn(&txn2, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn1_root, txn1, pool));
@@ -517,8 +517,8 @@
return SVN_NO_ERROR;
/* Prepare a txn to receive the greek tree. */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-del-from-dir",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-del-from-dir",
+ pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -713,8 +713,8 @@
*/
/* Prepare a txn to receive the greek tree. */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-del-tree",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-del-tree",
+ pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
@@ -1200,8 +1200,8 @@
return SVN_NO_ERROR;
/* Create a filesystem and repository. */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-create-within-copy",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-create-within-copy",
+ pool));
/*** Revision 1: Create the greek tree in revision. ***/
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
@@ -1329,8 +1329,8 @@
return SVN_NO_ERROR;
/* Create a filesystem and repository. */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-skip-deltas",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-skip-deltas",
+ pool));
/* Create the file. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
@@ -1408,8 +1408,8 @@
return SVN_NO_ERROR;
/* Create a filesystem and repository. */
- SVN_ERR(svn_test__create_fs(&fs, "test-repo-redundant-copy",
- "bdb", pool));
+ SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-redundant-copy",
+ pool));
/* Create the greek tree in revision 1. */
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool));
diff --git a/subversion/tests/libsvn_fs_base/strings-reps-test.c b/subversion/tests/libsvn_fs_base/strings-reps-test.c
index 7dc521a..cedd22c 100644
--- a/subversion/tests/libsvn_fs_base/strings-reps-test.c
+++ b/subversion/tests/libsvn_fs_base/strings-reps-test.c
@@ -103,9 +103,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-write-new-rep",
- "bdb", pool));
+ pool));
/* Set up transaction baton */
args.fs = fs;
@@ -142,9 +142,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-write-rep",
- "bdb", pool));
+ pool));
/* Set up transaction baton */
new_args.fs = fs;
@@ -220,9 +220,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-read-rep",
- "bdb", pool));
+ pool));
/* Set up transaction baton */
new_args.fs = fs;
@@ -305,9 +305,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-delete-rep",
- "bdb", pool));
+ pool));
/* Set up transaction baton */
new_args.fs = fs;
@@ -540,9 +540,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-test-strings",
- "bdb", pool));
+ pool));
/* The plan (after each step below, verify the size and contents of
the string):
@@ -645,9 +645,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-test-strings",
- "bdb", pool));
+ pool));
args.fs = fs;
args.key = NULL;
@@ -675,9 +675,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-abort-string",
- "bdb", pool));
+ pool));
/* The plan:
@@ -743,9 +743,9 @@
return SVN_NO_ERROR;
/* Create a new fs and repos */
- SVN_ERR(svn_test__create_fs
+ SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-copy-string",
- "bdb", pool));
+ pool));
/* Write a new string (string1). */
args.fs = fs;
diff --git a/subversion/tests/libsvn_ra_local/ra-local-test.c b/subversion/tests/libsvn_ra_local/ra-local-test.c
index 5714582..ecff146 100644
--- a/subversion/tests/libsvn_ra_local/ra-local-test.c
+++ b/subversion/tests/libsvn_ra_local/ra-local-test.c
@@ -85,7 +85,7 @@
static svn_error_t *
make_and_open_local_repos(svn_ra_session_t **session,
const char *repos_name,
- const char *fs_type,
+ svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_repos_t *repos;
@@ -94,7 +94,7 @@
SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
- SVN_ERR(svn_test__create_repos(&repos, repos_name, fs_type, pool));
+ SVN_ERR(svn_test__create_repos(&repos, repos_name, opts, pool));
SVN_ERR(svn_ra_initialize(pool));
SVN_ERR(current_directory_url(&url, repos_name, pool));
@@ -130,7 +130,7 @@
return SVN_NO_ERROR;
SVN_ERR(make_and_open_local_repos(&session,
- "test-repo-open", opts->fs_type, pool));
+ "test-repo-open", opts, pool));
return SVN_NO_ERROR;
}
@@ -152,7 +152,7 @@
return SVN_NO_ERROR;
SVN_ERR(make_and_open_local_repos(&session,
- "test-repo-getrev", opts->fs_type,
+ "test-repo-getrev", opts,
pool));
/* Get the youngest revision and make sure it's 0. */
@@ -294,14 +294,14 @@
static svn_error_t *
check_split_url(const char *repos_path,
const char *in_repos_path,
- const char *fs_type,
+ svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_repos_t *repos;
const char *url, *root_url, *repos_part, *in_repos_part;
/* Create a filesystem and repository */
- SVN_ERR(svn_test__create_repos(&repos, repos_path, fs_type, pool));
+ SVN_ERR(svn_test__create_repos(&repos, repos_path, opts, pool));
SVN_ERR(current_directory_url(&root_url, repos_path, pool));
if (in_repos_path)
@@ -347,15 +347,15 @@
in-repository path begins. */
SVN_ERR(check_split_url("test-repo-split-fs1",
"/trunk/foobar/quux.c",
- opts->fs_type,
+ opts,
pool));
SVN_ERR(check_split_url("test-repo-split-fs2",
"/alpha/beta/gamma/delta/epsilon/zeta/eta/theta",
- opts->fs_type,
+ opts,
pool));
SVN_ERR(check_split_url("test-repo-split-fs3",
NULL,
- opts->fs_type,
+ opts,
pool));
return SVN_NO_ERROR;
diff --git a/subversion/tests/libsvn_repos/repos-test.c b/subversion/tests/libsvn_repos/repos-test.c
index 6bbd7e3..d715959 100644
--- a/subversion/tests/libsvn_repos/repos-test.c
+++ b/subversion/tests/libsvn_repos/repos-test.c
@@ -73,7 +73,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-dir-deltas",
- opts->fs_type, pool));
+ opts, pool));
fs = svn_repos_fs(repos);
expected_trees[revision_count].num_entries = 0;
expected_trees[revision_count++].entries = 0;
@@ -380,7 +380,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-del-under-copy",
- opts->fs_type, pool));
+ opts, pool));
fs = svn_repos_fs(repos);
/* Prepare a txn to receive the greek tree. */
@@ -520,7 +520,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-revisions-changed",
- opts->fs_type, pool));
+ opts, pool));
fs = svn_repos_fs(repos);
/*** Testing Algorithm ***
@@ -779,7 +779,7 @@
/* Create the repository with a Greek tree. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-node-locations",
- opts->fs_type, pool));
+ opts, pool));
fs = svn_repos_fs(repos);
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
@@ -832,7 +832,7 @@
/* Create the repository. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-node-locations2",
- opts->fs_type, pool));
+ opts, pool));
fs = svn_repos_fs(repos);
/* Revision 1: Add a directory /foo */
@@ -1045,7 +1045,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-rmlocks",
- opts->fs_type, pool));
+ opts, pool));
fs = svn_repos_fs(repos);
/* Prepare a txn to receive the greek tree. */
@@ -1419,7 +1419,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-commit-authz",
- opts->fs_type, subpool));
+ opts, subpool));
fs = svn_repos_fs(repos);
/* Prepare a txn to receive the greek tree. */
@@ -1637,7 +1637,7 @@
/* Create a filesystem and repository. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-commit-continue",
- opts->fs_type, subpool));
+ opts, subpool));
fs = svn_repos_fs(repos);
/* Prepare a txn to receive the greek tree. */
@@ -1818,7 +1818,7 @@
/* Create the repository. */
SVN_ERR(svn_test__create_repos(&repos, "test-repo-node-location-segments",
- opts->fs_type, pool));
+ opts, pool));
fs = svn_repos_fs(repos);
/* Revision 1: Create the Greek tree. */
@@ -2011,7 +2011,7 @@
return SVN_NO_ERROR;
SVN_ERR(svn_test__create_repos(&repos, "test-repo-reporter-depth-exclude",
- opts->fs_type, pool));
+ opts, pool));
fs = svn_repos_fs(repos);
/* Prepare a txn to receive the greek tree. */
diff --git a/subversion/tests/libsvn_subr/cache-test.c b/subversion/tests/libsvn_subr/cache-test.c
new file mode 100644
index 0000000..4828ace
--- /dev/null
+++ b/subversion/tests/libsvn_subr/cache-test.c
@@ -0,0 +1,274 @@
+/*
+ * cache-test.c -- test the in-memory cache
+ *
+ * ====================================================================
+ * Copyright (c) 2006 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <apr_general.h>
+#include <apr_lib.h>
+#include <apr_time.h>
+
+#include "svn_cache.h"
+#include "svn_pools.h"
+
+#include "svn_private_config.h"
+
+#include "../svn_test.h"
+
+static svn_cache_dup_func_t dup_revnum;
+static svn_error_t *
+dup_revnum(void **out,
+ void *in,
+ apr_pool_t *pool)
+{
+ svn_revnum_t *in_rn = in, *duped = apr_palloc(pool, sizeof(*duped));
+
+ *duped = *in_rn;
+
+ *out = duped;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_cache_serialize_func_t serialize_revnum;
+static svn_error_t *
+serialize_revnum(char **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool)
+{
+ *data_len = sizeof(svn_revnum_t);
+ *data = apr_pmemdup(pool, in, *data_len);
+
+ return SVN_NO_ERROR;
+}
+
+
+static svn_cache_deserialize_func_t deserialize_revnum;
+static svn_error_t *
+deserialize_revnum(void **out,
+ const char *data,
+ apr_size_t data_len,
+ apr_pool_t *pool)
+{
+ svn_revnum_t *in_rev, *out_rev;
+ if (data_len != sizeof(*in_rev))
+ return svn_error_create(SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
+ _("Bad size for revision number in cache"));
+ in_rev = (svn_revnum_t *) data;
+ out_rev = apr_palloc(pool, sizeof(*out_rev));
+ *out_rev = *in_rev;
+ *out = out_rev;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+basic_cache_test(svn_cache_t *cache,
+ svn_boolean_t size_is_one,
+ apr_pool_t *pool)
+{
+ svn_boolean_t found;
+ svn_revnum_t twenty = 20, thirty = 30, *answer;
+ apr_pool_t *subpool;
+
+ /* We use a subpool for all calls in this test and aggressively
+ * clear it, to try to find any bugs where the cached values aren't
+ * actually saved away in the cache's pools. */
+ subpool = svn_pool_create(pool);
+
+ SVN_ERR(svn_cache_get((void **) &answer, &found, cache, "twenty", subpool));
+ if (found)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "cache found an entry that wasn't there");
+ svn_pool_clear(subpool);
+
+ SVN_ERR(svn_cache_set(cache, "twenty", &twenty, subpool));
+ svn_pool_clear(subpool);
+
+ SVN_ERR(svn_cache_get((void **) &answer, &found, cache, "twenty", subpool));
+ if (! found)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "cache failed to find entry for 'twenty'");
+ if (*answer != 20)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+ "expected 20 but found '%ld'", *answer);
+ svn_pool_clear(subpool);
+
+ SVN_ERR(svn_cache_set(cache, "thirty", &thirty, subpool));
+ svn_pool_clear(subpool);
+
+ SVN_ERR(svn_cache_get((void **) &answer, &found, cache, "thirty", subpool));
+ if (! found)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "cache failed to find entry for 'thirty'");
+ if (*answer != 30)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+ "expected 30 but found '%ld'", *answer);
+
+ if (size_is_one)
+ {
+ SVN_ERR(svn_cache_get((void **) &answer, &found, cache, "twenty", subpool));
+ if (found)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "cache found entry for 'twenty' that should have "
+ "expired");
+ }
+ svn_pool_destroy(subpool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_inprocess_cache_basic(const char **msg,
+ svn_boolean_t msg_only,
+ svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_cache_t *cache;
+
+ *msg = "basic inprocess svn_cache test";
+
+ if (msg_only)
+ return SVN_NO_ERROR;
+
+ /* Create a cache with just one entry. */
+ SVN_ERR(svn_cache_create_inprocess(&cache,
+ dup_revnum,
+ APR_HASH_KEY_STRING,
+ 1,
+ 1,
+ TRUE,
+ pool));
+
+ return basic_cache_test(cache, TRUE, pool);
+}
+
+static svn_error_t *
+test_memcache_basic(const char **msg,
+ svn_boolean_t msg_only,
+ svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_cache_t *cache;
+ svn_config_t *config;
+ svn_memcache_t *memcache = NULL;
+ const char *prefix = apr_psprintf(pool,
+ "test_memcache_basic-%" APR_TIME_T_FMT,
+ apr_time_now());
+
+ *msg = "basic memcache svn_cache test";
+
+ if (msg_only)
+ return SVN_NO_ERROR;
+
+ if (opts->config_file)
+ {
+ SVN_ERR(svn_config_read(&config, opts->config_file, TRUE, pool));
+ SVN_ERR(svn_cache_make_memcache_from_config(&memcache, config, pool));
+ }
+
+ if (! memcache)
+ return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+ "not configured to use memcached");
+
+
+ /* Create a memcache-based cache. */
+ SVN_ERR(svn_cache_create_memcache(&cache,
+ memcache,
+ serialize_revnum,
+ deserialize_revnum,
+ APR_HASH_KEY_STRING,
+ prefix,
+ pool));
+
+ return basic_cache_test(cache, FALSE, pool);
+}
+
+
+
+static svn_error_t *
+test_memcache_long_key(const char **msg,
+ svn_boolean_t msg_only,
+ svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_cache_t *cache;
+ svn_config_t *config;
+ svn_memcache_t *memcache = NULL;
+ svn_revnum_t fifty = 50, *answer;
+ svn_boolean_t found = FALSE;
+ const char *prefix = apr_psprintf(pool,
+ "test_memcache_long_key-%" APR_TIME_T_FMT,
+ apr_time_now());
+ static const char *long_key =
+ "0123456789" "0123456789" "0123456789" "0123456789" "0123456789" /* 50 */
+ "0123456789" "0123456789" "0123456789" "0123456789" "0123456789" /* 100 */
+ "0123456789" "0123456789" "0123456789" "0123456789" "0123456789" /* 150 */
+ "0123456789" "0123456789" "0123456789" "0123456789" "0123456789" /* 200 */
+ "0123456789" "0123456789" "0123456789" "0123456789" "0123456789" /* 250 */
+ "0123456789" "0123456789" "0123456789" "0123456789" "0123456789" /* 300 */
+ ;
+
+ *msg = "memcache svn_cache with very long keys";
+
+ if (msg_only)
+ return SVN_NO_ERROR;
+
+ if (opts->config_file)
+ {
+ SVN_ERR(svn_config_read(&config, opts->config_file, TRUE, pool));
+ SVN_ERR(svn_cache_make_memcache_from_config(&memcache, config, pool));
+ }
+
+ if (! memcache)
+ return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+ "not configured to use memcached");
+
+
+ /* Create a memcache-based cache. */
+ SVN_ERR(svn_cache_create_memcache(&cache,
+ memcache,
+ serialize_revnum,
+ deserialize_revnum,
+ APR_HASH_KEY_STRING,
+ prefix,
+ pool));
+
+ SVN_ERR(svn_cache_set(cache, long_key, &fifty, pool));
+ SVN_ERR(svn_cache_get((void **) &answer, &found, cache, long_key, pool));
+
+ if (! found)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "cache failed to find entry for 'fifty'");
+ if (*answer != 50)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+ "expected 50 but found '%ld'", *answer);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* The test table. */
+
+struct svn_test_descriptor_t test_funcs[] =
+ {
+ SVN_TEST_NULL,
+ SVN_TEST_PASS(test_inprocess_cache_basic),
+ SVN_TEST_PASS(test_memcache_basic),
+ SVN_TEST_PASS(test_memcache_long_key),
+ SVN_TEST_NULL
+ };
diff --git a/subversion/tests/svn_test.h b/subversion/tests/svn_test.h
index 2860e80..a7756bc 100644
--- a/subversion/tests/svn_test.h
+++ b/subversion/tests/svn_test.h
@@ -37,6 +37,8 @@
{
/* Description of the fs backend that should be used for testing. */
const char *fs_type;
+ /* Config file. */
+ const char *config_file;
/* Add future "arguments" here. */
} svn_test_opts_t;
diff --git a/subversion/tests/svn_test_fs.c b/subversion/tests/svn_test_fs.c
index c2daad3..3b29017 100644
--- a/subversion/tests/svn_test_fs.c
+++ b/subversion/tests/svn_test_fs.c
@@ -73,11 +73,11 @@
}
-svn_error_t *
-svn_test__create_fs(svn_fs_t **fs_p,
- const char *name,
- const char *fs_type,
- apr_pool_t *pool)
+static svn_error_t *
+create_fs(svn_fs_t **fs_p,
+ const char *name,
+ const char *fs_type,
+ apr_pool_t *pool)
{
apr_finfo_t finfo;
apr_hash_t *fs_config = make_fs_config(fs_type, pool);
@@ -110,15 +110,63 @@
return SVN_NO_ERROR;
}
+static svn_error_t *
+maybe_install_fsfs_conf(svn_fs_t *fs,
+ svn_test_opts_t *opts,
+ svn_boolean_t *must_reopen,
+ apr_pool_t *pool)
+{
+ *must_reopen = FALSE;
+ if (strcmp(opts->fs_type, "fsfs") != 0 || ! opts->config_file)
+ return SVN_NO_ERROR;
+
+ *must_reopen = TRUE;
+ return svn_io_copy_file(opts->config_file,
+ svn_path_join(svn_fs_path(fs, pool), "fsfs.conf", pool),
+ FALSE,
+ pool);
+}
+
+
+svn_error_t *
+svn_test__create_bdb_fs(svn_fs_t **fs_p,
+ const char *name,
+ apr_pool_t *pool)
+{
+ return create_fs(fs_p, name, "bdb", pool);
+}
+
+svn_error_t *
+svn_test__create_fs(svn_fs_t **fs_p,
+ const char *name,
+ svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_boolean_t must_reopen;
+
+ SVN_ERR(create_fs(fs_p, name, opts->fs_type, pool));
+
+ SVN_ERR(maybe_install_fsfs_conf(*fs_p, opts, &must_reopen, pool));
+ if (must_reopen)
+ {
+ SVN_ERR(svn_fs_open(fs_p, name, NULL, pool));
+ svn_fs_set_warning_func(*fs_p, fs_warning_handler, NULL);
+ }
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_test__create_repos(svn_repos_t **repos_p,
const char *name,
- const char *fs_type,
+ svn_test_opts_t *opts,
apr_pool_t *pool)
{
apr_finfo_t finfo;
- apr_hash_t *fs_config = make_fs_config(fs_type, pool);
+ svn_repos_t *repos;
+ svn_boolean_t must_reopen;
+ apr_hash_t *fs_config = make_fs_config(opts->fs_type, pool);
/* If there's already a repository named NAME, delete it. Doing
things this way means that repositories stick around after a
@@ -134,12 +182,21 @@
"there is already a file named '%s'", name);
}
- SVN_ERR(svn_repos_create(repos_p, name, NULL, NULL, NULL,
+ SVN_ERR(svn_repos_create(&repos, name, NULL, NULL, NULL,
fs_config, pool));
/* Register this repo for cleanup. */
svn_test_add_dir_cleanup(name);
+ SVN_ERR(maybe_install_fsfs_conf(svn_repos_fs(repos), opts, &must_reopen,
+ pool));
+ if (must_reopen)
+ {
+ SVN_ERR(svn_repos_open(&repos, name, pool));
+ svn_fs_set_warning_func(svn_repos_fs(repos), fs_warning_handler, NULL);
+ }
+
+ *repos_p = repos;
return SVN_NO_ERROR;
}
diff --git a/subversion/tests/svn_test_fs.h b/subversion/tests/svn_test_fs.h
index c50a482..11198c6 100644
--- a/subversion/tests/svn_test_fs.h
+++ b/subversion/tests/svn_test_fs.h
@@ -41,23 +41,29 @@
svn_test__fs_new(svn_fs_t **fs_p, apr_pool_t *pool);
-/* Create a filesystem of FS_TYPE in a subdir NAME and return a new FS
- object which points to it. FS_TYPE should be either "bdb" or
- "fsfs". Filesystem tests that are backend-specific should use
- svn_test__create_fs instead of this. */
+/* Creates a filesystem which is always of type "bdb" in a subdir NAME
+ and return a new FS object which points to it. */
+svn_error_t *
+svn_test__create_bdb_fs(svn_fs_t **fs_p,
+ const char *name,
+ apr_pool_t *pool);
+
+
+/* Create a filesystem based on OPTS in a subdir NAME and return a new
+ FS object which points to it. */
svn_error_t *
svn_test__create_fs(svn_fs_t **fs_p,
const char *name,
- const char *fs_type,
+ svn_test_opts_t *opts,
apr_pool_t *pool);
-/* Create a repository with a filesystem of FS_TYPE in a subdir NAME
+/* Create a repository with a filesystem based on OPTS in a subdir NAME
and return a new REPOS object which points to it. */
svn_error_t *
svn_test__create_repos(svn_repos_t **repos_p,
const char *name,
- const char *fs_type,
+ svn_test_opts_t *opts,
apr_pool_t *pool);
diff --git a/subversion/tests/svn_test_main.c b/subversion/tests/svn_test_main.c
index 943f857..7b5020c 100644
--- a/subversion/tests/svn_test_main.c
+++ b/subversion/tests/svn_test_main.c
@@ -57,13 +57,16 @@
fstype_opt,
list_opt,
verbose_opt,
- quiet_opt
+ quiet_opt,
+ config_opt
};
static const apr_getopt_option_t cl_options[] =
{
{"cleanup", cleanup_opt, 0,
N_("remove test directories after success")},
+ {"config-file", config_opt, 1,
+ N_("specify test config file ARG")},
{"fs-type", fstype_opt, 1,
N_("specify a filesystem backend type ARG")},
{"list", list_opt, 0,
@@ -185,6 +188,13 @@
/* Do test */
err = func(&msg, msg_only || skip, opts, pool);
+ if (err && err->apr_err == SVN_ERR_TEST_SKIPPED)
+ {
+ svn_error_clear(err);
+ err = SVN_NO_ERROR;
+ skip = TRUE;
+ }
+
/* Failure means unexpected results -- FAIL or XPASS. */
test_failed = ((err != SVN_NO_ERROR) != (xfail != 0));
@@ -306,6 +316,9 @@
case cleanup_opt:
cleanup_mode = 1;
break;
+ case config_opt:
+ opts.config_file = apr_pstrdup(pool, opt_arg);
+ break;
case fstype_opt:
opts.fs_type = apr_pstrdup(pool, opt_arg);
break;
diff --git a/subversion/tests/tests.conf b/subversion/tests/tests.conf
new file mode 100644
index 0000000..1b3d643
--- /dev/null
+++ b/subversion/tests/tests.conf
@@ -0,0 +1,19 @@
+### This config file configures some aspects of the Subversion test
+### suite. Pass --config-file FILENAME to test programs if running
+### them manually; "make check" passes this file in automatically.
+
+### Currently, it is used for two purposes: it is used to configure
+### memcached for direct svn_cache/memcached tests in
+### libsvn_subr/cache-test; and it is copied into new FSFS
+### repositories as fsfs.conf (to configure their use of memcached as
+### well).
+
+[memcached-servers]
+### Run memcached servers and enter lines like the following (the key
+### is ignored):
+# key = 127.0.0.1:11211
+
+[caches]
+### In the test suite, we should make FSFS cache failures into actual
+### test failures:
+fail-stop = true