Author: stefan2
Date: Thu Aug 21 14:35:05 2014
New Revision: 1619413

URL: http://svn.apache.org/r1619413
Log:
Enforce the "everybody or nobody" restriction on revprop caching, i.e.
either all processes (usually servers) or non must use revprop caching.

This patch makes sure that the first process to use revprop caching will
create the revprop generation file before even reading and caching any
revprops - instead of auto-creating it upon the first write.  Then this
file is being used as an indicator that the repo has been accessed at
least once using revprop caching.  FS instances without that feature
enabled will then be banned from writing any revprops.

To make this workable, applications that may write revprops but are not
servers themselves (the latter having explicit revprop caching options)
need to engage the revprop caching infrastructure automatically if the
repo that they are accessing requires it.  svnadmin, svnlook and ra_local
enable this new mode.

* subversion/include/svn_fs.h
  (SVN_FS_CONFIG_FSFS_CACHE_REVPROPS): Document that there is now yet
                                       another mode.

* subversion/libsvn_fs_fs/fs.h
  (fs_fs_data_t): Add the flag allowing on-demand activation of revprop
                  caching.

* subversion/libsvn_fs_fs/fs_fs.h
  (svn_fs_fs__initialize_revprop_caches): Declare new function to init
                                          the revprop caches only.

* subversion/libsvn_fs_fs/caching.c
  (read_config): Handle the new revprop caching mode.
  (cache_key_prefix): Common functionality factored out from 
                      svn_fs_fs__initialize_caches.
  (svn_fs_fs__initialize_revprop_caches): Implement, mainly taking out
                                          of ...
  (svn_fs_fs__initialize_caches): ... this.  Update after refactoring.

* subversion/libsvn_fs_fs/revprops.c
  (init_generation_baton_t,
   init_revprop_generation_file): New code to create the revprop generation
                                  file before the first cached revprop
                                  access (read or write).
  (read_revprop_generation_file): Trigger auto-creation of that file.
  (enforce_consistent_caching): New function containing the actual
                                cache settings consistency check.
  (svn_fs_fs__set_revision_proplist): Trigger the new check to make sure
                                      we don't modify revprops w/o telling
                                      caching applications.

* subversion/libsvn_ra_local/split_url.c
  (svn_ra_local__split_URL): Set "enable revprop caching on demand" feature
                             instead of "no revprop caching" on open repos.

* subversion/svnadmin/svnadmin.c
  (open_repos): Use the same mode here.

* subversion/svnlook/svnlook.c
  (get_ctxt_baton): And here.

* subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
  (enforce_consistent_revprop_caching): New test covering the new mode as
                                        well as new the consistency check.
  (test_funcs): Register new test. 

Modified:
    subversion/trunk/subversion/include/svn_fs.h
    subversion/trunk/subversion/libsvn_fs_fs/caching.c
    subversion/trunk/subversion/libsvn_fs_fs/fs.h
    subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h
    subversion/trunk/subversion/libsvn_fs_fs/revprops.c
    subversion/trunk/subversion/libsvn_ra_local/split_url.c
    subversion/trunk/subversion/svnadmin/svnadmin.c
    subversion/trunk/subversion/svnlook/svnlook.c
    subversion/trunk/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c

Modified: subversion/trunk/subversion/include/svn_fs.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_fs.h?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_fs.h (original)
+++ subversion/trunk/subversion/include/svn_fs.h Thu Aug 21 14:35:05 2014
@@ -91,6 +91,9 @@ typedef struct svn_fs_t svn_fs_t;
  * i.e. this will not create warning at runtime if there
  * if no efficient support for revprop caching.
  *
+ * "3" is allowed as well and means "enable if required by the repository".
+ * (valid in 1.9+ only).
+ *
  * @since New in 1.8.
  */
 #define SVN_FS_CONFIG_FSFS_CACHE_REVPROPS       "fsfs-cache-revprops"

