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,