On the 1.6.x-issue3506 branch, merge r879966 and r879971 from trunk.


git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/1.6.x-issue3506@934631 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/libsvn_fs_fs/fs.h b/subversion/libsvn_fs_fs/fs.h
index 2752f77..b2da454 100644
--- a/subversion/libsvn_fs_fs/fs.h
+++ b/subversion/libsvn_fs_fs/fs.h
@@ -225,6 +225,10 @@
 
   /* The oldest revision not in a pack file. */
   svn_revnum_t min_unpacked_rev;
+
+  /* Whether rep-sharing is supported by the filesystem
+   * and allowed by the configuration. */
+  svn_boolean_t rep_sharing_allowed;
 } fs_fs_data_t;
 
 
diff --git a/subversion/libsvn_fs_fs/fs_fs.c b/subversion/libsvn_fs_fs/fs_fs.c
index ed003ad..f06ba10 100644
--- a/subversion/libsvn_fs_fs/fs_fs.c
+++ b/subversion/libsvn_fs_fs/fs_fs.c
@@ -1058,6 +1058,27 @@
 }
 
 static svn_error_t *
+read_config(svn_fs_t *fs,
+            apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  SVN_ERR(svn_config_read(&ffd->config,
+                          svn_path_join(fs->path, PATH_CONFIG, pool),
+                          FALSE, fs->pool));
+
+  /* Initialize ffd->rep_sharing_allowed. */
+  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
+    SVN_ERR(svn_config_get_bool(ffd->config, &ffd->rep_sharing_allowed,
+                                CONFIG_SECTION_REP_SHARING,
+                                CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
+  else
+    ffd->rep_sharing_allowed = FALSE;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 write_config(svn_fs_t *fs,
              apr_pool_t *pool)
 {
@@ -1147,7 +1168,6 @@
   int format, max_files_per_dir;
   char buf[APR_UUID_FORMATTED_LENGTH + 2];
   apr_size_t limit;
-  svn_boolean_t rep_sharing_allowed;
 
   fs->path = apr_pstrdup(fs->pool, path);
 
@@ -1175,17 +1195,7 @@
     SVN_ERR(update_min_unpacked_rev(fs, pool));
 
   /* Read the configuration file. */
-  SVN_ERR(svn_config_read(&ffd->config,
-                          svn_path_join(fs->path, PATH_CONFIG, pool),
-                          FALSE, fs->pool));
-  SVN_ERR(svn_config_get_bool(ffd->config, &rep_sharing_allowed,
-                              CONFIG_SECTION_REP_SHARING,
-                              CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
-
-  /* Open the rep cache. */
-  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT
-        && rep_sharing_allowed)
-    SVN_ERR(svn_fs_fs__open_rep_cache(fs, fs->pool));
+  SVN_ERR(read_config(fs, pool));
 
   return get_youngest(&(ffd->youngest_rev_cache), path, pool);
 }
@@ -5028,7 +5038,8 @@
 
   /* Check and see if we already have a representation somewhere that's
      identical to the one we just wrote out. */
-  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
+  if (ffd->rep_sharing_allowed)
+    /* ### TODO: ignore errors opening the DB (issue #3506) * */
     SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, b->fs, rep->sha1_checksum,
                                          b->parent_pool));
   else
@@ -5275,6 +5286,8 @@
                 const svn_fs_id_t *id,
                 const char *start_node_id,
                 const char *start_copy_id,
+                apr_array_header_t *reps_to_cache,
+                apr_pool_t *reps_pool,
                 apr_pool_t *pool)
 {
   node_revision_t *noderev;
@@ -5313,6 +5326,7 @@
           dirent = val;
           SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
                                   start_node_id, start_copy_id,
+                                  reps_to_cache, reps_pool,
                                   subpool));
           if (new_id && (svn_fs_fs__id_rev(new_id) == rev))
             dirent->id = svn_fs_fs__id_copy(new_id, pool);
@@ -5408,10 +5422,14 @@
                                    pool));
 
   /* Save the data representation's hash in the rep cache. */