Modified: subversion/trunk/subversion/libsvn_fs_fs/caching.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/caching.c?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/caching.c (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/caching.c Thu Aug 21 14:35:05 2014
@@ -80,6 +80,8 @@ read_config(const char **cache_namespace
             svn_fs_t *fs,
             apr_pool_t *pool)
 {
+  const char *revprop_option;
+
   /* No cache namespace by default.  I.e. all FS instances share the
    * cached data.  If you specify different namespaces, the data will
    * share / compete for the same cache memory but keys will not match
@@ -126,16 +128,31 @@ read_config(const char **cache_namespace
    *
    * If the caller chose option "2", enable revprop caching if
    * the required API support is there to make it efficient.
+   *
+   * If the caller chose option "3", don't enable revprop caching
+   * right now but set a flag in FS that it should be enabled if
+   * the repo had been previously accessed using revprop caching.
    */
-  if (strcmp(svn_hash__get_cstring(fs->config,
-                                   SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
-                                   ""), "2"))
-    *cache_revprops
-      = svn_hash__get_bool(fs->config,
-                           SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
-                           FALSE);
+  revprop_option = svn_hash__get_cstring(fs->config,
+                                         SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
+                                         "");
+  if (0 == strcmp(revprop_option, "3"))
+    {
+      fs_fs_data_t *ffd = fs->fsap_data;
+      ffd->auto_enable_revprop_caching = TRUE;
+      *cache_revprops = FALSE;
+    }
+  else if (0 == strcmp(revprop_option, "2"))
+    {
+      *cache_revprops = svn_named_atomic__is_efficient();
+    }
   else
-    *cache_revprops = svn_named_atomic__is_efficient();
+    {
+      *cache_revprops
+        = svn_hash__get_bool(fs->config,
+                            SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
+                            FALSE);
+    }
 
   return SVN_NO_ERROR;
 }
@@ -354,17 +371,71 @@ create_cache(svn_cache__t **cache_p,
   return SVN_NO_ERROR;
 }
 
+/* Return the cache key prefix for FS, including CACHE_NAMESPACE.
+ * Allocate the result in RESULT_POOL. */
+static const char *
+cache_key_prefix(svn_fs_t *fs,
+                 const char *cache_namespace,
+                 apr_pool_t *result_pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  return apr_pstrcat(result_pool,
+                     "ns:", cache_namespace, ":",
+                     "fsfs:", fs->uuid,
+                     ":", ffd->instance_id,
+                     "/", normalize_key_part(fs->path, result_pool),
+                     ":",
+                     SVN_VA_NULL);
+}
+
+svn_error_t *
+svn_fs_fs__initialize_revprop_caches(svn_fs_t *fs,
+                                     apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  const char *prefix;
+  svn_boolean_t no_handler = ffd->fail_stop;
+  svn_boolean_t cache_txdeltas;
+  svn_boolean_t cache_fulltexts;
+  svn_boolean_t cache_revprops;
+  const char *cache_namespace;
+
+  /* Don't replace an existing cache instance. */
+  if (ffd->revprop_cache)
+    return SVN_NO_ERROR;
+
+  /* Evaluating the cache configuration. */
+  SVN_ERR(read_config(&cache_namespace,
+                      &cache_txdeltas,
+                      &cache_fulltexts,
+                      &cache_revprops,
+                      fs,
+                      pool));
+
+  /* Instantiate the cache unconditionally. */
+  prefix = cache_key_prefix(fs, cache_namespace, pool);
+  SVN_ERR(create_cache(&(ffd->revprop_cache),
+                       NULL,
+                       svn_cache__get_global_membuffer_cache(),
+                       0, 0, /* Do not use inprocess cache */
+                       svn_fs_fs__serialize_properties,
+                       svn_fs_fs__deserialize_properties,
+                       sizeof(pair_cache_key_t),
+                       apr_pstrcat(pool, prefix, "REVPROP", SVN_VA_NULL),
+                       SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
+                       fs,
+                       no_handler,
+                       fs->pool, pool));
+
+  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:", fs->uuid,
-                                   ":", ffd->instance_id,
-                                   "/", normalize_key_part(fs->path, pool),
-                                   ":",
-                                   SVN_VA_NULL);
+  const char *prefix;
   svn_membuffer_t *membuffer;
   svn_boolean_t no_handler = ffd->fail_stop;
   svn_boolean_t cache_txdeltas;
@@ -380,8 +451,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                       fs,
                       pool));
 
