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 DDAA510521 for ; Sat, 3 Jan 2015 14:00:54 +0000 (UTC) Received: (qmail 85224 invoked by uid 500); 3 Jan 2015 14:00:55 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 85077 invoked by uid 500); 3 Jan 2015 14:00:55 -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 84930 invoked by uid 99); 3 Jan 2015 14:00:55 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 03 Jan 2015 14:00:55 +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 31B6AAC095B; Sat, 3 Jan 2015 14:00:52 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1649205 [4/30] - in /subversion/branches/authzperf: ./ build/ build/ac-macros/ notes/ subversion/bindings/ctypes-python/ subversion/bindings/cxxhl/ subversion/bindings/javahl/tests/org/apache/subversion/javahl/ subversion/bindings/swig/ su... Date: Sat, 03 Jan 2015 14:00:44 -0000 To: commits@subversion.apache.org From: brane@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150103140052.31B6AAC095B@hades.apache.org> Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/cached_data.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/cached_data.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/cached_data.c (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/cached_data.c Sat Jan 3 14:00:41 2015 @@ -74,7 +74,7 @@ dbg_log_access(svn_fs_t *fs, svn_revnum_t revision, apr_uint64_t item_index, void *item, - int item_type, + apr_uint32_t item_type, apr_pool_t *scratch_pool) { /* no-op if this macro is not defined */ @@ -154,7 +154,7 @@ dbg_log_access(svn_fs_t *fs, } /* some info is only available in format7 repos */ - if (svn_fs_fs__use_log_addressing(fs, revision)) + if (svn_fs_fs__use_log_addressing(fs)) { /* reverse index lookup: get item description in ENTRY */ SVN_ERR(svn_fs_fs__p2l_entry_lookup(&entry, fs, rev_file, revision, @@ -277,15 +277,13 @@ err_dangling_id(svn_fs_t *fs, const svn_ id_str->data, fs->path); } -/* Return TRUE, if REVISION in FS is of a format that supports block-read - and the feature has been enabled. */ +/* Return TRUE, if FS is of a format that supports block-read and the + feature has been enabled. */ static svn_boolean_t -use_block_read(svn_fs_t *fs, - svn_revnum_t revision) +use_block_read(svn_fs_t *fs) { fs_fs_data_t *ffd = fs->fsap_data; - return svn_fs_fs__use_log_addressing(fs, revision) - && ffd->use_block_read; + return svn_fs_fs__use_log_addressing(fs) && ffd->use_block_read; } /* Get the node-revision for the node ID in FS. @@ -360,7 +358,7 @@ get_node_revision_body(node_revision_t * rev_item->number, scratch_pool)); - if (use_block_read(fs, rev_item->revision)) + if (use_block_read(fs)) { /* block-read will parse the whole block and will also return the one noderev that we need right now. */ @@ -552,7 +550,7 @@ svn_fs_fs__rev_get_root(svn_fs_id_t **ro fs_fs_data_t *ffd = fs->fsap_data; SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, scratch_pool)); - if (svn_fs_fs__use_log_addressing(fs, rev)) + if (svn_fs_fs__use_log_addressing(fs)) { *root_id_p = svn_fs_fs__id_create_root(rev, result_pool); } @@ -840,7 +838,7 @@ create_rep_state_body(rep_state_t **rep_ /* populate the cache if appropriate */ if (! svn_fs_fs__id_txn_used(&rep->txn_id)) { - if (use_block_read(fs, rep->revision)) + if (use_block_read(fs)) SVN_ERR(block_read(NULL, fs, rep->revision, rep->item_index, rs->sfile->rfile, result_pool, scratch_pool)); else @@ -922,14 +920,24 @@ svn_fs_fs__check_rep(representation_t *r void **hint, apr_pool_t *scratch_pool) { - if (svn_fs_fs__use_log_addressing(fs, rep->revision)) + if (svn_fs_fs__use_log_addressing(fs)) { apr_off_t offset; svn_fs_fs__p2l_entry_t *entry; + svn_fs_fs__revision_file_t *rev_file = NULL; - svn_fs_fs__revision_file_t *rev_file; - SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, rep->revision, - scratch_pool, scratch_pool)); + /* Reuse the revision file provided by *HINT, if it is given and + * actually the rev / pack file that we want. */ + svn_revnum_t start_rev = svn_fs_fs__packed_base_rev(fs, rep->revision); + if (hint) + rev_file = *(svn_fs_fs__revision_file_t **)hint; + + if (rev_file == NULL || rev_file->start_revision != start_rev) + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, rep->revision, + scratch_pool, scratch_pool)); + + if (hint) + *hint = rev_file; /* This will auto-retry if there was a background pack. */ SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev_file, rep->revision, @@ -937,8 +945,9 @@ svn_fs_fs__check_rep(representation_t *r /* This may fail if there is a background pack operation (can't auto- retry because the item offset lookup has to be redone as well). */ - SVN_ERR(svn_fs_fs__p2l_entry_lookup(&entry, fs, rev_file, rep->revision, - offset, scratch_pool)); + SVN_ERR(svn_fs_fs__p2l_entry_lookup(&entry, fs, rev_file, + rep->revision, offset, + scratch_pool, scratch_pool)); if ( entry == NULL || entry->type < SVN_FS_FS__ITEM_TYPE_FILE_REP @@ -951,8 +960,6 @@ svn_fs_fs__check_rep(representation_t *r "%" APR_UINT64_T_FMT, rep->item_index), rep->revision); - - SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); } else { @@ -978,7 +985,7 @@ svn_fs_fs__rep_chain_length(int *chain_l svn_revnum_t shard_size = ffd->max_files_per_dir ? ffd->max_files_per_dir : 1; - apr_pool_t *sub_pool = svn_pool_create(scratch_pool); + apr_pool_t *subpool = svn_pool_create(scratch_pool); apr_pool_t *iterpool = svn_pool_create(scratch_pool); svn_boolean_t is_delta = FALSE; int count = 0; @@ -1014,7 +1021,7 @@ svn_fs_fs__rep_chain_length(int *chain_l &file_hint, &base_rep, fs, - sub_pool, + subpool, iterpool)); base_rep.revision = header->base_revision; @@ -1023,18 +1030,28 @@ svn_fs_fs__rep_chain_length(int *chain_l svn_fs_fs__id_txn_reset(&base_rep.txn_id); is_delta = header->type == svn_fs_fs__rep_delta; + /* Clear it the SUBPOOL once in a while. Doing it too frequently + * renders the FILE_HINT ineffective. Doing too infrequently, may + * leave us with too many open file handles. + * + * Note that this is mostly about efficiency, with larger values + * being more efficient, and any non-zero value is legal here. When + * reading deltified contents, we may keep 10s of rev files open at + * the same time and the system has to cope with that. Thus, the + * limit of 16 chosen below is in the same ballpark. + */ ++count; if (count % 16 == 0) { file_hint = NULL; - svn_pool_clear(sub_pool); + svn_pool_clear(subpool); } } while (is_delta && base_rep.revision); *chain_length = count; *shard_count = shards; - svn_pool_destroy(sub_pool); + svn_pool_destroy(subpool); svn_pool_destroy(iterpool); return SVN_NO_ERROR; @@ -1466,7 +1483,7 @@ read_delta_window(svn_txdelta_window_t * because the block is unlikely to contain other data. */ if ( rs->chunk_index == 0 && SVN_IS_VALID_REVNUM(rs->revision) - && use_block_read(rs->sfile->fs, rs->revision) + && use_block_read(rs->sfile->fs) && rs->raw_window_cache) { SVN_ERR(block_read(NULL, rs->sfile->fs, rs->revision, rs->item_index, @@ -2393,7 +2410,7 @@ read_dir_entries(apr_array_header_t *ent _("Directory entry corrupt in '%s'"), svn_fs_fs__id_unparse(id, scratch_pool)->data); - dirent->id = svn_fs_fs__id_parse(str, result_pool); + SVN_ERR(svn_fs_fs__id_parse(&dirent->id, str, result_pool)); /* In incremental mode, update the hash; otherwise, write to the * final array. Be sure to use hash keys that survive this iteration. @@ -2699,7 +2716,7 @@ svn_fs_fs__get_changes(apr_array_header_ SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev, scratch_pool, scratch_pool)); - if (use_block_read(fs, rev)) + if (use_block_read(fs)) { /* 'block-read' will also provide us with the desired data */ SVN_ERR(block_read((void **)changes, fs, @@ -2710,7 +2727,7 @@ svn_fs_fs__get_changes(apr_array_header_ { /* Addressing is very different for old formats * (needs to read the revision trailer). */ - if (svn_fs_fs__use_log_addressing(fs, rev)) + if (svn_fs_fs__use_log_addressing(fs)) SVN_ERR(svn_fs_fs__item_offset(&changes_offset, fs, revision_file, rev, NULL, SVN_FS_FS__ITEM_INDEX_CHANGES, @@ -3251,7 +3268,8 @@ block_read(void **result, block_start = offset - (offset % ffd->block_size); SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, revision_file, revision, block_start, - ffd->block_size, scratch_pool)); + ffd->block_size, scratch_pool, + scratch_pool)); SVN_ERR(aligned_seek(fs, revision_file->file, &block_start, offset, iterpool)); @@ -3286,7 +3304,7 @@ block_read(void **result, && entry->size < ffd->block_size)) { void *item = NULL; - SVN_ERR(svn_io_file_seek(revision_file->file, SEEK_SET, + SVN_ERR(svn_io_file_seek(revision_file->file, APR_SET, &entry->offset, iterpool)); switch (entry->type) { Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/caching.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/caching.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/caching.c (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/caching.c Sat Jan 3 14:00:41 2015 @@ -66,9 +66,8 @@ normalize_key_part(const char *original, return normalized->data; } -/* *CACHE_TXDELTAS, *CACHE_FULLTEXTS and *CACHE_REVPROPS flags will be set - according to FS->CONFIG. *CACHE_NAMESPACE receives the cache prefix - to use. +/* *CACHE_TXDELTAS, *CACHE_FULLTEXTS flags will be set according to + FS->CONFIG. *CACHE_NAMESPACE receives the cache prefix to use. Use FS->pool for allocating the memcache and CACHE_NAMESPACE, and POOL for temporary allocations. */ @@ -76,7 +75,6 @@ static svn_error_t * read_config(const char **cache_namespace, svn_boolean_t *cache_txdeltas, svn_boolean_t *cache_fulltexts, - svn_boolean_t *cache_revprops, svn_fs_t *fs, apr_pool_t *pool) { @@ -119,10 +117,6 @@ read_config(const char **cache_namespace SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, TRUE); - /* For now, always disable revprop caching. - */ - *cache_revprops = FALSE; - return SVN_NO_ERROR; } @@ -347,7 +341,6 @@ svn_fs_fs__initialize_caches(svn_fs_t *f fs_fs_data_t *ffd = fs->fsap_data; const char *prefix = apr_pstrcat(pool, "fsfs:", fs->uuid, - ":", ffd->instance_id, "/", normalize_key_part(fs->path, pool), ":", SVN_VA_NULL); @@ -355,14 +348,12 @@ svn_fs_fs__initialize_caches(svn_fs_t *f svn_boolean_t no_handler = ffd->fail_stop; svn_boolean_t cache_txdeltas; svn_boolean_t cache_fulltexts; - svn_boolean_t cache_revprops; const char *cache_namespace; /* Evaluating the cache configuration. */ SVN_ERR(read_config(&cache_namespace, &cache_txdeltas, &cache_fulltexts, - &cache_revprops, fs, pool)); @@ -568,28 +559,6 @@ svn_fs_fs__initialize_caches(svn_fs_t *f ffd->mergeinfo_existence_cache = NULL; } - /* if enabled, cache revprops */ - if (cache_revprops) - { - SVN_ERR(create_cache(&(ffd->revprop_cache), - NULL, - membuffer, - 0, 0, /* Do not use inprocess cache */ - svn_fs_fs__serialize_properties, - svn_fs_fs__deserialize_properties, - sizeof(pair_cache_key_t), - apr_pstrcat(pool, prefix, "REVPROP", - SVN_VA_NULL), - SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, - fs, - no_handler, - fs->pool, pool)); - } - else - { - ffd->revprop_cache = NULL; - } - /* if enabled, cache text deltas and their combinations */ if (cache_txdeltas) { @@ -641,70 +610,60 @@ svn_fs_fs__initialize_caches(svn_fs_t *f ffd->combined_window_cache = NULL; } - if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) - { - SVN_ERR(create_cache(&(ffd->l2p_header_cache), - NULL, - membuffer, - 64, 16, /* entry size varies but we must cover - a reasonable number of revisions (1k) */ - svn_fs_fs__serialize_l2p_header, - svn_fs_fs__deserialize_l2p_header, - sizeof(pair_cache_key_t), - apr_pstrcat(pool, prefix, "L2P_HEADER", - (char *)NULL), - SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, - fs, - no_handler, - fs->pool, pool)); - SVN_ERR(create_cache(&(ffd->l2p_page_cache), - NULL, - membuffer, - 64, 16, /* entry size varies but we must cover - a reasonable number of revisions (1k) */ - svn_fs_fs__serialize_l2p_page, - svn_fs_fs__deserialize_l2p_page, - sizeof(svn_fs_fs__page_cache_key_t), - apr_pstrcat(pool, prefix, "L2P_PAGE", - (char *)NULL), - SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, - fs, - no_handler, - fs->pool, pool)); - SVN_ERR(create_cache(&(ffd->p2l_header_cache), - NULL, - membuffer, - 4, 1, /* Large entries. Rarely used. */ - svn_fs_fs__serialize_p2l_header, - svn_fs_fs__deserialize_p2l_header, - sizeof(pair_cache_key_t), - apr_pstrcat(pool, prefix, "P2L_HEADER", - (char *)NULL), - SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, - fs, - no_handler, - fs->pool, pool)); - SVN_ERR(create_cache(&(ffd->p2l_page_cache), - NULL, - membuffer, - 4, 16, /* Variably sized entries. Rarely used. */ - svn_fs_fs__serialize_p2l_page, - svn_fs_fs__deserialize_p2l_page, - sizeof(svn_fs_fs__page_cache_key_t), - apr_pstrcat(pool, prefix, "P2L_PAGE", - (char *)NULL), - SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, - fs, - no_handler, - fs->pool, pool)); - } - else - { - ffd->l2p_header_cache = NULL; - ffd->l2p_page_cache = NULL; - ffd->p2l_header_cache = NULL; - ffd->p2l_page_cache = NULL; - } + SVN_ERR(create_cache(&(ffd->l2p_header_cache), + NULL, + membuffer, + 64, 16, /* entry size varies but we must cover + a reasonable number of revisions (1k) */ + svn_fs_fs__serialize_l2p_header, + svn_fs_fs__deserialize_l2p_header, + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "L2P_HEADER", + (char *)NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); + SVN_ERR(create_cache(&(ffd->l2p_page_cache), + NULL, + membuffer, + 64, 16, /* entry size varies but we must cover + a reasonable number of revisions (1k) */ + svn_fs_fs__serialize_l2p_page, + svn_fs_fs__deserialize_l2p_page, + sizeof(svn_fs_fs__page_cache_key_t), + apr_pstrcat(pool, prefix, "L2P_PAGE", + (char *)NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); + SVN_ERR(create_cache(&(ffd->p2l_header_cache), + NULL, + membuffer, + 4, 1, /* Large entries. Rarely used. */ + svn_fs_fs__serialize_p2l_header, + svn_fs_fs__deserialize_p2l_header, + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "P2L_HEADER", + (char *)NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); + SVN_ERR(create_cache(&(ffd->p2l_page_cache), + NULL, + membuffer, + 4, 16, /* Variably sized entries. Rarely used. */ + svn_fs_fs__serialize_p2l_page, + svn_fs_fs__deserialize_p2l_page, + sizeof(svn_fs_fs__page_cache_key_t), + apr_pstrcat(pool, prefix, "P2L_PAGE", + (char *)NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); return SVN_NO_ERROR; } @@ -717,12 +676,44 @@ struct txn_cleanup_baton_t /* the position where to reset it */ svn_cache__t **to_reset; + + /* pool that TXN_CACHE was allocated in */ + apr_pool_t *txn_pool; + + /* pool that the FS containing the TO_RESET pointer was allocator */ + apr_pool_t *fs_pool; }; +/* Forward declaration. */ +static apr_status_t +remove_txn_cache_fs(void *baton_void); + /* APR pool cleanup handler that will reset the cache pointer given in - BATON_VOID. */ + BATON_VOID when the TXN_POOL gets cleaned up. */ static apr_status_t -remove_txn_cache(void *baton_void) +remove_txn_cache_txn(void *baton_void) +{ + struct txn_cleanup_baton_t *baton = baton_void; + + /* be careful not to hurt performance by resetting newer txn's caches. */ + if (*baton->to_reset == baton->txn_cache) + { + /* This is equivalent to calling svn_fs_fs__reset_txn_caches(). */ + *baton->to_reset = NULL; + } + + /* It's cleaned up now. Prevent double cleanup. */ + apr_pool_cleanup_kill(baton->fs_pool, + baton, + remove_txn_cache_fs); + + return APR_SUCCESS; +} + +/* APR pool cleanup handler that will reset the cache pointer given in + BATON_VOID when the FS_POOL gets cleaned up. */ +static apr_status_t +remove_txn_cache_fs(void *baton_void) { struct txn_cleanup_baton_t *baton = baton_void; @@ -730,19 +721,25 @@ remove_txn_cache(void *baton_void) if (*baton->to_reset == baton->txn_cache) { /* This is equivalent to calling svn_fs_fs__reset_txn_caches(). */ - *baton->to_reset = NULL; + *baton->to_reset = NULL; } + /* It's cleaned up now. Prevent double cleanup. */ + apr_pool_cleanup_kill(baton->txn_pool, + baton, + remove_txn_cache_txn); + return APR_SUCCESS; } /* This function sets / registers the required callbacks for a given - * transaction-specific *CACHE object, if CACHE is not NULL and a no-op - * otherwise. In particular, it will ensure that *CACHE gets reset to NULL - * upon POOL destruction latest. + * transaction-specific *CACHE object in FS, if CACHE is not NULL and + * a no-op otherwise. In particular, it will ensure that *CACHE gets + * reset to NULL upon POOL or FS->POOL destruction latest. */ static void -init_txn_callbacks(svn_cache__t **cache, +init_txn_callbacks(svn_fs_t *fs, + svn_cache__t **cache, apr_pool_t *pool) { if (*cache != NULL) @@ -752,10 +749,20 @@ init_txn_callbacks(svn_cache__t **cache, baton = apr_palloc(pool, sizeof(*baton)); baton->txn_cache = *cache; baton->to_reset = cache; + baton->txn_pool = pool; + baton->fs_pool = fs->pool; + /* If any of these pools gets cleaned, we must reset the cache. + * We don't know which one will get cleaned up first, so register + * cleanup actions for both and during the cleanup action, unregister + * the respective other action. */ apr_pool_cleanup_register(pool, baton, - remove_txn_cache, + remove_txn_cache_txn, + apr_pool_cleanup_null); + apr_pool_cleanup_register(fs->pool, + baton, + remove_txn_cache_fs, apr_pool_cleanup_null); } } @@ -774,7 +781,6 @@ svn_fs_fs__initialize_txn_caches(svn_fs_ Therefore, throw in a uuid as well - just to be sure. */ const char *prefix = apr_pstrcat(pool, "fsfs:", fs->uuid, - ":", ffd->instance_id, "/", fs->path, ":", txn_id, ":", svn_uuid_generate(pool), ":", @@ -806,7 +812,7 @@ svn_fs_fs__initialize_txn_caches(svn_fs_ pool, pool)); /* reset the transaction-specific cache if the pool gets cleaned up. */ - init_txn_callbacks(&(ffd->txn_dir_cache), pool); + init_txn_callbacks(fs, &(ffd->txn_dir_cache), pool); return SVN_NO_ERROR; } Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/dag.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/dag.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/dag.c (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/dag.c Sat Jan 3 14:00:41 2015 @@ -866,14 +866,20 @@ svn_fs_fs__dag_delete_if_mutable(svn_fs_ { apr_array_header_t *entries; int i; + apr_pool_t *iterpool = svn_pool_create(pool); /* Loop over directory entries */ SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool)); if (entries) for (i = 0; i < entries->nelts; ++i) - SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, - APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id, - pool)); + { + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, + APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id, + iterpool)); + } + + svn_pool_destroy(iterpool); } /* ... then delete the node itself, after deleting any mutable @@ -1099,6 +1105,15 @@ svn_fs_fs__dag_dup(const dag_node_t *nod return new_node; } +dag_node_t * +svn_fs_fs__dag_copy_into_pool(dag_node_t *node, + apr_pool_t *pool) +{ + return (node->node_pool == pool + ? node + : svn_fs_fs__dag_dup(node, pool)); +} + svn_error_t * svn_fs_fs__dag_serialize(void **data, apr_size_t *data_len, Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/dag.h URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/dag.h?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/dag.h (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/dag.h Sat Jan 3 14:00:41 2015 @@ -80,6 +80,12 @@ dag_node_t * svn_fs_fs__dag_dup(const dag_node_t *node, apr_pool_t *pool); +/* If NODE has been allocated in POOL, return NODE. Otherwise, return + a copy created in POOL with svn_fs_fs__dag_dup. */ +dag_node_t * +svn_fs_fs__dag_copy_into_pool(dag_node_t *node, + apr_pool_t *pool); + /* Serialize a DAG node, except don't try to preserve the 'fs' member. Implements svn_cache__serialize_func_t */ svn_error_t * Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/fs.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/fs.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/fs.c (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/fs.c Sat Jan 3 14:00:41 2015 @@ -44,6 +44,7 @@ #include "rep-cache.h" #include "revprops.h" #include "transaction.h" +#include "util.h" #include "verify.h" #include "svn_private_config.h" #include "private/svn_fs_util.h" @@ -213,6 +214,7 @@ fs_info(const void **fsfs_info, info->fs_type = SVN_FS_TYPE_FSFS; info->shard_size = ffd->max_files_per_dir; info->min_unpacked_rev = ffd->min_unpacked_rev; + info->log_addressing = ffd->use_log_addressing; *fsfs_info = info; return SVN_NO_ERROR; } @@ -265,7 +267,7 @@ static svn_error_t * initialize_fs_struct(svn_fs_t *fs) { fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd)); - ffd->min_log_addressing_rev = SVN_INVALID_REVNUM; + ffd->use_log_addressing = FALSE; fs->vtable = &fs_vtable; fs->fsap_data = ffd; @@ -348,20 +350,53 @@ fs_open_for_recovery(svn_fs_t *fs, apr_pool_t *pool, apr_pool_t *common_pool) { + svn_error_t * err; + svn_revnum_t youngest_rev; + apr_pool_t * subpool = svn_pool_create(pool); + /* Recovery for FSFS is currently limited to recreating the 'current' file from the latest revision. */ /* The only thing we have to watch out for is that the 'current' file - might not exist. So we'll try to create it here unconditionally, - and just ignore any errors that might indicate that it's already - present. (We'll need it to exist later anyway as a source for the - new file's permissions). */ + might not exist or contain garbage. So we'll try to read it here + and provide or replace the existing file if we couldn't read it. + (We'll also need it to exist later anyway as a source for the new + file's permissions). */ - /* Use a partly-filled fs pointer first to create 'current'. This will fail - if 'current' already exists, but we don't care about that. */ + /* Use a partly-filled fs pointer first to create 'current'. */ fs->path = apr_pstrdup(fs->pool, path); - svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool), - "0 1 1\n", pool)); + + SVN_ERR(initialize_fs_struct(fs)); + + /* Figure out the repo format and check that we can even handle it. */ + SVN_ERR(svn_fs_fs__read_format_file(fs, subpool)); + + /* Now, read 'current' and try to patch it if necessary. */ + err = svn_fs_fs__youngest_rev(&youngest_rev, fs, subpool); + if (err) + { + const char *file_path; + + /* 'current' file is missing or contains garbage. Since we are trying + * to recover from whatever problem there is, being picky about the + * error code here won't do us much good. If there is a persistent + * problem that we can't fix, it will show up when we try rewrite the + * file a few lines further below and we will report the failure back + * to the caller. + * + * Start recovery with HEAD = 0. */ + svn_error_clear(err); + file_path = svn_fs_fs__path_current(fs, subpool); + + /* Best effort to ensure the file exists and is valid. + * This may fail for r/o filesystems etc. */ + SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool)); + SVN_ERR(svn_io_file_create_empty(file_path, subpool)); + SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, subpool)); + } + + uninitialize_fs_struct(fs); + svn_pool_destroy(subpool); /* Now open the filesystem properly by calling the vtable method directly. */ return fs_open(fs, path, common_pool_lock, pool, common_pool); @@ -496,7 +531,7 @@ fs_delete_fs(const char *path, apr_pool_t *pool) { /* Remove everything. */ - return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); + return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool)); } static const svn_version_t * Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/fs.h URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/fs.h?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/fs.h (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/fs.h Sat Jan 3 14:00:41 2015 @@ -36,7 +36,6 @@ #include "private/svn_fs_private.h" #include "private/svn_sqlite.h" #include "private/svn_mutex.h" -#include "private/svn_named_atomic.h" #include "id.h" @@ -58,7 +57,8 @@ extern "C" { #define PATH_PACK_LOCK_FILE "pack-lock" /* Pack lock file */ #define PATH_REVS_DIR "revs" /* Directory of revisions */ #define PATH_REVPROPS_DIR "revprops" /* Directory of revprops */ -#define PATH_TXNS_DIR "transactions" /* Directory of transactions */ +#define PATH_TXNS_DIR "transactions" /* Directory of transactions in + repos w/o log addressing */ #define PATH_NODE_ORIGINS_DIR "node-origins" /* Lazy node-origin cache */ #define PATH_TXN_PROTOS_DIR "txn-protorevs" /* Directory of proto-revs */ #define PATH_TXN_CURRENT "txn-current" /* File with next txn key */ @@ -177,7 +177,7 @@ extern "C" { #define SVN_FS_FS__MIN_PACK_LOCK_FORMAT 7 /* Minimum format number that stores mergeinfo-mode flag in changed paths */ -#define SVN_FS_FS__MIN_MERGEINFO_IN_CHANGES_FORMAT 7 +#define SVN_FS_FS__MIN_MERGEINFO_IN_CHANGED_FORMAT 7 /* Minimum format number that supports per-instance filesystem IDs. */ #define SVN_FS_FS__MIN_INSTANCE_ID_FORMAT 7 @@ -189,7 +189,6 @@ extern "C" { per file. On Windows apr implements the locking as per file handle locks, so we don't have to add our own mutex for just in-process synchronization. */ -/* Compare ../libsvn_subr/named_atomic.c:USE_THREAD_MUTEX */ #if APR_HAS_THREADS && !defined(WIN32) #define SVN_FS_FS__USE_LOCK_MUTEX 1 #else @@ -303,11 +302,9 @@ typedef struct fs_fs_data_t layouts) or zero (for linear layouts). */ int max_files_per_dir; - /* The first revision that uses logical addressing. SVN_INVALID_REVNUM - if there is no such revision (pre-f7 or non-sharded). May be a - future revision if the current shard started with physical addressing - and is not complete, yet. */ - svn_revnum_t min_log_addressing_rev; + /* If set, this FS is using logical addressing. Otherwise, it is using + physical addressing. */ + svn_boolean_t use_log_addressing; /* Rev / pack file read granularity in bytes. */ apr_int64_t block_size; @@ -356,21 +353,6 @@ typedef struct fs_fs_data_t rep key (revision/offset) to svn_stringbuf_t. */ svn_cache__t *fulltext_cache; - /* Access object to the atomics namespace used by revprop caching. - Will be NULL until the first access. */ - svn_atomic_namespace__t *revprop_namespace; - - /* Access object to the revprop "generation". Will be NULL until - the first access. */ - svn_named_atomic__t *revprop_generation; - - /* Access object to the revprop update timeout. Will be NULL until - the first access. */ - svn_named_atomic__t *revprop_timeout; - - /* Revision property cache. Maps from (rev,generation) to apr_hash_t. */ - svn_cache__t *revprop_cache; - /* Node properties cache. Maps from rep key to apr_hash_t. */ svn_cache__t *properties_cache; Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/fs_fs.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/fs_fs.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/fs_fs.c (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/fs_fs.c Sat Jan 3 14:00:41 2015 @@ -86,7 +86,7 @@ are likely some errors because of that. /* Declarations. */ static svn_error_t * -get_youngest(svn_revnum_t *youngest_p, const char *fs_path, apr_pool_t *pool); +get_youngest(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool); /* Pathname helper functions */ @@ -213,7 +213,7 @@ with_some_lock_file(with_lock_baton_t *b if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) err = svn_fs_fs__update_min_unpacked_rev(fs, pool); if (!err) - err = get_youngest(&ffd->youngest_rev_cache, fs->path, pool); + err = get_youngest(&ffd->youngest_rev_cache, fs, pool); } if (!err) @@ -457,18 +457,18 @@ check_format(int format) /* Read the format number and maximum number of files per directory from PATH and return them in *PFORMAT, *MAX_FILES_PER_DIR and - MIN_LOG_ADDRESSING_REV respectively. + USE_LOG_ADDRESSIONG respectively. *MAX_FILES_PER_DIR is obtained from the 'layout' format option, and will be set to zero if a linear scheme should be used. - *MIN_LOG_ADDRESSING_REV is obtained from the 'addressing' format option, - and will be set to SVN_INVALID_REVNUM for physical addressing. + *USE_LOG_ADDRESSIONG is obtained from the 'addressing' format option, + and will be set to FALSE for physical addressing. Use POOL for temporary allocation. */ static svn_error_t * read_format(int *pformat, int *max_files_per_dir, - svn_revnum_t *min_log_addressing_rev, + svn_boolean_t *use_log_addressing, const char *path, apr_pool_t *pool) { @@ -515,7 +515,7 @@ read_format(int *pformat, /* Set the default values for anything that can be set via an option. */ *max_files_per_dir = 0; - *min_log_addressing_rev = SVN_INVALID_REVNUM; + *use_log_addressing = FALSE; /* Read any options. */ while (!eos) @@ -547,18 +547,13 @@ read_format(int *pformat, { if (strcmp(buf->data + 11, "physical") == 0) { - *min_log_addressing_rev = SVN_INVALID_REVNUM; + *use_log_addressing = FALSE; continue; } - if (strncmp(buf->data + 11, "logical ", 8) == 0) + if (strcmp(buf->data + 11, "logical") == 0) { - int value; - - /* Check that the argument is numeric. */ - SVN_ERR(check_format_file_buffer_numeric(buf->data, 19, path, pool)); - SVN_ERR(svn_cstring_atoi(&value, buf->data + 19)); - *min_log_addressing_rev = value; + *use_log_addressing = TRUE; continue; } } @@ -572,7 +567,7 @@ read_format(int *pformat, * If the format file is inconsistent in that respect, something * probably went wrong. */ - if (*min_log_addressing_rev != SVN_INVALID_REVNUM && !*max_files_per_dir) + if (*use_log_addressing && !*max_files_per_dir) return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, _("'%s' specifies logical addressing for a non-sharded repository"), svn_dirent_local_style(path, pool)); @@ -610,13 +605,10 @@ svn_fs_fs__write_format(svn_fs_t *fs, if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) { - if (ffd->min_log_addressing_rev == SVN_INVALID_REVNUM) - svn_stringbuf_appendcstr(sb, "addressing physical\n"); + if (ffd->use_log_addressing) + svn_stringbuf_appendcstr(sb, "addressing logical\n"); else - svn_stringbuf_appendcstr(sb, - apr_psprintf(pool, - "addressing logical %ld\n", - ffd->min_log_addressing_rev)); + svn_stringbuf_appendcstr(sb, "addressing physical\n"); } /* svn_io_write_version_file() does a load of magic to allow it to @@ -644,6 +636,50 @@ svn_fs_fs__fs_supports_mergeinfo(svn_fs_ return ffd->format >= SVN_FS_FS__MIN_MERGEINFO_FORMAT; } +/* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within + * the range of what the current system may address in RAM and it is a + * power of 2. Assume that the element size within the block is ITEM_SIZE. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +verify_block_size(apr_int64_t block_size, + apr_size_t item_size, + const char *name, + apr_pool_t *scratch_pool + ) +{ + /* Limit range. */ + if (block_size <= 0) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is too small for fsfs.conf setting '%s'."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + if (block_size > SVN_MAX_OBJECT_SIZE / item_size) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is too large for fsfs.conf setting '%s'."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + /* Ensure it is a power of two. + * For positive X, X & (X-1) will reset the lowest bit set. + * If the result is 0, at most one bit has been set. */ + if (0 != (block_size & (block_size - 1))) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is invalid for fsfs.conf setting '%s' " + "because it is not a power of 2."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + return SVN_NO_ERROR; +} + /* Read the configuration information of the file system at FS_PATH * and set the respective values in FFD. Use pools as usual. */ @@ -717,8 +753,8 @@ read_config(fs_fs_data_t *ffd, CONFIG_SECTION_PACKED_REVPROPS, CONFIG_OPTION_REVPROP_PACK_SIZE, ffd->compress_packed_revprops - ? 0x100 - : 0x40)); + ? 0x10 + : 0x4)); ffd->revprop_pack_size *= 1024; } @@ -743,6 +779,16 @@ read_config(fs_fs_data_t *ffd, CONFIG_OPTION_P2L_PAGE_SIZE, 0x400)); + /* Don't accept unreasonable or illegal values. + * Block size and P2L page size are in kbytes; + * L2P blocks are arrays of apr_off_t. */ + SVN_ERR(verify_block_size(ffd->block_size, 0x400, + CONFIG_OPTION_BLOCK_SIZE, scratch_pool)); + SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400, + CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool)); + SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t), + CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool)); + /* convert kBytes to bytes */ ffd->block_size *= 0x400; ffd->p2l_page_size *= 0x400; @@ -912,20 +958,16 @@ write_config(svn_fs_t *fs, "### much larger than the limit set here. The threshold will be applied" NL "### before optional compression takes place." NL "### Large values will reduce disk space usage at the expense of increased" NL -"### latency and CPU usage reading and changing individual revprops. They" NL -"### become an advantage when revprop caching has been enabled because a" NL -"### lot of data can be read in one go. Values smaller than 4 kByte will" NL -"### not improve latency any further and quickly render revprop packing" NL -"### ineffective." NL -"### revprop-pack-size is 64 kBytes by default for non-compressed revprop" NL -"### pack files and 256 kBytes when compression has been enabled." NL -"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 64" NL +"### latency and CPU usage reading and changing individual revprops." NL +"### Values smaller than 4 kByte will not improve latency any further and " NL +"### quickly render revprop packing ineffective." NL +"### revprop-pack-size is 4 kBytes by default for non-compressed revprop" NL +"### pack files and 16 kBytes when compression has been enabled." NL +"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 4" NL "###" NL "### To save disk space, packed revprop files may be compressed. Standard" NL "### revprops tend to allow for very effective compression. Reading and" NL -"### even more so writing, become significantly more CPU intensive. With" NL -"### revprop caching enabled, the overhead can be offset by reduced I/O" NL -"### unless you often modify revprops after packing." NL +"### even more so writing, become significantly more CPU intensive." NL "### Compressing packed revprops is disabled by default." NL "# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = false" NL "" NL @@ -949,19 +991,14 @@ write_config(svn_fs_t *fs, "# " CONFIG_OPTION_BLOCK_SIZE " = 64" NL "###" NL "### The log-to-phys index maps data item numbers to offsets within the" NL -"### rev or pack file. A revision typically contains 2 .. 5 such items" NL -"### per changed path. For each revision, at least one page is being" NL -"### allocated in the l2p index with unused parts resulting in no wasted" NL -"### space." NL -"### Changing this parameter only affects larger revisions with thousands" NL -"### of changed paths. A smaller value means that more pages need to be" NL -"### allocated for such revisions, increasing the size of the page table" NL -"### meaning it takes longer to read that table (once). Access to each" NL -"### page is then faster because less data has to read. So, if you have" NL -"### several extremely large revisions (approaching 1 mio changes), think" NL +"### rev or pack file. This index is organized in pages of a fixed maximum" NL +"### capacity. To access an item, the page table and the respective page" NL +"### must be read." NL +"### This parameter only affects revisions with thousands of changed paths." NL +"### If you have several extremely large revisions (~1 mio changes), think" NL "### about increasing this setting. Reducing the value will rarely result" NL "### in a net speedup." NL -"### This is an expert setting. Any non-zero value is possible." NL +"### This is an expert setting. Must be a power of 2." NL "### l2p-page-size is 8192 entries by default." NL "# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192" NL "###" NL @@ -992,8 +1029,14 @@ static svn_error_t * read_global_config(svn_fs_t *fs) { fs_fs_data_t *ffd = fs->fsap_data; - ffd->use_block_read - = svn_hash__get_bool(fs->config, SVN_FS_CONFIG_FSFS_BLOCK_READ, FALSE); + + /* Providing a config hash is optional. */ + if (fs->config) + ffd->use_block_read = svn_hash__get_bool(fs->config, + SVN_FS_CONFIG_FSFS_BLOCK_READ, + FALSE); + else + ffd->use_block_read = FALSE; /* Ignore the user-specified larger block size if we don't use block-read. Defaulting to 4k gives us the same access granularity in format 7 as in @@ -1042,22 +1085,32 @@ read_uuid(svn_fs_t *fs, } svn_error_t * -svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool) +svn_fs_fs__read_format_file(svn_fs_t *fs, apr_pool_t *scratch_pool) { fs_fs_data_t *ffd = fs->fsap_data; int format, max_files_per_dir; - svn_revnum_t min_log_addressing_rev; - - fs->path = apr_pstrdup(fs->pool, path); + svn_boolean_t use_log_addressing; - /* Read the FS format number. */ - SVN_ERR(read_format(&format, &max_files_per_dir, &min_log_addressing_rev, - path_format(fs, pool), pool)); + /* Read info from format file. */ + SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing, + path_format(fs, scratch_pool), scratch_pool)); - /* Now we've got a format number no matter what. */ + /* Now that we've got *all* info, store / update values in FFD. */ ffd->format = format; ffd->max_files_per_dir = max_files_per_dir; - ffd->min_log_addressing_rev = min_log_addressing_rev; + ffd->use_log_addressing = use_log_addressing; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + fs->path = apr_pstrdup(fs->pool, path); + + /* Read the FS format file. */ + SVN_ERR(svn_fs_fs__read_format_file(fs, pool)); /* Read in and cache the repository uuid. */ SVN_ERR(read_uuid(fs, pool)); @@ -1072,7 +1125,7 @@ svn_fs_fs__open(svn_fs_t *fs, const char /* Global configuration options. */ SVN_ERR(read_global_config(fs)); - return get_youngest(&(ffd->youngest_rev_cache), path, pool); + return get_youngest(&(ffd->youngest_rev_cache), fs, pool); } /* Wrapper around svn_io_file_create which ignores EEXIST. */ @@ -1108,13 +1161,13 @@ upgrade_body(void *baton, apr_pool_t *po svn_fs_t *fs = upgrade_baton->fs; fs_fs_data_t *ffd = fs->fsap_data; int format, max_files_per_dir; - svn_revnum_t min_log_addressing_rev; + svn_boolean_t use_log_addressing; const char *format_path = path_format(fs, pool); svn_node_kind_t kind; svn_boolean_t needs_revprop_shard_cleanup = FALSE; /* Read the FS format number and max-files-per-dir setting. */ - SVN_ERR(read_format(&format, &max_files_per_dir, &min_log_addressing_rev, + SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing, format_path, pool)); /* If the config file does not exist, create one. */ @@ -1155,10 +1208,8 @@ upgrade_body(void *baton, apr_pool_t *po dir, make that directory. */ if (format < SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) { - /* We don't use path_txn_proto_rev() here because it expects - we've already bumped our format. */ SVN_ERR(svn_io_make_dir_recursively( - svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool), pool)); + svn_fs_fs__path_txn_proto_revs(fs, pool), pool)); } /* If our filesystem is new enough, write the min unpacked rev file. */ @@ -1183,14 +1234,6 @@ upgrade_body(void *baton, apr_pool_t *po pool)); } - if ( format < SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT - && max_files_per_dir > 0) - { - min_log_addressing_rev - = (ffd->youngest_rev_cache / max_files_per_dir + 1) - * max_files_per_dir; - } - /* We will need the UUID info shortly ... Read it before the format bump as the UUID file still uses the old format. */ @@ -1200,7 +1243,7 @@ upgrade_body(void *baton, apr_pool_t *po down will use the format from FS to create missing info. */ ffd->format = SVN_FS_FS__FORMAT_NUMBER; ffd->max_files_per_dir = max_files_per_dir; - ffd->min_log_addressing_rev = min_log_addressing_rev; + ffd->use_log_addressing = use_log_addressing; /* Always add / bump the instance ID such that no form of caching accidentally uses outdated information. Keep the UUID. */ @@ -1208,6 +1251,7 @@ upgrade_body(void *baton, apr_pool_t *po /* Bump the format file. */ SVN_ERR(svn_fs_fs__write_format(fs, TRUE, pool)); + if (upgrade_baton->notify_func) SVN_ERR(upgrade_baton->notify_func(upgrade_baton->notify_baton, SVN_FS_FS__FORMAT_NUMBER, @@ -1251,17 +1295,11 @@ svn_fs_fs__upgrade(svn_fs_t *fs, POOL. */ static svn_error_t * get_youngest(svn_revnum_t *youngest_p, - const char *fs_path, + svn_fs_t *fs, apr_pool_t *pool) { - svn_stringbuf_t *buf; - SVN_ERR(svn_fs_fs__read_content(&buf, - svn_dirent_join(fs_path, PATH_CURRENT, - pool), - pool)); - - *youngest_p = SVN_STR_TO_REV(buf->data); - + apr_uint64_t dummy; + SVN_ERR(svn_fs_fs__read_current(youngest_p, &dummy, &dummy, fs, pool)); return SVN_NO_ERROR; } @@ -1273,12 +1311,33 @@ svn_fs_fs__youngest_rev(svn_revnum_t *yo { fs_fs_data_t *ffd = fs->fsap_data; - SVN_ERR(get_youngest(youngest_p, fs->path, pool)); + SVN_ERR(get_youngest(youngest_p, fs, pool)); ffd->youngest_rev_cache = *youngest_p; return SVN_NO_ERROR; } +int +svn_fs_fs__shard_size(svn_fs_t *fs) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + return ffd->max_files_per_dir; +} + +svn_error_t * +svn_fs_fs__min_unpacked_rev(svn_revnum_t *min_unpacked, + svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool)); + *min_unpacked = ffd->min_unpacked_rev; + + return SVN_NO_ERROR; +} + svn_error_t * svn_fs_fs__ensure_revision_exists(svn_revnum_t rev, svn_fs_t *fs, @@ -1296,7 +1355,7 @@ svn_fs_fs__ensure_revision_exists(svn_re if (rev <= ffd->youngest_rev_cache) return SVN_NO_ERROR; - SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs->path, pool)); + SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs, pool)); /* Check again. */ if (rev <= ffd->youngest_rev_cache) @@ -1370,10 +1429,15 @@ svn_fs_fs__file_text_rep_equal(svn_boole } /* Old repositories may not have the SHA1 checksum handy. - This check becomes expensive. Skip it unless explicitly required. */ + This check becomes expensive. Skip it unless explicitly required. + + We already have seen that the ID is different, so produce a likely + false negative as allowed by the API description - even though the + MD5 matched, there is an extremely slim chance that the SHA1 wouldn't. + */ if (!strict) { - *equal = TRUE; + *equal = FALSE; return SVN_NO_ERROR; } @@ -1505,7 +1569,7 @@ write_revision_zero(svn_fs_t *fs, svn_string_t date; /* Write out a rev file for revision 0. */ - if (svn_fs_fs__use_log_addressing(fs, 0)) + if (svn_fs_fs__use_log_addressing(fs)) { apr_array_header_t *index_entries; svn_fs_fs__p2l_entry_t *entry; @@ -1590,59 +1654,29 @@ write_revision_zero(svn_fs_t *fs, } svn_error_t * -svn_fs_fs__create(svn_fs_t *fs, - const char *path, - apr_pool_t *pool) +svn_fs_fs__create_file_tree(svn_fs_t *fs, + const char *path, + int format, + int shard_size, + svn_boolean_t use_log_addressing, + apr_pool_t *pool) { - int format = SVN_FS_FS__FORMAT_NUMBER; fs_fs_data_t *ffd = fs->fsap_data; fs->path = apr_pstrdup(fs->pool, path); - /* See if compatibility with older versions was explicitly requested. */ - if (fs->config) - { - svn_version_t *compatible_version; - SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config, - pool)); - - /* select format number */ - switch(compatible_version->minor) - { - case 0: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, - _("FSFS is not compatible with Subversion prior to 1.1")); - - case 1: - case 2: - case 3: format = 1; - break; - - case 4: format = 2; - break; - - case 5: format = 3; - break; - - case 6: - case 7: format = 4; - break; - - case 8: format = 6; - break; - - default:format = SVN_FS_FS__FORMAT_NUMBER; - } - } ffd->format = format; - /* Override the default linear layout if this is a new-enough format. */ + /* Use an appropriate sharding mode if supported by the format. */ if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT) - ffd->max_files_per_dir = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR; + ffd->max_files_per_dir = shard_size; + else + ffd->max_files_per_dir = 0; /* Select the addressing mode depending on the format. */ if (format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) - ffd->min_log_addressing_rev = 0; + ffd->use_log_addressing = use_log_addressing; else - ffd->min_log_addressing_rev = SVN_INVALID_REVNUM; + ffd->use_log_addressing = FALSE; /* Create the revision data directories. */ if (ffd->max_files_per_dir) @@ -1666,21 +1700,20 @@ svn_fs_fs__create(svn_fs_t *fs, pool)); /* Create the transaction directory. */ - SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_TXNS_DIR, - pool), + SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txns_dir(fs, pool), pool)); /* Create the protorevs directory. */ if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) - SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_TXN_PROTOS_DIR, - pool), + SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txn_proto_revs(fs, + pool), pool)); /* Create the 'current' file. */ - SVN_ERR(svn_io_file_create(svn_fs_fs__path_current(fs, pool), - (format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT - ? "0\n" : "0 1 1\n"), - pool)); + SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_current(fs, pool), pool)); + SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, pool)); + + /* Create the 'uuid' file. */ SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_lock(fs, pool), pool)); SVN_ERR(svn_fs_fs__set_uuid(fs, NULL, NULL, pool)); @@ -1715,10 +1748,76 @@ svn_fs_fs__create(svn_fs_t *fs, pool)); } + ffd->youngest_rev_cache = 0; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__create(svn_fs_t *fs, + const char *path, + apr_pool_t *pool) +{ + int format = SVN_FS_FS__FORMAT_NUMBER; + int shard_size = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR; + svn_boolean_t log_addressing; + + /* Process the given filesystem config. */ + if (fs->config) + { + svn_version_t *compatible_version; + const char *shard_size_str; + SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config, + pool)); + + /* select format number */ + switch(compatible_version->minor) + { + case 0: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, + _("FSFS is not compatible with Subversion prior to 1.1")); + + case 1: + case 2: + case 3: format = 1; + break; + + case 4: format = 2; + break; + + case 5: format = 3; + break; + + case 6: + case 7: format = 4; + break; + + case 8: format = 6; + break; + + default:format = SVN_FS_FS__FORMAT_NUMBER; + } + + shard_size_str = svn_hash_gets(fs->config, SVN_FS_CONFIG_FSFS_SHARD_SIZE); + if (shard_size_str) + { + apr_int64_t val; + SVN_ERR(svn_cstring_strtoi64(&val, shard_size_str, 0, + APR_INT32_MAX, 10)); + + shard_size = (int) val; + } + } + + log_addressing = svn_hash__get_bool(fs->config, + SVN_FS_CONFIG_FSFS_LOG_ADDRESSING, + TRUE); + + /* Actual FS creation. */ + SVN_ERR(svn_fs_fs__create_file_tree(fs, path, format, shard_size, + log_addressing, pool)); + /* This filesystem is ready. Stamp it with a format number. */ SVN_ERR(svn_fs_fs__write_format(fs, FALSE, pool)); - ffd->youngest_rev_cache = 0; return SVN_NO_ERROR; } @@ -1834,9 +1933,9 @@ svn_fs_fs__get_node_origin(const svn_fs_ = apr_hash_get(node_origins, node_id_ptr, len); if (origin_id_str) - *origin_id = svn_fs_fs__id_parse(apr_pstrdup(pool, - origin_id_str->data), - pool); + SVN_ERR(svn_fs_fs__id_parse(origin_id, + apr_pstrdup(pool, origin_id_str->data), + pool)); } return SVN_NO_ERROR; } Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/fs_fs.h URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/fs_fs.h?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/fs_fs.h (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/fs_fs.h Sat Jan 3 14:00:41 2015 @@ -25,6 +25,11 @@ #include "fs.h" +/* Read the 'format' file of fsfs filesystem FS and store its info in FS. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__read_format_file(svn_fs_t *fs, apr_pool_t *scratch_pool); + /* Open the fsfs filesystem pointed to by PATH and associate it with filesystem object FS. Use POOL for temporary allocations. @@ -51,6 +56,17 @@ svn_error_t *svn_fs_fs__youngest_rev(svn svn_fs_t *fs, apr_pool_t *pool); +/* Return the shard size of filesystem FS. Return 0 for non-shared ones. */ +int +svn_fs_fs__shard_size(svn_fs_t *fs); + +/* Set *MIN_UNPACKED to the oldest non-packed revision in filesystem FS. + Do any temporary allocation in POOL. */ +svn_error_t * +svn_fs_fs__min_unpacked_rev(svn_revnum_t *min_unpacked, + svn_fs_t *fs, + apr_pool_t *pool); + /* Return SVN_ERR_FS_NO_SUCH_REVISION if the given revision REV is newer than the current youngest revision in FS or is simply not a valid revision number, else return success. */ @@ -106,6 +122,25 @@ svn_error_t *svn_fs_fs__file_checksum(sv /* Return whether or not the given FS supports mergeinfo metadata. */ svn_boolean_t svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs); +/* Under the repository db PATH, create a FSFS repository with FORMAT, + * the given SHARD_SIZE. If USE_LOG_ADDRESSING is non-zero, repository + * will use logical addressing. If not supported by the respective format, + * the latter two parameters will be ignored. FS will be updated. + * + * The only file not being written is the 'format' file. This allows + * callers such as hotcopy to modify the contents before turning the + * tree into an accessible repository. + * + * Use POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__create_file_tree(svn_fs_t *fs, + const char *path, + int format, + int shard_size, + svn_boolean_t use_log_addressing, + apr_pool_t *pool); + /* Create a fs_fs fileysystem referenced by FS at path PATH. Get any temporary allocations from POOL. Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/hotcopy.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/hotcopy.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/hotcopy.c (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/hotcopy.c Sat Jan 3 14:00:41 2015 @@ -369,33 +369,30 @@ hotcopy_copy_packed_shard(svn_boolean_t return SVN_NO_ERROR; } -/* Remove FILE in SHARD folder. Use POOL for temporary allocations. */ +/* Remove file PATH, if it exists - even if it is read-only. + * Use POOL for temporary allocations. */ static svn_error_t * -hotcopy_remove_file(const char *shard, - const char *file, +hotcopy_remove_file(const char *path, apr_pool_t *pool) { - const char *rev_path = svn_dirent_join(shard, file, pool); - /* Make the rev file writable and remove it. */ - SVN_ERR(svn_io_set_file_read_write(rev_path, TRUE, pool)); - SVN_ERR(svn_io_remove_file2(rev_path, TRUE, pool)); + SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool)); + SVN_ERR(svn_io_remove_file2(path, TRUE, pool)); return SVN_NO_ERROR; } /* Remove revision or revprop files between START_REV (inclusive) and - * END_REV (non-inclusive) from folder DST_SUBDIR in DST_FS. Also, - * remove index files if REMOVE_INDEXES is set. Assume sharding as per - * MAX_FILES_PER_DIR. Use SCRATCH_POOL for temporary allocations. */ + * END_REV (non-inclusive) from folder DST_SUBDIR in DST_FS. Assume + * sharding as per MAX_FILES_PER_DIR. + * Use SCRATCH_POOL for temporary allocations. */ static svn_error_t * hotcopy_remove_files(svn_fs_t *dst_fs, const char *dst_subdir, svn_revnum_t start_rev, svn_revnum_t end_rev, int max_files_per_dir, - svn_boolean_t remove_indexes, apr_pool_t *scratch_pool) { const char *shard; @@ -420,18 +417,11 @@ hotcopy_remove_files(svn_fs_t *dst_fs, } /* remove files for REV */ - SVN_ERR(hotcopy_remove_file(dst_subdir_shard, - apr_psprintf(iterpool, "%ld", rev), + SVN_ERR(hotcopy_remove_file(svn_dirent_join(dst_subdir_shard, + apr_psprintf(iterpool, + "%ld", rev), + iterpool), iterpool)); - if (remove_indexes && svn_fs_fs__use_log_addressing(dst_fs, rev)) - { - SVN_ERR(hotcopy_remove_file(dst_subdir_shard, - apr_psprintf(iterpool, "%ld.p2l", rev), - iterpool)); - SVN_ERR(hotcopy_remove_file(dst_subdir_shard, - apr_psprintf(iterpool, "%ld.l2p", rev), - iterpool)); - } } svn_pool_destroy(iterpool); @@ -455,7 +445,7 @@ hotcopy_remove_rev_files(svn_fs_t *dst_f PATH_REVS_DIR, scratch_pool), start_rev, end_rev, - max_files_per_dir, TRUE, scratch_pool)); + max_files_per_dir, scratch_pool)); return SVN_NO_ERROR; } @@ -479,7 +469,7 @@ hotcopy_remove_revprop_files(svn_fs_t *d PATH_REVPROPS_DIR, scratch_pool), start_rev ? start_rev : 1, end_rev, - max_files_per_dir, FALSE, scratch_pool)); + max_files_per_dir, scratch_pool)); return SVN_NO_ERROR; } @@ -1003,17 +993,6 @@ hotcopy_body(void *baton, apr_pool_t *po SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path, PATH_TXN_CURRENT, pool)); - /* If a revprop generation file exists in the source filesystem, - * reset it to zero (since this is on a different path, it will not - * overlap with data already in cache). Also, clean up stale files - * used for the named atomics implementation. */ - SVN_ERR(svn_io_check_path(svn_fs_fs__path_revprop_generation(src_fs, pool), - &kind, pool)); - if (kind == svn_node_file) - SVN_ERR(svn_fs_fs__write_revprop_generation_file(dst_fs, 0, pool)); - - SVN_ERR(svn_fs_fs__cleanup_revprop_namespace(dst_fs)); - return SVN_NO_ERROR; } @@ -1042,76 +1021,26 @@ hotcopy_create_empty_dest(svn_fs_t *src_ apr_pool_t *pool) { fs_fs_data_t *src_ffd = src_fs->fsap_data; - fs_fs_data_t *dst_ffd = dst_fs->fsap_data; - dst_fs->path = apr_pstrdup(pool, dst_path); - - dst_ffd->max_files_per_dir = src_ffd->max_files_per_dir; - dst_ffd->min_log_addressing_rev = src_ffd->min_log_addressing_rev; - dst_ffd->format = src_ffd->format; - - /* Create the revision data directories. */ - if (dst_ffd->max_files_per_dir) - SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_rev_shard(dst_fs, - 0, pool), - pool)); - else - SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path, - PATH_REVS_DIR, pool), - pool)); - - /* Create the revprops directory. */ - if (src_ffd->max_files_per_dir) - SVN_ERR(svn_io_make_dir_recursively( - svn_fs_fs__path_revprops_shard(dst_fs, 0, pool), - pool)); - else - SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path, - PATH_REVPROPS_DIR, - pool), - pool)); - - /* Create the transaction directory. */ - SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path, PATH_TXNS_DIR, - pool), + /* Create the DST_FS repository with the same layout as SRC_FS. */ + SVN_ERR(svn_fs_fs__create_file_tree(dst_fs, dst_path, src_ffd->format, + src_ffd->max_files_per_dir, + src_ffd->use_log_addressing, pool)); - /* Create the protorevs directory. */ - if (dst_ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) - SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(dst_path, - PATH_TXN_PROTOS_DIR, - pool), - pool)); - - /* Create the 'current' file. */ - SVN_ERR(svn_io_file_create(svn_fs_fs__path_current(dst_fs, pool), - (dst_ffd->format >= - SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT - ? "0\n" : "0 1 1\n"), - pool)); - - /* Create the lock and 'uuid' files. Hotcopy destination receives a - new instance ID, but has the same filesystem UUID as the source. */ - SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_lock(dst_fs, pool), pool)); + /* Copy the UUID. Hotcopy destination receives a new instance ID, but + * has the same filesystem UUID as the source. */ SVN_ERR(svn_fs_fs__set_uuid(dst_fs, src_fs->uuid, NULL, pool)); - /* Create the min unpacked rev file. */ - if (dst_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) - SVN_ERR(svn_io_file_create(svn_fs_fs__path_min_unpacked_rev(dst_fs, pool), - "0\n", pool)); - /* Create the txn-current file if the repository supports - the transaction sequence file. */ - if (dst_ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT) - { - SVN_ERR(svn_io_file_create(svn_fs_fs__path_txn_current(dst_fs, pool), - "0\n", pool)); - SVN_ERR(svn_io_file_create_empty( - svn_fs_fs__path_txn_current_lock(dst_fs, pool), - pool)); - } - - /* FS creation is complete. Stamp it with a format file. */ - SVN_ERR(svn_fs_fs__write_format(dst_fs, TRUE, pool)); + /* Remove revision 0 contents. Otherwise, it may not get overwritten + * due to having a newer timestamp. */ + SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_rev(dst_fs, 0, pool), pool)); + SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_revprops(dst_fs, 0, pool), + pool)); + + /* This filesystem is ready. Stamp it with a format number. Fail if + * the 'format' file should already exist. */ + SVN_ERR(svn_fs_fs__write_format(dst_fs, FALSE, pool)); return SVN_NO_ERROR; } @@ -1175,8 +1104,8 @@ svn_fs_fs__hotcopy(svn_fs_t *src_fs, hbb.notify_baton = notify_baton; hbb.cancel_func = cancel_func; hbb.cancel_baton = cancel_baton; - SVN_ERR(svn_fs_fs__with_write_lock(dst_fs, hotcopy_locking_src_body, &hbb, - pool)); + SVN_ERR(svn_fs_fs__with_all_locks(dst_fs, hotcopy_locking_src_body, &hbb, + pool)); return SVN_NO_ERROR; } Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/id.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/id.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/id.c (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/id.c Sat Jan 3 14:00:41 2015 @@ -494,10 +494,11 @@ svn_fs_fs__id_copy(const svn_fs_id_t *so return (svn_fs_id_t *)new_id; } - -svn_fs_id_t * -svn_fs_fs__id_parse(char *data, - apr_pool_t *pool) +/* Return an ID resulting from parsing the string DATA, or NULL if DATA is + an invalid ID string. *DATA will be modified / invalidated by this call. */ +static svn_fs_id_t * +id_parse(char *data, + apr_pool_t *pool) { fs_fs__id_t *id; char *str; @@ -573,6 +574,21 @@ svn_fs_fs__id_parse(char *data, return (svn_fs_id_t *)id; } +svn_error_t * +svn_fs_fs__id_parse(const svn_fs_id_t **id_p, + char *data, + apr_pool_t *pool) +{ + svn_fs_id_t *id = id_parse(data, pool); + if (id == NULL) + return svn_error_createf(SVN_ERR_FS_MALFORMED_NODEREV_ID, NULL, + "Malformed node revision ID string"); + + *id_p = id; + + return SVN_NO_ERROR; +} + /* (de-)serialization support */ /* Serialize an ID within the serialization CONTEXT. Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/id.h URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/id.h?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_fs/id.h (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_fs/id.h Sat Jan 3 14:00:41 2015 @@ -24,32 +24,12 @@ #define SVN_LIBSVN_FS_FS_ID_H #include "svn_fs.h" +#include "private/svn_fs_fs_private.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -/* Node-revision IDs in FSFS consist of 3 of sub-IDs ("parts") that consist - * of a creation REVISION number and some revision- / transaction-local - * counter value (NUMBER). Old-style ID parts use global counter values. - * - * The parts are: node_id, copy_id and txn_id for in-txn IDs as well as - * node_id, copy_id and rev_offset for in-revision IDs. This struct the - * data structure used for each of those parts. - */ -typedef struct svn_fs_fs__id_part_t -{ - /* SVN_INVALID_REVNUM for txns -> not a txn, COUNTER must be 0. - SVN_INVALID_REVNUM for others -> not assigned to a revision, yet. - 0 for others -> old-style ID or the root in rev 0. */ - svn_revnum_t revision; - - /* sub-id value relative to REVISION. Its interpretation depends on - the part itself. In rev_item, it is the index_index value, in others - it represents a unique counter value. */ - apr_uint64_t number; -} svn_fs_fs__id_part_t; - /*** Operations on ID parts. ***/ @@ -149,10 +129,13 @@ svn_fs_id_t *svn_fs_fs__id_rev_create(co svn_fs_id_t *svn_fs_fs__id_copy(const svn_fs_id_t *id, apr_pool_t *pool); -/* Return an ID resulting from parsing the string DATA, or NULL if DATA is - an invalid ID string. *DATA will be modified / invalidated by this call. */ -svn_fs_id_t *svn_fs_fs__id_parse(char *data, - apr_pool_t *pool); +/* Return an ID in *ID_P resulting from parsing the string DATA, or an error + if DATA is an invalid ID string. *DATA will be modified / invalidated by + this call. */ +svn_error_t * +svn_fs_fs__id_parse(const svn_fs_id_t **id_p, + char *data, + apr_pool_t *pool); /* (de-)serialization support*/