-  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT
+  if (ffd->rep_sharing_allowed
         && noderev->data_rep && noderev->kind == svn_node_file
         && noderev->data_rep->revision == rev)
-    SVN_ERR(svn_fs_fs__set_rep_reference(fs, noderev->data_rep, FALSE, pool));
+    {
+      SVN_ERR_ASSERT(reps_to_cache && reps_pool);
+      APR_ARRAY_PUSH(reps_to_cache, representation_t *)
+        = svn_fs_fs__rep_copy(noderev->data_rep, reps_pool);
+    }
 
   /* Return our ID that references the revision file. */
   *new_id_p = noderev->id;
@@ -5652,6 +5670,8 @@
   svn_revnum_t *new_rev_p;
   svn_fs_t *fs;
   svn_fs_txn_t *txn;
+  apr_array_header_t *reps_to_cache;
+  apr_pool_t *reps_pool;
 };
 
 /* The work-horse for svn_fs_fs__commit, called with the FS write lock.
@@ -5705,6 +5725,7 @@
   root_id = svn_fs_fs__id_txn_create("0", "0", cb->txn->id, pool);
   SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, root_id,
                           start_node_id, start_copy_id,
+                          cb->reps_to_cache, cb->reps_pool,
                           pool));
 
   /* Write the changed-path information. */
@@ -5826,28 +5847,42 @@
   apr_pool_t *pool;
 };
 