-  prefix = apr_pstrcat(pool, "ns:", cache_namespace, ":", prefix, SVN_VA_NULL);
-
+  prefix = cache_key_prefix(fs, cache_namespace, pool);
   membuffer = svn_cache__get_global_membuffer_cache();
 
   /* General rules for assigning cache priorities:
@@ -583,26 +653,9 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
     }
 
   /* if enabled, cache revprops */
+  ffd->revprop_cache = NULL;
   if (cache_revprops)
-    {
-      SVN_ERR(create_cache(&(ffd->revprop_cache),
-                           NULL,
-                           membuffer,
-                           0, 0, /* Do not use inprocess cache */
-                           svn_fs_fs__serialize_properties,
-                           svn_fs_fs__deserialize_properties,
-                           sizeof(pair_cache_key_t),
-                           apr_pstrcat(pool, prefix, "REVPROP",
-                                       SVN_VA_NULL),
-                           SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
-                           fs,
-                           no_handler,
-                           fs->pool, pool));
-    }
-  else
-    {
-      ffd->revprop_cache = NULL;
-    }
+    SVN_ERR(svn_fs_fs__initialize_revprop_caches(fs, pool));
 
   /* if enabled, cache text deltas and their combinations */
   if (cache_txdeltas)

Modified: subversion/trunk/subversion/libsvn_fs_fs/fs.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/fs.h?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/fs.h Thu Aug 21 14:35:05 2014
@@ -357,6 +357,11 @@ typedef struct fs_fs_data_t
      the first access. */
   svn_named_atomic__t *revprop_timeout;
 
+  /* If TRUE, delay instantiation of the REVPROP_CACHE until we find
+     the repo being / having been accessed with revprop caching by
+     other svn_fs_t instances. */
+  svn_boolean_t auto_enable_revprop_caching;
+
   /* Revision property cache.  Maps from (rev,generation) to apr_hash_t. */
   svn_cache__t *revprop_cache;
 

