You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by da...@apache.org on 2015/08/19 04:35:15 UTC

svn commit: r1696514 [6/10] - in /subversion/branches/patch-exec: ./ build/ac-macros/ notes/ subversion/bindings/javahl/native/ subversion/bindings/swig/ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_fs_fs/...

Modified: subversion/branches/patch-exec/subversion/libsvn_fs_x/transaction.c
URL: http://svn.apache.org/viewvc/subversion/branches/patch-exec/subversion/libsvn_fs_x/transaction.c?rev=1696514&r1=1696513&r2=1696514&view=diff
==============================================================================
--- subversion/branches/patch-exec/subversion/libsvn_fs_x/transaction.c (original)
+++ subversion/branches/patch-exec/subversion/libsvn_fs_x/transaction.c Wed Aug 19 02:35:12 2015
@@ -41,6 +41,7 @@
 #include "lock.h"
 #include "rep-cache.h"
 #include "index.h"
+#include "batch_fsync.h"
 
 #include "private/svn_fs_util.h"
 #include "private/svn_fspath.h"
@@ -315,8 +316,8 @@ with_lock(void *baton,
 /* Enum identifying a filesystem lock. */
 typedef enum lock_id_t
 {
-  write_lock,
   txn_lock,
+  write_lock,
   pack_lock
 } lock_id_t;
 
@@ -332,12 +333,6 @@ init_lock_baton(with_lock_baton_t *baton
 
   switch (lock_id)
     {
-    case write_lock:
-      baton->mutex = ffsd->fs_write_lock;
-      baton->lock_path = svn_fs_x__path_lock(baton->fs, baton->lock_pool);
-      baton->is_global_lock = TRUE;
-      break;
-
     case txn_lock:
       baton->mutex = ffsd->txn_current_lock;
       baton->lock_path = svn_fs_x__path_txn_current_lock(baton->fs,
@@ -345,6 +340,12 @@ init_lock_baton(with_lock_baton_t *baton
       baton->is_global_lock = FALSE;
       break;
 
+    case write_lock:
+      baton->mutex = ffsd->fs_write_lock;
+      baton->lock_path = svn_fs_x__path_lock(baton->fs, baton->lock_pool);
+      baton->is_global_lock = TRUE;
+      break;
+
     case pack_lock:
       baton->mutex = ffsd->fs_pack_lock;
       baton->lock_path = svn_fs_x__path_pack_lock(baton->fs,
@@ -477,10 +478,10 @@ svn_fs_x__with_all_locks(svn_fs_t *fs,
      fs_fs_shared_data_t.  The lock chain is being created in
      innermost (last to acquire) -> outermost (first to acquire) order. */
   with_lock_baton_t *lock_baton
-    = create_lock_baton(fs, write_lock, body, baton, scratch_pool);
+    = create_lock_baton(fs, txn_lock, body, baton, scratch_pool);
 
+  lock_baton = chain_lock_baton(write_lock, lock_baton);
   lock_baton = chain_lock_baton(pack_lock, lock_baton);
-  lock_baton = chain_lock_baton(txn_lock, lock_baton);
 
   return svn_error_trace(with_lock(lock_baton, scratch_pool));
 }
@@ -932,19 +933,10 @@ fold_change(apr_hash_t *changed_paths,
       /* This path already exists in the hash, so we have to merge
          this change into the already existing one. */
 
-      /* Sanity check:  only allow unused node revision IDs in the
-         `reset' case. */
-      if ((! svn_fs_x__id_used(&change->noderev_id))
-           && (change->change_kind != svn_fs_path_change_reset))
-        return svn_error_create
-          (SVN_ERR_FS_CORRUPT, NULL,
-           _("Missing required node revision ID"));
-
       /* Sanity check: we should be talking about the same node
          revision ID as our last change except where the last change
          was a deletion. */
-      if (svn_fs_x__id_used(&change->noderev_id)
-          && (!svn_fs_x__id_eq(&old_change->noderev_id, &change->noderev_id))
+      if (!svn_fs_x__id_eq(&old_change->noderev_id, &change->noderev_id)
           && (old_change->change_kind != svn_fs_path_change_delete))
         return svn_error_create
           (SVN_ERR_FS_CORRUPT, NULL,
@@ -955,7 +947,6 @@ fold_change(apr_hash_t *changed_paths,
          thing to follow a deletion. */
       if ((old_change->change_kind == svn_fs_path_change_delete)
           && (! ((change->change_kind == svn_fs_path_change_replace)
-                 || (change->change_kind == svn_fs_path_change_reset)
                  || (change->change_kind == svn_fs_path_change_add))))
         return svn_error_create
           (SVN_ERR_FS_CORRUPT, NULL,
@@ -964,8 +955,7 @@ fold_change(apr_hash_t *changed_paths,
       /* Sanity check: an add can't follow anything except
          a delete or reset.  */
       if ((change->change_kind == svn_fs_path_change_add)
-          && (old_change->change_kind != svn_fs_path_change_delete)
-          && (old_change->change_kind != svn_fs_path_change_reset))
+          && (old_change->change_kind != svn_fs_path_change_delete))
         return svn_error_create
           (SVN_ERR_FS_CORRUPT, NULL,
            _("Invalid change ordering: add change on preexisting path"));
@@ -973,12 +963,6 @@ fold_change(apr_hash_t *changed_paths,
       /* Now, merge that change in. */
       switch (change->change_kind)
         {
-        case svn_fs_path_change_reset:
-          /* A reset here will simply remove the path change from the
-             hash. */
-          apr_hash_set(changed_paths, path->data, path->len, NULL);
-          break;
-
         case svn_fs_path_change_delete:
           if (old_change->change_kind == svn_fs_path_change_add)
             {
@@ -1192,6 +1176,60 @@ create_new_txn_noderev_from_rev(svn_fs_t
   return svn_fs_x__put_node_revision(fs, noderev, scratch_pool);
 }
 
+/* Read 'txn-current', return it in *TXN_NUMBER and write the next value
+   into 'txn-next' for FS.  Schedule fsyncs in BATCH.  Use SCRATCH_POOL
+   for temporaries. */
+static svn_error_t *
+get_and_txn_key(apr_uint64_t *txn_number,
+                svn_fs_t *fs,
+                svn_fs_x__batch_fsync_t *batch,
+                apr_pool_t *scratch_pool)
+{
+  const char *txn_current_path = svn_fs_x__path_txn_current(fs, scratch_pool);
+  const char *txn_next_path = svn_fs_x__path_txn_next(fs, scratch_pool);
+
+  apr_file_t *file;
+  char new_id_str[SVN_INT64_BUFFER_SIZE];
+
+  svn_stringbuf_t *buf;
+  SVN_ERR(svn_fs_x__read_content(&buf, txn_current_path, scratch_pool));
+
+  /* remove trailing newlines */
+  *txn_number = svn__base36toui64(NULL, buf->data);
+  if (*txn_number == 0)
+    ++(*txn_number);
+
+  /* Increment the key and add a trailing \n to the string so the
+     txn-current file has a newline in it. */
+  SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, txn_next_path,
+                                          scratch_pool));
+  SVN_ERR(svn_io_file_write_full(file, new_id_str,
+                                 svn__ui64tobase36(new_id_str, *txn_number+1),
+                                 NULL, scratch_pool));
+  SVN_ERR(svn_io_copy_perms(txn_current_path, txn_next_path, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Move 'txn-next' into place as 'txn-current' for FS.  Schedule fsyncs
+   in BATCH.  Use SCRATCH_POOL for temporaries. */
+static svn_error_t *
+bump_txn_key(svn_fs_t *fs,
+             svn_fs_x__batch_fsync_t *batch,
+             apr_pool_t *scratch_pool)
+{
+  const char *txn_current_path = svn_fs_x__path_txn_current(fs, scratch_pool);
+  const char *txn_next_path = svn_fs_x__path_txn_next(fs, scratch_pool);
+
+  /* Increment the key and add a trailing \n to the string so the
+     txn-current file has a newline in it. */
+  SVN_ERR(svn_io_file_rename(txn_next_path, txn_current_path, scratch_pool));
+  SVN_ERR(svn_fs_x__batch_fsync_new_path(batch, txn_current_path,
+                                         scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* A structure used by get_and_increment_txn_key_body(). */
 typedef struct get_and_increment_txn_key_baton_t
 {
@@ -1207,34 +1245,22 @@ get_and_increment_txn_key_body(void *bat
                                apr_pool_t *scratch_pool)
 {
   get_and_increment_txn_key_baton_t *cb = baton;
-  const char *txn_current_filename = svn_fs_x__path_txn_current(cb->fs,
-                                                                scratch_pool);
-  const char *tmp_filename;
-  char new_id_str[SVN_INT64_BUFFER_SIZE];
+  svn_fs_x__batch_fsync_t *batch;
 
-  svn_stringbuf_t *buf;
-  SVN_ERR(svn_fs_x__read_content(&buf, txn_current_filename, scratch_pool));
+  SVN_ERR(svn_fs_x__batch_fsync_create(&batch, scratch_pool));
+  SVN_ERR(get_and_txn_key(&cb->txn_number, cb->fs, batch, scratch_pool));
+  SVN_ERR(svn_fs_x__batch_fsync_run(batch, scratch_pool));
 
-  /* remove trailing newlines */
-  cb->txn_number = svn__base36toui64(NULL, buf->data);
-
-  /* Increment the key and add a trailing \n to the string so the
-     txn-current file has a newline in it. */
-  SVN_ERR(svn_io_write_unique(&tmp_filename,
-                              svn_dirent_dirname(txn_current_filename,
-                                                 scratch_pool),
-                              new_id_str,
-                              svn__ui64tobase36(new_id_str, cb->txn_number+1),
-                              svn_io_file_del_none, scratch_pool));
-  SVN_ERR(svn_fs_x__move_into_place(tmp_filename, txn_current_filename,
-                                    txn_current_filename, scratch_pool));
+  SVN_ERR(bump_txn_key(cb->fs, batch, scratch_pool));
+  SVN_ERR(svn_fs_x__batch_fsync_run(batch, scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
 /* Create a unique directory for a transaction in FS based on revision REV.
-   Return the ID for this transaction in *ID_P and *TXN_ID.  Use a sequence
-   value in the transaction ID to prevent reuse of transaction IDs. */
+   Return the ID for this transaction in *ID_P, allocated from RESULT_POOL
+   and *TXN_ID.  Use a sequence value in the transaction ID to prevent reuse
+   of transaction IDs.  Allocate temporaries from SCRATCH_POOL. */
 static svn_error_t *
 create_txn_dir(const char **id_p,
                svn_fs_x__txn_id_t *txn_id,
@@ -1242,19 +1268,33 @@ create_txn_dir(const char **id_p,
                apr_pool_t *result_pool,
                apr_pool_t *scratch_pool)
 {
-  get_and_increment_txn_key_baton_t cb;
   const char *txn_dir;
+  svn_fs_x__data_t *ffd = fs->fsap_data;
 
-  /* Get the current transaction sequence value, which is a base-36
-     number, from the txn-current file, and write an
-     incremented value back out to the file.  Place the revision
-     number the transaction is based off into the transaction id. */
-  cb.fs = fs;
-  SVN_ERR(svn_fs_x__with_txn_current_lock(fs,
-                                          get_and_increment_txn_key_body,
-                                          &cb,
-                                          scratch_pool));
-  *txn_id = cb.txn_number;
+  /* If we recently committed a revision through FS, we will have a
+     pre-allocated txn-ID that we can just use. */
+  if (ffd->next_txn_id)
+    {
+      *txn_id = ffd->next_txn_id;
+
+      /* Not pre-allocated anymore. */
+      ffd->next_txn_id = 0;
+    }
+  else
+    {
+      get_and_increment_txn_key_baton_t cb;
+
+      /* Get the current transaction sequence value, which is a base-36
+        number, from the txn-current file, and write an
+        incremented value back out to the file.  Place the revision
+        number the transaction is based off into the transaction id. */
+      cb.fs = fs;
+      SVN_ERR(svn_fs_x__with_txn_current_lock(fs,
+                                              get_and_increment_txn_key_body,
+                                              &cb,
+                                              scratch_pool));
+      *txn_id = cb.txn_number;
+    }
 
   *id_p = svn_fs_x__txn_name(*txn_id, result_pool);
   txn_dir = svn_fs_x__path_txn_dir(fs, *txn_id, scratch_pool);
@@ -1322,12 +1362,12 @@ create_txn(svn_fs_txn_t **txn_p,
 }
 
 /* Store the property list for transaction TXN_ID in PROPLIST.
-   Perform temporary allocations in POOL. */
+   Perform temporary allocations in SCRATCH_POOL. */
 static svn_error_t *
 get_txn_proplist(apr_hash_t *proplist,
                  svn_fs_t *fs,
                  svn_fs_x__txn_id_t txn_id,
-                 apr_pool_t *pool)
+                 apr_pool_t *scratch_pool)
 {
   svn_stream_t *stream;
 
@@ -1340,11 +1380,13 @@ get_txn_proplist(apr_hash_t *proplist,
 
   /* Open the transaction properties file. */
   SVN_ERR(svn_stream_open_readonly(&stream,
-                                   svn_fs_x__path_txn_props(fs, txn_id, pool),
-                                   pool, pool));
+                                   svn_fs_x__path_txn_props(fs, txn_id,
+                                                            scratch_pool),
+                                   scratch_pool, scratch_pool));
 
   /* Read in the property list. */
-  SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool));
+  SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR,
+                         scratch_pool));
 
   return svn_stream_close(stream);
 }
@@ -1355,26 +1397,26 @@ static svn_error_t *
 set_txn_proplist(svn_fs_t *fs,
                  svn_fs_x__txn_id_t txn_id,
                  apr_hash_t *props,
-                 svn_boolean_t final,
                  apr_pool_t *scratch_pool)
 {
-  svn_stringbuf_t *buf;
   svn_stream_t *stream;
+  const char *temp_path;
 
-  /* Write out the new file contents to BUF. */
-  buf = svn_stringbuf_create_ensure(1024, scratch_pool);
-  stream = svn_stream_from_stringbuf(buf, scratch_pool);
+  /* Write the new contents into a temporary file. */
+  SVN_ERR(svn_stream_open_unique(&stream, &temp_path,
+                                 svn_fs_x__path_txn_dir(fs, txn_id,
+                                                        scratch_pool),
+                                 svn_io_file_del_none,
+                                 scratch_pool, scratch_pool));
   SVN_ERR(svn_hash_write2(props, stream, SVN_HASH_TERMINATOR, scratch_pool));
   SVN_ERR(svn_stream_close(stream));
 
-  /* Open the transaction properties file and write new contents to it. */
-  SVN_ERR(svn_io_write_atomic((final
-                               ? svn_fs_x__path_txn_props_final(fs, txn_id,
-                                                                scratch_pool)
-                               : svn_fs_x__path_txn_props(fs, txn_id,
-                                                          scratch_pool)),
-                              buf->data, buf->len,
-                              NULL /* copy_perms_path */, scratch_pool));
+  /* Replace the old file with the new one. */
+  SVN_ERR(svn_io_file_rename(temp_path,
+                             svn_fs_x__path_txn_props(fs, txn_id,
+                                                      scratch_pool),
+                             scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -1430,8 +1472,7 @@ svn_fs_x__change_txn_props(svn_fs_txn_t
 
   /* Create a new version of the file and write out the new props. */
   /* Open the transaction properties file. */
-  SVN_ERR(set_txn_proplist(txn->fs, ftd->txn_id, txn_prop, FALSE,
-                           scratch_pool));
+  SVN_ERR(set_txn_proplist(txn->fs, ftd->txn_id, txn_prop, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -1462,8 +1503,8 @@ svn_fs_x__get_txn(svn_fs_x__transaction_
   return SVN_NO_ERROR;
 }
 
-/* If it is supported by the format of file system FS, store the (ITEM_INDEX,
- * OFFSET) pair in the log-to-phys proto index file of transaction TXN_ID.
+/* Store the (ITEM_INDEX, OFFSET) pair in the log-to-phys proto index file
+ * of transaction TXN_ID in filesystem FS.
  * Use SCRATCH_POOL for temporary allocations.
  */
 static svn_error_t *
@@ -1483,9 +1524,8 @@ store_l2p_index_entry(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
-/* If it is supported by the format of file system FS, store ENTRY in the
- * phys-to-log proto index file of transaction TXN_ID.
- * Use SCRATCH_POOL for temporary allocations.
+/* Store ENTRY in the phys-to-log proto index file of transaction TXN_ID
+ * in filesystem FS.  Use SCRATCH_POOL for temporary allocations.
  */
 static svn_error_t *
 store_p2l_index_entry(svn_fs_t *fs,
@@ -1683,25 +1723,30 @@ svn_fs_x__purge_txn(svn_fs_t *fs,
                     apr_pool_t *scratch_pool)
 {
   svn_fs_x__txn_id_t txn_id;
+
+  /* The functions we are calling open files and operate on the OS FS.
+     Since these may allocate a non-trivial amount of memory, do that
+     in a SUBPOOL and clear that one up before returning. */
+  apr_pool_t *subpool = svn_pool_create(scratch_pool);
   SVN_ERR(svn_fs_x__txn_by_name(&txn_id, txn_id_str));
 
   /* Remove the shared transaction object associated with this transaction. */
-  SVN_ERR(purge_shared_txn(fs, txn_id, scratch_pool));
+  SVN_ERR(purge_shared_txn(fs, txn_id, subpool));
   /* Remove the directory associated with this transaction. */
-  SVN_ERR(svn_io_remove_dir2(svn_fs_x__path_txn_dir(fs, txn_id, scratch_pool),
-                             FALSE, NULL, NULL, scratch_pool));
+  SVN_ERR(svn_io_remove_dir2(svn_fs_x__path_txn_dir(fs, txn_id, subpool),
+                             FALSE, NULL, NULL, subpool));
 
-  /* Delete protorev and its lock, which aren't in the txn
-      directory.  It's OK if they don't exist (for example, if this
-      is post-commit and the proto-rev has been moved into
-      place). */
+  /* Delete protorev and its lock, which aren't in the txn directory.
+     It's OK if they don't exist (for example, if this is post-commit
+     and the proto-rev has been moved into place). */
   SVN_ERR(svn_io_remove_file2(
-                  svn_fs_x__path_txn_proto_rev(fs, txn_id, scratch_pool),
-                  TRUE, scratch_pool));
+                svn_fs_x__path_txn_proto_rev(fs, txn_id, subpool),
+                TRUE, subpool));
   SVN_ERR(svn_io_remove_file2(
-                  svn_fs_x__path_txn_proto_rev_lock(fs, txn_id, scratch_pool),
-                  TRUE, scratch_pool));
+                svn_fs_x__path_txn_proto_rev_lock(fs, txn_id, subpool),
+                TRUE, subpool));
 
+  svn_pool_destroy(subpool);
   return SVN_NO_ERROR;
 }
 
@@ -1975,8 +2020,7 @@ shards_spanned(int *spanned,
 /* Given a node-revision NODEREV in filesystem FS, return the
    representation in *REP to use as the base for a text representation
    delta if PROPS is FALSE.  If PROPS has been set, a suitable props
-   base representation will be returned.  Perform temporary allocations
-   in *POOL. */
+   base representation will be returned.  Perform allocations in POOL. */
 static svn_error_t *
 choose_delta_base(svn_fs_x__representation_t **rep,
                   svn_fs_t *fs,
@@ -1984,9 +2028,10 @@ choose_delta_base(svn_fs_x__representati
                   svn_boolean_t props,
                   apr_pool_t *pool)
 {
-  /* The zero-based index (counting from the "oldest" end), along NODEREVs line
-   * predecessors, of the node-rev we will use as delta base. */
+  /* The zero-based index (counting from the "oldest" end), along NODEREVs
+   * line predecessors, of the node-rev we will use as delta base. */
   int count;
+
   /* The length of the linear part of a delta chain.  (Delta chains use
    * skip-delta bits for the high-order bits and are linear in the low-order
    * bits.) */
@@ -2195,7 +2240,7 @@ rep_write_get_baton(rep_write_baton_t **
       header.type = svn_fs_x__rep_self_delta;
     }
   SVN_ERR(svn_fs_x__write_rep_header(&header, b->rep_stream,
-                                      b->local_pool));
+                                     b->local_pool));
 
   /* Now determine the offset of the actual svndiff data. */
   SVN_ERR(svn_fs_x__get_file_offset(&b->delta_start, file,
@@ -2529,7 +2574,8 @@ svn_fs_x__set_proplist(svn_fs_t *fs,
     {
       svn_fs_x__txn_id_t txn_id
         = svn_fs_x__get_txn_id(noderev->noderev_id.change_set);
-      noderev->prop_rep = apr_pcalloc(scratch_pool, sizeof(*noderev->prop_rep));
+      noderev->prop_rep = apr_pcalloc(scratch_pool,
+                                      sizeof(*noderev->prop_rep));
       noderev->prop_rep->id.change_set = id->change_set;
       SVN_ERR(allocate_item_index(&noderev->prop_rep->id.number, fs,
                                   txn_id, scratch_pool));
@@ -3199,62 +3245,56 @@ verify_locks(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
-/* Return in *PATH the path to a file containing the properties that
-   make up the final revision properties file.  This involves setting
-   svn:date and removing any temporary properties associated with the
-   commit flags. */
+/* Based on the transaction properties of TXN, write the final revision
+   properties for REVISION into their final location. Return that location
+   in *PATH and schedule the necessary fsync calls in BATCH.  This involves
+   setting svn:date and removing any temporary properties associated with
+   the commit flags. */
 static svn_error_t *
 write_final_revprop(const char **path,
                     svn_fs_txn_t *txn,
-                    svn_fs_x__txn_id_t txn_id,
-                    apr_pool_t *pool)
+                    svn_revnum_t revision,
+                    svn_fs_x__batch_fsync_t *batch,
+                    apr_pool_t *result_pool,
+                    apr_pool_t *scratch_pool)
 {
-  apr_hash_t *txnprops;
-  svn_boolean_t final_mods = FALSE;
+  apr_hash_t *props;
   svn_string_t date;
   svn_string_t *client_date;
+  apr_file_t *file;
+  svn_stream_t *stream;
 
-  SVN_ERR(svn_fs_x__txn_proplist(&txnprops, txn, pool));
+  SVN_ERR(svn_fs_x__txn_proplist(&props, txn, scratch_pool));
 
   /* Remove any temporary txn props representing 'flags'. */
-  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
-    {
-      svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD, NULL);
-      final_mods = TRUE;
-    }
+  if (svn_hash_gets(props, SVN_FS__PROP_TXN_CHECK_OOD))
+    svn_hash_sets(props, SVN_FS__PROP_TXN_CHECK_OOD, NULL);
 
-  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
-    {
-      svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL);
-      final_mods = TRUE;
-    }
+  if (svn_hash_gets(props, SVN_FS__PROP_TXN_CHECK_LOCKS))
+    svn_hash_sets(props, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL);
 
-  client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE);
+  client_date = svn_hash_gets(props, SVN_FS__PROP_TXN_CLIENT_DATE);
   if (client_date)
-    {
-      svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE, NULL);
-      final_mods = TRUE;
-    }
+    svn_hash_sets(props, SVN_FS__PROP_TXN_CLIENT_DATE, NULL);
 
   /* Update commit time to ensure that svn:date revprops remain ordered if
      requested. */
   if (!client_date || strcmp(client_date->data, "1"))
     {
-      date.data = svn_time_to_cstring(apr_time_now(), pool);
+      date.data = svn_time_to_cstring(apr_time_now(), scratch_pool);
       date.len = strlen(date.data);
-      svn_hash_sets(txnprops, SVN_PROP_REVISION_DATE, &date);
-      final_mods = TRUE;
+      svn_hash_sets(props, SVN_PROP_REVISION_DATE, &date);
     }
 
-  if (final_mods)
-    {
-      SVN_ERR(set_txn_proplist(txn->fs, txn_id, txnprops, TRUE, pool));
-      *path = svn_fs_x__path_txn_props_final(txn->fs, txn_id, pool);
-    }
-  else
-    {
-      *path = svn_fs_x__path_txn_props(txn->fs, txn_id, pool);
-    }
+  /* Create a file at the final revprops location. */
+  *path = svn_fs_x__path_revprops(txn->fs, revision, result_pool);
+  SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, *path, scratch_pool));
+  SVN_ERR(svn_fs_x__batch_fsync_new_path(batch, *path, scratch_pool));
+
+  /* Write the new contents to the final revprops file. */
+  stream = svn_stream_from_aprfile2(file, TRUE, scratch_pool);
+  SVN_ERR(svn_hash_write2(props, stream, SVN_HASH_TERMINATOR, scratch_pool));
+  SVN_ERR(svn_stream_close(stream));
 
   return SVN_NO_ERROR;
 }
@@ -3302,6 +3342,171 @@ svn_fs_x__add_index_data(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
+/* Make sure that the shard folder for REVSION exists in FS.  If we had to
+   create them, schedule their fsync in BATCH.  Use SCRATCH_POOL for
+   temporary allocations. */
+static svn_error_t *
+auto_create_shard(svn_fs_t *fs,
+                  svn_revnum_t revision,
+                  svn_fs_x__batch_fsync_t *batch,
+                  apr_pool_t *scratch_pool)
+{
+  svn_fs_x__data_t *ffd = fs->fsap_data;
+  if (revision % ffd->max_files_per_dir == 0)
+    {
+      const char *new_dir = svn_fs_x__path_shard(fs, revision, scratch_pool);
+      svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT,
+                                         scratch_pool);
+
+      if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
+        return svn_error_trace(err);
+      svn_error_clear(err);
+
+      SVN_ERR(svn_io_copy_perms(svn_dirent_join(fs->path, PATH_REVS_DIR,
+                                                scratch_pool),
+                                new_dir, scratch_pool));
+      SVN_ERR(svn_fs_x__batch_fsync_new_path(batch, new_dir, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Move the protype revision file of transaction TXN_ID in FS to the final
+   location for REVISION and return a handle to it in *FILE.  Schedule any
+   fsyncs in BATCH and use SCRATCH_POOL for temporaries.
+
+   Note that the lifetime of *FILE is determined by BATCH instead of
+   SCRATCH_POOL.  It will be invalidated by either BATCH being cleaned up
+   itself of by running svn_fs_x__batch_fsync_run on it.
+
+   This function will "destroy" the transaction by removing its prototype
+   revision file, so it can at most be called once per transaction.  Also,
+   later attempts to modify this txn will fail due to get_writable_proto_rev
+   not finding the protorev file.  Therefore, we will take out the lock for
+   it only until we move the file to its final location.
+
+   If the prototype revision file is already locked, return error
+   SVN_ERR_FS_REP_BEING_WRITTEN. */
+static svn_error_t *
+get_writable_final_rev(apr_file_t **file,
+                       svn_fs_t *fs,
+                       svn_fs_x__txn_id_t txn_id,
+                       svn_revnum_t revision,
+                       svn_fs_x__batch_fsync_t *batch,
+                       apr_pool_t *scratch_pool)
+{
+  get_writable_proto_rev_baton_t baton;
+  apr_off_t end_offset = 0;
+  void *lockcookie;
+
+  const char *proto_rev_filename
+    = svn_fs_x__path_txn_proto_rev(fs, txn_id, scratch_pool);
+  const char *final_rev_filename
+    = svn_fs_x__path_rev(fs, revision, scratch_pool);
+
+  /* Acquire exclusive access to the proto-rev file. */
+  baton.lockcookie = &lockcookie;
+  baton.txn_id = txn_id;
+
+  SVN_ERR(with_txnlist_lock(fs, get_writable_proto_rev_body, &baton,
+                            scratch_pool));
+
+  /* Move the proto-rev file to its final location as revision data file.
+     After that, we don't need to protect it anymore and can unlock it. */
+  SVN_ERR(svn_error_compose_create(svn_io_file_rename(proto_rev_filename,
+                                                      final_rev_filename,
+                                                      scratch_pool),
+                                   unlock_proto_rev(fs, txn_id, lockcookie,
+                                                    scratch_pool)));
+  SVN_ERR(svn_fs_x__batch_fsync_new_path(batch, final_rev_filename,
+                                         scratch_pool));
+
+  /* Now open the prototype revision file and seek to the end.
+     Note that BATCH always seeks to position 0 before returning the file. */
+  SVN_ERR(svn_fs_x__batch_fsync_open_file(file, batch, final_rev_filename,
+                                          scratch_pool));
+  SVN_ERR(svn_io_file_seek(*file, APR_END, &end_offset, scratch_pool));
+
+  /* We don't want unused sections (such as leftovers from failed delta
+     stream) in our file.  If we use log addressing, we would need an
+     index entry for the unused section and that section would need to
+     be all NUL by convention.  So, detect and fix those cases by truncating
+     the protorev file. */
+  SVN_ERR(auto_truncate_proto_rev(fs, *file, end_offset, txn_id,
+                                  scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Write REVISION into FS' 'next' file and schedule necessary fsyncs in BATCH.
+   Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+write_next_file(svn_fs_t *fs,
+                svn_revnum_t revision,
+                svn_fs_x__batch_fsync_t *batch,
+                apr_pool_t *scratch_pool)
+{
+  apr_file_t *file;
+  const char *path = svn_fs_x__path_next(fs, scratch_pool);
+  const char *perms_path = svn_fs_x__path_current(fs, scratch_pool);
+  char *buf;
+
+  /* Create / open the 'next' file. */
+  SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, path, scratch_pool));
+  SVN_ERR(svn_fs_x__batch_fsync_new_path(batch, path, scratch_pool));
+
+  /* Write its contents. */
+  buf = apr_psprintf(scratch_pool, "%ld\n", revision);
+  SVN_ERR(svn_io_file_write_full(file, buf, strlen(buf), NULL, scratch_pool));
+
+  /* Adjust permissions. */
+  SVN_ERR(svn_io_copy_perms(perms_path, path, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Baton type to be used with bump_ids. */
+typedef struct bump_ids_baton_t
+{
+  svn_fs_t *fs;
+  svn_revnum_t new_rev;
+  svn_fs_x__batch_fsync_t *batch;
+} bump_ids_baton_t;
+
+/* Bump the 'current' and 'txn-current' files in BATON->FS. */
+static svn_error_t *
+bump_ids(void *baton,
+         apr_pool_t *scratch_pool)
+{
+  bump_ids_baton_t *b = baton;
+  svn_fs_x__data_t *ffd = b->fs->fsap_data;
+  const char *current_filename;
+
+  /* Write the 'next' file. */
+  SVN_ERR(write_next_file(b->fs, b->new_rev, b->batch, scratch_pool));
+
+  /* Allocate a new txn id. */
+  SVN_ERR(get_and_txn_key(&ffd->next_txn_id, b->fs, b->batch, scratch_pool));
+
+  /* Commit all changes to disk. */
+  SVN_ERR(svn_fs_x__batch_fsync_run(b->batch, scratch_pool));
+
+  /* Make the revision visible to all processes and threads. */
+  current_filename = svn_fs_x__path_current(b->fs, scratch_pool);
+  SVN_ERR(svn_io_file_rename(svn_fs_x__path_next(b->fs, scratch_pool),
+                             current_filename, scratch_pool));
+  SVN_ERR(svn_fs_x__batch_fsync_new_path(b->batch, current_filename,
+                                         scratch_pool));
+
+  /* Bump txn id. */
+  SVN_ERR(bump_txn_key(b->fs, b->batch, scratch_pool));
+
+  /* Make the new revision permanently visible. */
+  SVN_ERR(svn_fs_x__batch_fsync_run(b->batch, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Baton used for commit_body below. */
 typedef struct commit_baton_t {
   svn_revnum_t *new_rev_p;
@@ -3321,15 +3526,21 @@ commit_body(void *baton,
 {
   commit_baton_t *cb = baton;
   svn_fs_x__data_t *ffd = cb->fs->fsap_data;
-  const char *old_rev_filename, *rev_filename, *proto_filename;
-  const char *revprop_filename, *final_revprop;
+  const char *old_rev_filename, *rev_filename;
+  const char *revprop_filename;
   svn_fs_x__id_t root_id, new_root_id;
   svn_revnum_t old_rev, new_rev;
   apr_file_t *proto_file;
-  void *proto_file_lockcookie;
   apr_off_t initial_offset, changed_path_offset;
   svn_fs_x__txn_id_t txn_id = svn_fs_x__txn_get_id(cb->txn);
   apr_hash_t *changed_paths;
+  svn_fs_x__batch_fsync_t *batch;
+  bump_ids_baton_t bump_ids_baton;
+
+  /* We perform a sequence of (potentially) large allocations.
+     Keep the peak memory usage low by using a SUBPOOL and cleaning it
+     up frequently. */
+  apr_pool_t *subpool = svn_pool_create(scratch_pool);
 
   /* Re-Read the current repository format.  All our repo upgrade and
      config evaluation strategies are such that existing information in
@@ -3338,17 +3549,12 @@ commit_body(void *baton,
      Although we don't recommend upgrading hot repositories, people may
      still do it and we must make sure to either handle them gracefully
      or to error out.
-
-     Committing pre-format 3 txns will fail after upgrade to format 3+
-     because the proto-rev cannot be found; no further action needed.
-     Upgrades from pre-f7 to f7+ means a potential change in addressing
-     mode for the final rev.  We must be sure to detect that cause because
-     the failure would only manifest once the new revision got committed.
    */
-  SVN_ERR(svn_fs_x__read_format_file(cb->fs, scratch_pool));
+  SVN_ERR(svn_fs_x__read_format_file(cb->fs, subpool));
 
   /* Get the current youngest revision. */
-  SVN_ERR(svn_fs_x__youngest_rev(&old_rev, cb->fs, scratch_pool));
+  SVN_ERR(svn_fs_x__youngest_rev(&old_rev, cb->fs, subpool));
+  svn_pool_clear(subpool);
 
   /* Check to make sure this transaction is based off the most recent
      revision. */
@@ -3365,111 +3571,76 @@ commit_body(void *baton,
      previous svn_fs.h functions and svn_fs_commit_txn(), so we need
      to re-examine every changed-path in the txn and re-verify all
      discovered locks. */
-  SVN_ERR(verify_locks(cb->fs, txn_id, changed_paths, scratch_pool));
+  SVN_ERR(verify_locks(cb->fs, txn_id, changed_paths, subpool));
+  svn_pool_clear(subpool);
 
   /* We are going to be one better than this puny old revision. */
   new_rev = old_rev + 1;
 
-  /* Get a write handle on the proto revision file. */
-  SVN_ERR(get_writable_proto_rev(&proto_file, &proto_file_lockcookie,
-                                 cb->fs, txn_id, scratch_pool));
-  SVN_ERR(svn_fs_x__get_file_offset(&initial_offset, proto_file,
-                                    scratch_pool));
+  /* Use this to force all data to be flushed to physical storage
+     (to the degree our environment will allow). */
+  SVN_ERR(svn_fs_x__batch_fsync_create(&batch, scratch_pool));
+
+  /* Set up the target directory. */
+  SVN_ERR(auto_create_shard(cb->fs, new_rev, batch, subpool));
+
+  /* Get a write handle on the proto revision file.
+
+     ### This "breaks" the transaction by removing the protorev file
+     ### but the revision is not yet complete.  If this commit does
+     ### not complete for any reason the transaction will be lost. */
+  SVN_ERR(get_writable_final_rev(&proto_file, cb->fs, txn_id, new_rev,
+                                 batch, subpool));
+  SVN_ERR(svn_fs_x__get_file_offset(&initial_offset, proto_file, subpool));
+  svn_pool_clear(subpool);
 
   /* Write out all the node-revisions and directory contents. */
   svn_fs_x__init_txn_root(&root_id, txn_id);
   SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, &root_id,
                           initial_offset, cb->reps_to_cache, cb->reps_hash,
-                          cb->reps_pool, TRUE, scratch_pool));
+                          cb->reps_pool, TRUE, subpool));
+  svn_pool_clear(subpool);
 
   /* Write the changed-path information. */
   SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file,
                                         cb->fs, txn_id, changed_paths,
-                                        new_rev, scratch_pool));
+                                        new_rev, subpool));
+  svn_pool_clear(subpool);
 
   /* Append the index data to the rev file. */
   SVN_ERR(svn_fs_x__add_index_data(cb->fs, proto_file,
-                svn_fs_x__path_l2p_proto_index(cb->fs, txn_id, scratch_pool),
-                svn_fs_x__path_p2l_proto_index(cb->fs, txn_id, scratch_pool),
-                new_rev, scratch_pool));
-
-  SVN_ERR(svn_io_file_flush_to_disk(proto_file, scratch_pool));
-  SVN_ERR(svn_io_file_close(proto_file, scratch_pool));
-
-  /* We don't unlock the prototype revision file immediately to avoid a
-     race with another caller writing to the prototype revision file
-     before we commit it. */
-
-  /* Create the shard for the rev and revprop file, if we're sharding and
-     this is the first revision of a new shard.  We don't care if this
-     fails because the shard already existed for some reason. */
-  if (new_rev % ffd->max_files_per_dir == 0)
-    {
-      /* Create the revs shard. */
-        {
-          const char *new_dir
-            = svn_fs_x__path_rev_shard(cb->fs, new_rev, scratch_pool);
-          svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT,
-                                             scratch_pool);
-          if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
-            return svn_error_trace(err);
-          svn_error_clear(err);
-          SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
-                                                    PATH_REVS_DIR,
-                                                    scratch_pool),
-                                    new_dir, scratch_pool));
-        }
-
-      /* Create the revprops shard. */
-      SVN_ERR_ASSERT(! svn_fs_x__is_packed_revprop(cb->fs, new_rev));
-        {
-          const char *new_dir
-            = svn_fs_x__path_revprops_shard(cb->fs, new_rev, scratch_pool);
-          svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT,
-                                             scratch_pool);
-          if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
-            return svn_error_trace(err);
-          svn_error_clear(err);
-          SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
-                                                    PATH_REVPROPS_DIR,
-                                                    scratch_pool),
-                                    new_dir, scratch_pool));
-        }
-    }
-
-  /* Move the finished rev file into place.
-
-     ### This "breaks" the transaction by removing the protorev file
-     ### but the revision is not yet complete.  If this commit does
-     ### not complete for any reason the transaction will be lost. */
-  old_rev_filename = svn_fs_x__path_rev_absolute(cb->fs, old_rev,
-                                                 scratch_pool);
+                      svn_fs_x__path_l2p_proto_index(cb->fs, txn_id, subpool),
+                      svn_fs_x__path_p2l_proto_index(cb->fs, txn_id, subpool),
+                      new_rev, subpool));
+  svn_pool_clear(subpool);
 
-  rev_filename = svn_fs_x__path_rev(cb->fs, new_rev, scratch_pool);
-  proto_filename = svn_fs_x__path_txn_proto_rev(cb->fs, txn_id,
-                                                scratch_pool);
-  SVN_ERR(svn_fs_x__move_into_place(proto_filename, rev_filename,
-                                    old_rev_filename, scratch_pool));
-
-  /* Now that we've moved the prototype revision file out of the way,
-     we can unlock it (since further attempts to write to the file
-     will fail as it no longer exists).  We must do this so that we can
-     remove the transaction directory later. */
-  SVN_ERR(unlock_proto_rev(cb->fs, txn_id, proto_file_lockcookie,
-                           scratch_pool));
+  /* Set the correct permissions. */
+  old_rev_filename = svn_fs_x__path_rev_absolute(cb->fs, old_rev, subpool);
+  rev_filename = svn_fs_x__path_rev(cb->fs, new_rev, subpool);
+  SVN_ERR(svn_io_copy_perms(rev_filename, old_rev_filename, subpool));
 
   /* Move the revprops file into place. */
   SVN_ERR_ASSERT(! svn_fs_x__is_packed_revprop(cb->fs, new_rev));
-  SVN_ERR(write_final_revprop(&revprop_filename, cb->txn, txn_id,
-                              scratch_pool));
-  final_revprop = svn_fs_x__path_revprops(cb->fs, new_rev, scratch_pool);
-  SVN_ERR(svn_fs_x__move_into_place(revprop_filename, final_revprop,
-                                    old_rev_filename, scratch_pool));
+  SVN_ERR(write_final_revprop(&revprop_filename, cb->txn, new_rev, batch,
+                              subpool, subpool));
+  SVN_ERR(svn_io_copy_perms(revprop_filename, old_rev_filename, subpool));
+  svn_pool_clear(subpool);
 
-  /* Update the 'current' file. */
+  /* Verify contents (no-op outside DEBUG mode). */
+  SVN_ERR(svn_io_file_flush(proto_file, subpool));
   SVN_ERR(verify_as_revision_before_current_plus_plus(cb->fs, new_rev,
-                                                      scratch_pool));
-  SVN_ERR(svn_fs_x__write_current(cb->fs, new_rev, scratch_pool));
+                                                      subpool));
+
+  /* Bump 'current' and 'txn-current'.
+     The latter is a piggy-back allocation of a new txn ID such that
+     reusing this FS for multiple commits does not involve additional
+     fsync latencies.  If that txn ID goes to waste, it's not a big loss
+     because we've got 18 quintillion of them ... */
+  bump_ids_baton.fs = cb->fs;
+  bump_ids_baton.new_rev = new_rev;
+  bump_ids_baton.batch = batch;
+  SVN_ERR(svn_fs_x__with_txn_current_lock(cb->fs, bump_ids, &bump_ids_baton,
+                                          subpool));
 
   /* At this point the new revision is committed and globally visible
      so let the caller know it succeeded by giving it the new revision
@@ -3481,8 +3652,9 @@ commit_body(void *baton,
   ffd->youngest_rev_cache = new_rev;
 
   /* Remove this transaction directory. */
-  SVN_ERR(svn_fs_x__purge_txn(cb->fs, cb->txn->id, scratch_pool));
+  SVN_ERR(svn_fs_x__purge_txn(cb->fs, cb->txn->id, subpool));
 
+  svn_pool_destroy(subpool);
   return SVN_NO_ERROR;
 }
 
@@ -3776,7 +3948,7 @@ svn_fs_x__begin_txn(svn_fs_txn_t **txn_p
                   svn_string_create("0", scratch_pool));
 
   ftd = (*txn_p)->fsap_data;
-  SVN_ERR(set_txn_proplist(fs, ftd->txn_id, props, FALSE, scratch_pool));
+  SVN_ERR(set_txn_proplist(fs, ftd->txn_id, props, scratch_pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/patch-exec/subversion/libsvn_fs_x/transaction.h
URL: http://svn.apache.org/viewvc/subversion/branches/patch-exec/subversion/libsvn_fs_x/transaction.h?rev=1696514&r1=1696513&r2=1696514&view=diff
==============================================================================
--- subversion/branches/patch-exec/subversion/libsvn_fs_x/transaction.h (original)
+++ subversion/branches/patch-exec/subversion/libsvn_fs_x/transaction.h Wed Aug 19 02:35:12 2015
@@ -20,8 +20,8 @@
  * ====================================================================
  */
 
-#ifndef SVN_LIBSVN_FS__TRANSACTION_H
-#define SVN_LIBSVN_FS__TRANSACTION_H
+#ifndef SVN_LIBSVN_FS_X_TRANSACTION_H
+#define SVN_LIBSVN_FS_X_TRANSACTION_H
 
 #include "fs.h"
 
@@ -128,7 +128,7 @@ svn_fs_x__reserve_copy_id(svn_fs_x__id_t
 /* Create an entirely new mutable node in the filesystem FS, whose
    node-revision is NODEREV.  COPY_ID is the copy_id to use in the
    node revision ID.  TXN_ID is the Subversion transaction  under
-   which this occurs. */
+   which this occurs.  Use SCRATCH_POOL for temporary allocations. */
 svn_error_t *
 svn_fs_x__create_node(svn_fs_t *fs,
                       svn_fs_x__noderev_t *noderev,