+/* Add the representations in REPS_TO_CACHE (an array of representation_t *)
+ * to the rep-cache database of FS. */
+static svn_error_t *
+write_reps_to_cache(svn_fs_t *fs,
+                    apr_array_header_t *reps_to_cache,
+                    apr_pool_t *scratch_pool)
+{
+  int i;
+
+  for (i = 0; i < reps_to_cache->nelts; i++)
+    {
+      representation_t *rep = APR_ARRAY_IDX(reps_to_cache, i, representation_t *);
+
+      /* FALSE because we don't care if another parallel commit happened to
+       * collide with us.  (Non-parallel collisions will not be detected.) */
+      SVN_ERR(svn_fs_fs__set_rep_reference(fs, rep, FALSE, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Implements svn_sqlite__transaction_callback_t. */
 static svn_error_t *
 commit_sqlite_txn_callback(void *baton, svn_sqlite__db_t *db)
 {
   struct commit_sqlite_txn_baton *cstb = baton;
-  return commit_body(cstb->cb, cstb->pool);
-}
+  struct commit_baton *cb = cstb->cb;
 
-/* Wrapper around commit_body() which implements SQLite transactions.  Arguments
-   the same as commit_body().  */
-static svn_error_t *
-commit_body_rep_cache(void *baton, apr_pool_t *pool)
-{
-  struct commit_sqlite_txn_baton cstb;
-  struct commit_baton *cb = baton;
-  fs_fs_data_t *ffd = cb->fs->fsap_data;
+  /* Write new entries to the rep-sharing database.
+   *
+   * We use an sqlite transcation to speed things up;
+   * see <http://www.sqlite.org/faq.html#q19>.
+   */
+  SVN_ERR(write_reps_to_cache(cb->fs, cb->reps_to_cache, cstb->pool));
 
-  cstb.cb = cb;
-  cstb.pool = pool;
-
-  return svn_sqlite__with_transaction(ffd->rep_cache_db,
-                                      commit_sqlite_txn_callback, &cstb);
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *
@@ -5862,10 +5897,34 @@
   cb.new_rev_p = new_rev_p;
   cb.fs = fs;
   cb.txn = txn;
-  return svn_fs_fs__with_write_lock(fs,
-                                    ffd->rep_cache_db ? commit_body_rep_cache :
-                                                        commit_body,
-                                    &cb, pool);
+
+  if (ffd->rep_sharing_allowed)
+    {
+      cb.reps_to_cache = apr_array_make(pool, 5, sizeof(representation_t *));
+      cb.reps_pool = pool;
+    }
+  else
+    {
+      cb.reps_to_cache = NULL;
+      cb.reps_pool = NULL;
+    }
+
+  SVN_ERR(svn_fs_fs__with_write_lock(fs, commit_body, &cb, pool));
+
+  if (ffd->rep_sharing_allowed)
+    {
+      struct commit_sqlite_txn_baton cstb;
+      cstb.cb = &cb;
+      cstb.pool = pool;
+
+      /* ### TODO: ignore errors opening the DB (issue #3506) * */
+      SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
+      SVN_ERR(svn_sqlite__with_transaction(ffd->rep_cache_db,
+                                           commit_sqlite_txn_callback,
+                                           &cstb));
+    }
+
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *
@@ -5926,7 +5985,6 @@
 {
   int format = SVN_FS_FS__FORMAT_NUMBER;
   fs_fs_data_t *ffd = fs->fsap_data;
-  svn_boolean_t rep_sharing_allowed;
 
   fs->path = apr_pstrdup(pool, path);
   /* See if we had an explicitly requested pre-1.4- or pre-1.5-compatible.  */
@@ -5986,17 +6044,7 @@
   SVN_ERR(write_config(fs, pool));
 
   /* Read the configuration file. */
-  SVN_ERR(svn_config_read(&ffd->config,
-                          svn_path_join(fs->path, PATH_CONFIG, pool),
-                          FALSE, fs->pool));
-  SVN_ERR(svn_config_get_bool(ffd->config, &rep_sharing_allowed,
-                              CONFIG_SECTION_REP_SHARING,
-                              CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
-
-  /* Create the rep cache. */
-  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT
-        && rep_sharing_allowed)
-    SVN_ERR(svn_fs_fs__open_rep_cache(fs, fs->pool));
+  SVN_ERR(read_config(fs, pool));
 
   /* Create the min unpacked rev file. */
   if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
diff --git a/subversion/libsvn_fs_fs/rep-cache.c b/subversion/libsvn_fs_fs/rep-cache.c
index 429bff6..89a7775 100644
--- a/subversion/libsvn_fs_fs/rep-cache.c
+++ b/subversion/libsvn_fs_fs/rep-cache.c
@@ -61,6 +61,10 @@
   fs_fs_data_t *ffd = fs->fsap_data;
   const char *db_path;
 
+  /* Be idempotent. */
+  if (ffd->rep_cache_db)
+    return SVN_NO_ERROR;
+
   /* Open (or create) the sqlite database.  It will be automatically
      closed when fs->pool is destoyed. */
   db_path = svn_path_join(fs->path, REP_CACHE_DB_NAME, pool);
@@ -82,11 +86,9 @@
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
 
-  if (ffd->rep_cache_db == NULL)
-    {
-      *rep = NULL;
-      return SVN_NO_ERROR;
-    }
+  SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
+  if (! ffd->rep_cache_db)
+    SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
 
   /* We only allow SHA1 checksums in this table. */
   if (checksum->kind != svn_checksum_sha1)
@@ -124,8 +126,9 @@
   representation_t *old_rep;
   svn_sqlite__stmt_t *stmt;
 
-  if (ffd->rep_cache_db == NULL)
-    return SVN_NO_ERROR;
+  SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
+  if (! ffd->rep_cache_db)
+    SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
 
   /* We only allow SHA1 checksums in this table. */
   if (rep->sha1_checksum == NULL)
diff --git a/subversion/libsvn_fs_fs/rep-cache.h b/subversion/libsvn_fs_fs/rep-cache.h
index 1c268b9..4b352f5 100644
--- a/subversion/libsvn_fs_fs/rep-cache.h
+++ b/subversion/libsvn_fs_fs/rep-cache.h
@@ -29,7 +29,7 @@
 
 #define REP_CACHE_DB_NAME        "rep-cache.db"
 
-/* Open (and create, if needed) the rep cache database associated with FS.
+/* Open and create, if needed, the rep cache database associated with FS.
    Use POOL for temporary allocations. */
 svn_error_t *
 svn_fs_fs__open_rep_cache(svn_fs_t *fs,