Modified: subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h Thu Aug 21 14:35:05 2014
@@ -240,6 +240,12 @@ svn_fs_fs__get_node_origin(const svn_fs_
 svn_error_t *
 svn_fs_fs__initialize_caches(svn_fs_t *fs, apr_pool_t *pool);
 
+/* Explicitly initialize the revprop cache in FS - independent of
+   global cache settings. Use POOL for temporary allocations.  If the
+   cache has already been initialized, we'll keep that instance. */
+svn_error_t *
+svn_fs_fs__initialize_revprop_caches(svn_fs_t *fs, apr_pool_t *pool);
+
 /* Initialize all transaction-local caches in FS according to the global
    cache settings and make TXN_ID part of their key space. Use POOL for
    allocations.

Modified: subversion/trunk/subversion/libsvn_fs_fs/revprops.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/revprops.c?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/revprops.c (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/revprops.c Thu Aug 21 14:35:05 2014
@@ -194,6 +194,59 @@ svn_fs_fs__upgrade_cleanup_pack_revprops
  * initialization phase, they will never operate on stale data.
  */
 
+/* Forward declaration. */
+static svn_error_t *
+read_revprop_generation_file(apr_int64_t *current,
+                             svn_fs_t *fs,
+                             apr_pool_t *pool);
+
+/* Baton to be used with init_revprop_generation_file. */
+typedef struct init_generation_baton_t
+{
+  /* FS for which to create the file / ensure its existence. */
+  svn_fs_t *fs;
+
+  /* File path to check and auto-create. */
+  const char *path;
+
+  /* Where to return the generation. */
+  apr_int64_t *generation;
+} init_generation_baton_t;
+
+/* "with-fs-lock" compatible function taking an init_generation_baton_t*
+ * in VOID_BATON.  It assumes that the write lock has been acquired and
+ * will then create the revprop generation file at the specified path.
+ * If that already exists, read it and return its contents.
+ */
+static svn_error_t *
+init_revprop_generation_file(void *void_baton,
+                             apr_pool_t *scratch_pool)
+{
+  init_generation_baton_t *baton = void_baton;
+  svn_node_kind_t kind;
+
+  SVN_ERR(svn_io_check_path(baton->path, &kind, scratch_pool));
+  if (kind == svn_node_none)
+    {
+      /* First generation is "2" as odd numbers are used as crash
+       * indicators and we reserve the use of "0" for "not set /
+       * unspecified" internally. */
+      *baton->generation = 2;
+      SVN_ERR(svn_fs_fs__write_revprop_generation_file(baton->fs,
+                                                       *baton->generation,
+                                                       scratch_pool));
+    }
+  else
+    {
+      /* Race condition.  By the time we finally got the FS write lock,
+       * somebody else already created the file.  Simply read it. */
+      SVN_ERR(read_revprop_generation_file(baton->generation, baton->fs,
+                                           scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Read revprop generation as stored on disk for repository FS. The result
  * is returned in *CURRENT. Default to 2 if no such file is available.
  */
@@ -202,6 +255,7 @@ read_revprop_generation_file(apr_int64_t
                              svn_fs_t *fs,
                              apr_pool_t *pool)
 {
+  fs_fs_data_t *ffd = fs->fsap_data;
   svn_error_t *err;
   apr_file_t *file;
   char buf[80];
@@ -213,8 +267,23 @@ read_revprop_generation_file(apr_int64_t
                          APR_OS_DEFAULT, pool);
   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
     {
+      /* The file does not exist.  Because we use it to indicate that
+       * revprop caching is / may be in effect on FS, we must create the
+       * file before actually caching any contents. */
+      init_generation_baton_t baton;
       svn_error_clear(err);
-      *current = 2;
+
+      baton.fs = fs;
+      baton.generation = current;
+      baton.path = path;
+
+      /* We must synchronize writing to / creating the file.
+       * We may or may not have the write lock acquired already. */
+      if (ffd->has_write_lock)
+        SVN_ERR(init_revprop_generation_file(&baton, pool));
+      else
+        SVN_ERR(svn_fs_fs__with_write_lock(fs, init_revprop_generation_file,
+                                           &baton, pool));
 
       return SVN_NO_ERROR;
     }
@@ -1416,6 +1485,50 @@ write_packed_revprop(const char **final_
   return SVN_NO_ERROR;
 }
 
+/* Make sure revprops in FS are being written to with consistent revprop
+ * caching settings.  Once we read or wrote revprops using revprop cache
+ * infra, all writers must use it.  Otherwise, caches may become stale.
+ */
+static svn_error_t *
+enforce_consistent_caching(svn_fs_t *fs,
+                           apr_pool_t *scratch_pool)
+{
+  /* Reading or writing a revprop with revprop caching enabled will auto-
+   * create the generation file.  Since writes are serialized via repo
+   * write lock and we are currently holding it, this file presence check
+   * is not racy with the automatic generation file creation.
+   */
+  if (! has_revprop_cache(fs, scratch_pool))
+    {
+      fs_fs_data_t *ffd = fs->fsap_data;
+
+      /* We won't use revprop caching.  That is only safe if nobody else
+       * does.  Effectively however, we can only test whether somebody did
+       * at some point in the past and leave it to the administrator to
+       * remove the flag file manually if they need to.
+       */
+      const char *path = svn_fs_fs__path_revprop_generation(fs, scratch_pool);
+      svn_node_kind_t kind;
+      SVN_ERR(svn_io_check_path(path, &kind, scratch_pool));
+
+      if (kind != svn_node_none)
+        {
+          /* Are we supposed to enable revprop caching on demand?
+           * If so, we have to do it now (or won't be allowed to proceed). */
+          if (ffd->auto_enable_revprop_caching)
+            SVN_ERR(svn_fs_fs__initialize_revprop_caches(fs, scratch_pool));
+
+          /* Error out if revprop caching infrastructure is not available. */
+          if (! has_revprop_cache(fs, scratch_pool))
+            return svn_error_create(SVN_ERR_FS_REVPROP_CACHE_INIT_FAILURE, 
+                                    NULL, _("Writing revprops requires "
+                                            "revprop caching to be enabled."));
+        }
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Set the revision property list of revision REV in filesystem FS to
    PROPLIST.  Use POOL for temporary allocations. */
 svn_error_t *
@@ -1436,6 +1549,10 @@ svn_fs_fs__set_revision_proplist(svn_fs_
   /* this info will not change while we hold the global FS write lock */
   is_packed = svn_fs_fs__is_packed_revprop(fs, rev);
 
+  /* Make sure that everyone accessing revprops on this repository either
+   * uses revprop caching or nobody does it. */
+  SVN_ERR(enforce_consistent_caching(fs, pool));
+
   /* Test whether revprops already exist for this revision.
    * Only then will we need to bump the revprop generation. */
   if (has_revprop_cache(fs, pool))

Modified: subversion/trunk/subversion/libsvn_ra_local/split_url.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_local/split_url.c?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_local/split_url.c (original)
+++ subversion/trunk/subversion/libsvn_ra_local/split_url.c Thu Aug 21 14:35:05 
2014
@@ -25,9 +25,9 @@
 #include <string.h>
 #include "svn_path.h"
 #include "svn_dirent_uri.h"
+#include "svn_hash.h"
 #include "svn_private_config.h"
 
-
 svn_error_t *
 svn_ra_local__split_URL(svn_repos_t **repos,
                         const char **repos_root_url,
@@ -41,6 +41,9 @@ svn_ra_local__split_URL(svn_repos_t **re
   svn_stringbuf_t *urlbuf;
   apr_size_t root_end;
 
+  apr_hash_t *fs_config = apr_hash_make(pool);
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "3");
+
   SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dirent, URL, pool));
 
   /* Search for a repository in the full path. */
@@ -50,7 +53,7 @@ svn_ra_local__split_URL(svn_repos_t **re
                              _("Unable to open repository '%s'"), URL);
 
   /* Attempt to open a repository at URL. */
-  err = svn_repos_open3(repos, repos_root_dirent, NULL, pool, pool);
+  err = svn_repos_open3(repos, repos_root_dirent, fs_config, pool, pool);
   if (err)
     return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err,
                              _("Unable to open repository '%s'"), URL);

Modified: subversion/trunk/subversion/svnadmin/svnadmin.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svnadmin/svnadmin.c?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/svnadmin/svnadmin.c (original)
+++ subversion/trunk/subversion/svnadmin/svnadmin.c Thu Aug 21 14:35:05 2014
@@ -126,7 +126,7 @@ open_repos(svn_repos_t **repos,
   apr_hash_t *fs_config = apr_hash_make(pool);
   svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1");
   svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1");
-  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2");
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "3");
   svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS,
                            svn_uuid_generate(pool));
   svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ,

Modified: subversion/trunk/subversion/svnlook/svnlook.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svnlook/svnlook.c?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/svnlook/svnlook.c (original)
+++ subversion/trunk/subversion/svnlook/svnlook.c Thu Aug 21 14:35:05 2014
@@ -2083,8 +2083,10 @@ get_ctxt_baton(svnlook_ctxt_t **baton_p,
                apr_pool_t *pool)
 {
   svnlook_ctxt_t *baton = apr_pcalloc(pool, sizeof(*baton));
+  apr_hash_t *fs_config = apr_hash_make(pool);
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "3");
 
-  SVN_ERR(svn_repos_open3(&(baton->repos), opt_state->repos_path, NULL,
+  SVN_ERR(svn_repos_open3(&(baton->repos), opt_state->repos_path, fs_config,
                           pool, pool));
   baton->fs = svn_repos_fs(baton->repos);
   svn_fs_set_warning_func(baton->fs, warning_func, NULL);

Modified: subversion/trunk/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c?rev=1619413&r1=1619412&r2=1619413&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c Thu Aug 21 
14:35:05 2014
@@ -1228,6 +1228,73 @@ revprop_caching_on_off(const svn_test_op
 #undef REPO_NAME
 
 /* ------------------------------------------------------------------------ */
+
+#define REPO_NAME "revprop_cache_setting"
+
+static svn_error_t *
+enforce_consistent_revprop_caching(const svn_test_opts_t *opts,
+                                   apr_pool_t *pool)
+{
+  svn_fs_t *fs_cache;
+  svn_fs_t *fs_no_cache;
+  svn_fs_t *fs_auto_cache;
+  apr_hash_t *fs_config = apr_hash_make(pool);
+  svn_string_t value = { "0", 1 };
+
+  if (strcmp(opts->fs_type, "fsfs") != 0)
+    return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, NULL);
+
+  /* Create test repository with greek tree. */
+  SVN_ERR(svn_test__create_fs(&fs_no_cache, REPO_NAME, opts, pool));
+
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "1");
+  SVN_ERR(svn_fs_open2(&fs_cache, REPO_NAME, fs_config, pool, pool));
+
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "3");
+  SVN_ERR(svn_fs_open2(&fs_auto_cache, REPO_NAME, fs_config, pool, pool));
+
+  /* Revprop mods not using the revprop cache is fine by default */
+  value.data = "1";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_no_cache, 0, "rp", NULL, &value, pool));
+
+  /* Revprop caching should not auto-enable now b/c no caching access
+   * happened yet. */
+  value.data = "2";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_auto_cache, 0, "rp", NULL, &value, pool));
+  value.data = "3";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_no_cache, 0, "rp", NULL, &value, pool));
+
+  /* Revprop mods using the revprop cache are also fine by default */
+  value.data = "4";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_cache, 0, "rp", NULL, &value, pool));
+
+  /* Form now on, we need the revprop cache to be active in our FS struct
+   * or we get an error when trying to set a revprop value. */
+  value.data = "5";
+  SVN_TEST_ASSERT_ERROR(svn_fs_change_rev_prop2(fs_no_cache, 0, "rp", NULL,
+                                                &value, pool),
+                        SVN_ERR_FS_REVPROP_CACHE_INIT_FAILURE);
+  value.data = "6";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_cache, 0, "rp", NULL, &value, pool));
+  value.data = "7";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_auto_cache, 0, "rp", NULL, &value, pool));
+
+  /* Manually removing the revprop generation file will allow non-caching
+   * revprop changes changes again. */
+  SVN_ERR(svn_io_remove_file2(REPO_NAME "/revprop-generation", FALSE, pool));
+  value.data = "8";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_no_cache, 0, "rp", NULL, &value, pool));
+  value.data = "9";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_auto_cache, 0, "rp", NULL, &value, pool));
+  value.data = "10";
+  SVN_ERR(svn_fs_change_rev_prop2(fs_cache, 0, "rp", NULL, &value, pool));
+
+  return SVN_NO_ERROR;
+}
+
+#undef REPO_NAME
+
+/* ------------------------------------------------------------------------ */
 
 /* The test table.  */
 
@@ -1269,7 +1336,10 @@ static struct svn_test_descriptor_t test
     SVN_TEST_OPTS_WIMP(revprop_caching_on_off,
                        "change revprops with enabled and disabled caching",
                        "fails with FSFS / svn_named_atomic__is_efficient()"),
+    SVN_TEST_OPTS_PASS(enforce_consistent_revprop_caching,
+                       "test revprop cache setting consistency"),
     SVN_TEST_NULL
   };
 
 SVN_TEST_MAIN
+  
\ No newline at end of file


Reply via email to