Return-Path: X-Original-To: apmail-subversion-commits-archive@minotaur.apache.org Delivered-To: apmail-subversion-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id C000718275 for ; Thu, 13 Aug 2015 15:54:58 +0000 (UTC) Received: (qmail 15701 invoked by uid 500); 13 Aug 2015 15:54:58 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 15604 invoked by uid 500); 13 Aug 2015 15:54:58 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 15242 invoked by uid 99); 13 Aug 2015 15:54:58 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 13 Aug 2015 15:54:58 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id 50F24AC1A87 for ; Thu, 13 Aug 2015 15:54:58 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1695730 [6/9] - in /subversion/branches/move-tracking-2: ./ build/ build/ac-macros/ notes/ subversion/ subversion/bindings/javahl/native/ subversion/bindings/swig/ subversion/include/ subversion/include/private/ subversion/libsvn_client/ s... Date: Thu, 13 Aug 2015 15:54:56 -0000 To: commits@subversion.apache.org From: julianfoad@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150813155458.50F24AC1A87@hades.apache.org> Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c?rev=1695730&r1=1695729&r2=1695730&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c Thu Aug 13 15:54:54 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/move-tracking-2/subversion/libsvn_fs_x/transaction.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.h?rev=1695730&r1=1695729&r2=1695730&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.h Thu Aug 13 15:54:54 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,