You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/11/30 11:24:23 UTC
svn commit: r1717223 [16/50] - in /subversion/branches/ra-git: ./ build/
build/ac-macros/ build/generator/ build/generator/templates/
contrib/hook-scripts/ notes/ notes/api-errata/1.9/ notes/move-tracking/
subversion/ subversion/bindings/ctypes-python/...
Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/revprops.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/revprops.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/revprops.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/revprops.c Mon Nov 30 10:24:16 2015
@@ -25,9 +25,11 @@
#include "svn_pools.h"
#include "svn_hash.h"
#include "svn_dirent_uri.h"
+#include "svn_sorts.h"
#include "fs_fs.h"
#include "revprops.h"
+#include "temp_serializer.h"
#include "util.h"
#include "private/svn_subr_private.h"
@@ -36,11 +38,6 @@
#include "svn_private_config.h"
-/* Give writing processes 10 seconds to replace an existing revprop
- file with a new one. After that time, we assume that the writing
- process got aborted and that we have re-read revprops. */
-#define REVPROP_CHANGE_TIMEOUT (10 * 1000000)
-
svn_error_t *
svn_fs_fs__upgrade_pack_revprops(svn_fs_t *fs,
svn_fs_upgrade_notify_t notify_func,
@@ -144,9 +141,6 @@ typedef struct packed_revprops_t
/* revision number to read (not necessarily the first in the pack) */
svn_revnum_t revision;
- /* current revprop generation. Used when populating the revprop cache */
- apr_int64_t generation;
-
/* the actual revision properties */
apr_hash_t *properties;
@@ -189,35 +183,73 @@ typedef struct packed_revprops_t
/* Parse the serialized revprops in CONTENT and return them in *PROPERTIES.
* Also, put them into the revprop cache, if activated, for future use.
- * Three more parameters are being used to update the revprop cache: FS is
- * our file system, the revprops belong to REVISION and the global revprop
- * GENERATION is used as well.
*
- * The returned hash will be allocated in POOL, SCRATCH_POOL is being used
- * for temporary allocations.
+ * The returned hash will be allocated in RESULT_POOL, SCRATCH_POOL is being
+ * used for temporary allocations.
*/
static svn_error_t *
parse_revprop(apr_hash_t **properties,
svn_fs_t *fs,
svn_revnum_t revision,
- apr_int64_t generation,
svn_string_t *content,
- apr_pool_t *pool,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stream_t *stream = svn_stream_from_string(content, scratch_pool);
- *properties = apr_hash_make(pool);
+ *properties = apr_hash_make(result_pool);
- SVN_ERR_W(svn_hash_read2(*properties, stream, SVN_HASH_TERMINATOR, pool),
+ SVN_ERR_W(svn_hash_read2(*properties, stream, SVN_HASH_TERMINATOR,
+ result_pool),
apr_psprintf(scratch_pool, "Failed to parse revprops for r%ld.",
revision));
return SVN_NO_ERROR;
}
+void
+svn_fs_fs__reset_revprop_cache(svn_fs_t *fs)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ ffd->revprop_prefix = 0;
+}
+
+/* If FS has not a revprop cache prefix set, generate one.
+ * Always call this before accessing the revprop cache.
+ */
+static svn_error_t *
+prepare_revprop_cache(svn_fs_t *fs,
+ apr_pool_t *scratch_pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ if (!ffd->revprop_prefix)
+ SVN_ERR(svn_atomic__unique_counter(&ffd->revprop_prefix));
+
+ return SVN_NO_ERROR;
+}
+
+/* Store the unparsed revprop hash CONTENT for REVISION in FS's revprop
+ * cache. Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+cache_revprops(svn_fs_t *fs,
+ svn_revnum_t revision,
+ svn_string_t *content,
+ apr_pool_t *scratch_pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ pair_cache_key_t key;
+
+ /* Make sure prepare_revprop_cache() has been called. */
+ SVN_ERR_ASSERT(ffd->revprop_prefix);
+ key.revision = revision;
+ key.second = ffd->revprop_prefix;
+
+ SVN_ERR(svn_cache__set(ffd->revprop_cache, &key, content, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
/* Read the non-packed revprops for revision REV in FS, put them into the
- * revprop cache if activated and return them in *PROPERTIES. GENERATION
- * is the current revprop generation.
+ * revprop cache if PROPULATE_CACHE is set and return them in *PROPERTIES.
*
* If the data could not be read due to an otherwise recoverable error,
* leave *PROPERTIES unchanged. No error will be returned in that case.
@@ -228,7 +260,7 @@ static svn_error_t *
read_non_packed_revprop(apr_hash_t **properties,
svn_fs_t *fs,
svn_revnum_t rev,
- apr_int64_t generation,
+ svn_boolean_t populate_cache,
apr_pool_t *pool)
{
svn_stringbuf_t *content = NULL;
@@ -249,9 +281,13 @@ read_non_packed_revprop(apr_hash_t **pro
}
if (content)
- SVN_ERR(parse_revprop(properties, fs, rev, generation,
- svn_stringbuf__morph_into_string(content),
- pool, iterpool));
+ {
+ svn_string_t *as_string = svn_stringbuf__morph_into_string(content);
+ SVN_ERR(parse_revprop(properties, fs, rev, as_string, pool, iterpool));
+
+ if (populate_cache)
+ SVN_ERR(cache_revprops(fs, rev, as_string, iterpool));
+ }
svn_pool_clear(iterpool);
@@ -272,12 +308,13 @@ get_min_filename_len(packed_revprops_t *
}
/* Given FS and REVPROPS->REVISION, fill the FILENAME, FOLDER and MANIFEST
- * members. Use POOL for allocating results and SCRATCH_POOL for temporaries.
+ * members. Use RESULT_POOL for allocating results and SCRATCH_POOL for
+ * temporaries.
*/
static svn_error_t *
get_revprop_packname(svn_fs_t *fs,
packed_revprops_t *revprops,
- apr_pool_t *pool,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
@@ -298,18 +335,20 @@ get_revprop_packname(svn_fs_t *fs,
--rev_count;
}
- revprops->manifest = apr_array_make(pool, rev_count, sizeof(const char*));
+ revprops->manifest = apr_array_make(result_pool, rev_count,
+ sizeof(const char*));
/* No line in the file can be less than this number of chars long. */
min_filename_len = get_min_filename_len(revprops);
/* Read the content of the manifest file */
revprops->folder
- = svn_fs_fs__path_revprops_pack_shard(fs, revprops->revision, pool);
+ = svn_fs_fs__path_revprops_pack_shard(fs, revprops->revision,
+ result_pool);
manifest_file_path
- = svn_dirent_join(revprops->folder, PATH_MANIFEST, pool);
+ = svn_dirent_join(revprops->folder, PATH_MANIFEST, result_pool);
- SVN_ERR(svn_fs_fs__read_content(&content, manifest_file_path, pool));
+ SVN_ERR(svn_fs_fs__read_content(&content, manifest_file_path, result_pool));
/* There CONTENT must have a certain minimal size and there no
* unterminated lines at the end of the file. Both guarantees also
@@ -392,7 +431,8 @@ same_shard(svn_fs_t *fs,
/* Given FS and the full packed file content in REVPROPS->PACKED_REVPROPS,
* fill the START_REVISION member, and make PACKED_REVPROPS point to the
* first serialized revprop. If READ_ALL is set, initialize the SIZES
- * and OFFSETS members as well.
+ * and OFFSETS members as well. If POPULATE_CACHE is set, cache all
+ * revprops found in this pack.
*
* Parse the revprops for REVPROPS->REVISION and set the PROPERTIES as
* well as the SERIALIZED_SIZE member. If revprop caching has been
@@ -402,20 +442,22 @@ static svn_error_t *
parse_packed_revprops(svn_fs_t *fs,
packed_revprops_t *revprops,
svn_boolean_t read_all,
- apr_pool_t *pool,
+ svn_boolean_t populate_cache,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stream_t *stream;
apr_int64_t first_rev, count, i;
- apr_off_t offset;
+ apr_size_t offset;
const char *header_end;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* decompress (even if the data is only "stored", there is still a
* length header to remove) */
svn_stringbuf_t *compressed = revprops->packed_revprops;
- svn_stringbuf_t *uncompressed = svn_stringbuf_create_empty(pool);
- SVN_ERR(svn__decompress(compressed, uncompressed, APR_SIZE_MAX));
+ svn_stringbuf_t *uncompressed = svn_stringbuf_create_empty(result_pool);
+ SVN_ERR(svn__decompress(compressed->data, compressed->len,
+ uncompressed, APR_SIZE_MAX));
/* read first revision number and number of revisions in the pack */
stream = svn_stream_from_stringbuf(uncompressed, scratch_pool);
@@ -453,18 +495,21 @@ parse_packed_revprops(svn_fs_t *fs,
offset = header_end - uncompressed->data + 2;
- revprops->packed_revprops = svn_stringbuf_create_empty(pool);
+ revprops->packed_revprops = svn_stringbuf_create_empty(result_pool);
revprops->packed_revprops->data = uncompressed->data + offset;
revprops->packed_revprops->len = (apr_size_t)(uncompressed->len - offset);
- revprops->packed_revprops->blocksize = (apr_size_t)(uncompressed->blocksize - offset);
+ revprops->packed_revprops->blocksize = (apr_size_t)(uncompressed->blocksize
+ - offset);
/* STREAM still points to the first entry in the sizes list. */
revprops->start_revision = (svn_revnum_t)first_rev;
if (read_all)
{
/* Init / construct REVPROPS members. */
- revprops->sizes = apr_array_make(pool, (int)count, sizeof(offset));
- revprops->offsets = apr_array_make(pool, (int)count, sizeof(offset));
+ revprops->sizes = apr_array_make(result_pool, (int)count,
+ sizeof(offset));
+ revprops->offsets = apr_array_make(result_pool, (int)count,
+ sizeof(offset));
}
/* Now parse, revision by revision, the size and content of each
@@ -489,21 +534,24 @@ parse_packed_revprops(svn_fs_t *fs,
if (revision == revprops->revision)
{
+ /* Parse (and possibly cache) the one revprop list we care about. */
SVN_ERR(parse_revprop(&revprops->properties, fs, revision,
- revprops->generation, &serialized,
- pool, iterpool));
+ &serialized, result_pool, iterpool));
revprops->serialized_size = serialized.len;
/* If we only wanted the revprops for REVISION then we are done. */
- if (!read_all)
+ if (!read_all && !populate_cache)
break;
}
+ if (populate_cache)
+ SVN_ERR(cache_revprops(fs, revision, &serialized, iterpool));
+
if (read_all)
{
/* fill REVPROPS data structures */
- APR_ARRAY_PUSH(revprops->sizes, apr_off_t) = serialized.len;
- APR_ARRAY_PUSH(revprops->offsets, apr_off_t) = offset;
+ APR_ARRAY_PUSH(revprops->sizes, apr_size_t) = serialized.len;
+ APR_ARRAY_PUSH(revprops->offsets, apr_size_t) = offset;
}
revprops->total_size += serialized.len;
@@ -514,7 +562,7 @@ parse_packed_revprops(svn_fs_t *fs,
}
/* In filesystem FS, read the packed revprops for revision REV into
- * *REVPROPS. Use GENERATION to populate the revprop cache, if enabled.
+ * *REVPROPS. Populate the revprop cache, if POPULATE_CACHE is set.
* If you want to modify revprop contents / update REVPROPS, READ_ALL
* must be set. Otherwise, only the properties of REV are being provided.
* Allocate data in POOL.
@@ -523,8 +571,8 @@ static svn_error_t *
read_pack_revprop(packed_revprops_t **revprops,
svn_fs_t *fs,
svn_revnum_t rev,
- apr_int64_t generation,
svn_boolean_t read_all,
+ svn_boolean_t populate_cache,
apr_pool_t *pool)
{
apr_pool_t *iterpool = svn_pool_create(pool);
@@ -544,7 +592,6 @@ read_pack_revprop(packed_revprops_t **re
/* initialize the result data structure */
result = apr_pcalloc(pool, sizeof(*result));
result->revision = rev;
- result->generation = generation;
/* try to read the packed revprops. This may require retries if we have
* concurrent writers. */
@@ -575,7 +622,8 @@ read_pack_revprop(packed_revprops_t **re
_("Failed to read revprop pack file for r%ld"), rev);
/* parse it. RESULT will be complete afterwards. */
- err = parse_packed_revprops(fs, result, read_all, pool, iterpool);
+ err = parse_packed_revprops(fs, result, read_all, populate_cache, pool,
+ iterpool);
svn_pool_destroy(iterpool);
if (err)
return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
@@ -594,16 +642,48 @@ svn_error_t *
svn_fs_fs__get_revision_proplist(apr_hash_t **proplist_p,
svn_fs_t *fs,
svn_revnum_t rev,
- apr_pool_t *pool)
+ svn_boolean_t refresh,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
- apr_int64_t generation = 0;
+
+ /* Only populate the cache if we did not just cross a sync barrier.
+ * This is to eliminate overhead from code that always sets REFRESH.
+ * For callers that want caching, the caching kicks in on read "later". */
+ svn_boolean_t populate_cache = !refresh;
/* not found, yet */
*proplist_p = NULL;
/* should they be available at all? */
- SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool));
+ SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, scratch_pool));
+
+ if (refresh)
+ {
+ /* Previous cache contents is invalid now. */
+ svn_fs_fs__reset_revprop_cache(fs);
+ }
+ else
+ {
+ /* Try cache lookup first. */
+ svn_boolean_t is_cached;
+ pair_cache_key_t key;
+
+ /* Auto-alloc prefix and construct the key. */
+ SVN_ERR(prepare_revprop_cache(fs, scratch_pool));
+ key.revision = rev;
+ key.second = ffd->revprop_prefix;
+
+ /* The only way that this might error out is due to parser error. */
+ SVN_ERR_W(svn_cache__get((void **) proplist_p, &is_cached,
+ ffd->revprop_cache, &key, result_pool),
+ apr_psprintf(scratch_pool,
+ "Failed to parse revprops for r%ld.",
+ rev));
+ if (is_cached)
+ return SVN_NO_ERROR;
+ }
/* if REV had not been packed when we began, try reading it from the
* non-packed shard. If that fails, we will fall through to packed
@@ -611,7 +691,7 @@ svn_fs_fs__get_revision_proplist(apr_has
if (!svn_fs_fs__is_packed_revprop(fs, rev))
{
svn_error_t *err = read_non_packed_revprop(proplist_p, fs, rev,
- generation, pool);
+ populate_cache, result_pool);
if (err)
{
if (!APR_STATUS_IS_ENOENT(err->apr_err)
@@ -629,7 +709,8 @@ svn_fs_fs__get_revision_proplist(apr_has
if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT && !*proplist_p)
{
packed_revprops_t *revprops;
- SVN_ERR(read_pack_revprop(&revprops, fs, rev, generation, FALSE, pool));
+ SVN_ERR(read_pack_revprop(&revprops, fs, rev, FALSE, populate_cache,
+ result_pool));
*proplist_p = revprops->properties;
}
@@ -657,17 +738,23 @@ write_non_packed_revprop(const char **fi
apr_hash_t *proplist,
apr_pool_t *pool)
{
+ apr_file_t *file;
svn_stream_t *stream;
*final_path = svn_fs_fs__path_revprops(fs, rev, pool);
/* ### do we have a directory sitting around already? we really shouldn't
### have to get the dirname here. */
- SVN_ERR(svn_stream_open_unique(&stream, tmp_path,
- svn_dirent_dirname(*final_path, pool),
- svn_io_file_del_none, pool, pool));
+ SVN_ERR(svn_io_open_unique_file3(&file, tmp_path,
+ svn_dirent_dirname(*final_path, pool),
+ svn_io_file_del_none, pool, pool));
+ stream = svn_stream_from_aprfile2(file, TRUE, pool);
SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, pool));
SVN_ERR(svn_stream_close(stream));
+ /* Flush temporary file to disk and close it. */
+ SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
+
return SVN_NO_ERROR;
}
@@ -738,8 +825,8 @@ serialize_revprops_header(svn_stream_t *
* We only allocate a few bytes each iteration -- even with a
* million iterations we would still be in good shape memory-wise.
*/
- apr_off_t size = APR_ARRAY_IDX(sizes, i, apr_off_t);
- SVN_ERR(svn_stream_printf(stream, iterpool, "%" APR_OFF_T_FMT "\n",
+ apr_size_t size = APR_ARRAY_IDX(sizes, i, apr_size_t);
+ SVN_ERR(svn_stream_printf(stream, iterpool, "%" APR_SIZE_T_FMT "\n",
size));
}
@@ -750,7 +837,7 @@ serialize_revprops_header(svn_stream_t *
return SVN_NO_ERROR;
}
-/* Writes the a pack file to FILE_STREAM. It copies the serialized data
+/* Writes the a pack file to FILE. It copies the serialized data
* from REVPROPS for the indexes [START,END) except for index CHANGED_INDEX.
*
* The data for the latter is taken from NEW_SERIALIZED. Note, that
@@ -767,8 +854,8 @@ repack_revprops(svn_fs_t *fs,
int end,
int changed_index,
svn_stringbuf_t *new_serialized,
- apr_off_t new_total_size,
- svn_stream_t *file_stream,
+ apr_size_t new_total_size,
+ apr_file_t *file,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
@@ -796,10 +883,8 @@ repack_revprops(svn_fs_t *fs,
}
else
{
- apr_size_t size
- = (apr_size_t)APR_ARRAY_IDX(revprops->sizes, i, apr_off_t);
- apr_size_t offset
- = (apr_size_t)APR_ARRAY_IDX(revprops->offsets, i, apr_off_t);
+ apr_size_t size = APR_ARRAY_IDX(revprops->sizes, i, apr_size_t);
+ apr_size_t offset = APR_ARRAY_IDX(revprops->offsets, i, apr_size_t);
SVN_ERR(svn_stream_write(stream,
revprops->packed_revprops->data + offset,
@@ -810,15 +895,17 @@ repack_revprops(svn_fs_t *fs,
SVN_ERR(svn_stream_close(stream));
/* compress / store the data */
- SVN_ERR(svn__compress(uncompressed,
+ SVN_ERR(svn__compress(uncompressed->data, uncompressed->len,
compressed,
ffd->compress_packed_revprops
? SVN_DELTA_COMPRESSION_LEVEL_DEFAULT
: SVN_DELTA_COMPRESSION_LEVEL_NONE));
- /* finally, write the content to the target stream and close it */
- SVN_ERR(svn_stream_write(file_stream, compressed->data, &compressed->len));
- SVN_ERR(svn_stream_close(file_stream));
+ /* finally, write the content to the target file, flush and close it */
+ SVN_ERR(svn_io_file_write_full(file, compressed->data, compressed->len,
+ NULL, pool));
+ SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
return SVN_NO_ERROR;
}
@@ -826,23 +913,22 @@ repack_revprops(svn_fs_t *fs,
/* Allocate a new pack file name for revisions
* [REVPROPS->START_REVISION + START, REVPROPS->START_REVISION + END - 1]
* of REVPROPS->MANIFEST. Add the name of old file to FILES_TO_DELETE,
- * auto-create that array if necessary. Return an open file stream to
- * the new file in *STREAM allocated in POOL.
+ * auto-create that array if necessary. Return an open file *FILE that is
+ * allocated in POOL.
*/
static svn_error_t *
-repack_stream_open(svn_stream_t **stream,
- svn_fs_t *fs,
- packed_revprops_t *revprops,
- int start,
- int end,
- apr_array_header_t **files_to_delete,
- apr_pool_t *pool)
+repack_file_open(apr_file_t **file,
+ svn_fs_t *fs,
+ packed_revprops_t *revprops,
+ int start,
+ int end,
+ apr_array_header_t **files_to_delete,
+ apr_pool_t *pool)
{
apr_int64_t tag;
const char *tag_string;
svn_string_t *new_filename;
int i;
- apr_file_t *file;
int manifest_offset
= (int)(revprops->start_revision - revprops->manifest_start);
@@ -874,12 +960,11 @@ repack_stream_open(svn_stream_t **stream
APR_ARRAY_IDX(revprops->manifest, i + manifest_offset, const char*)
= new_filename->data;
- /* create a file stream for the new file */
- SVN_ERR(svn_io_file_open(&file, svn_dirent_join(revprops->folder,
- new_filename->data,
- pool),
+ /* open the file */
+ SVN_ERR(svn_io_file_open(file, svn_dirent_join(revprops->folder,
+ new_filename->data,
+ pool),
APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pool));
- *stream = svn_stream_from_aprfile2(file, FALSE, pool);
return SVN_NO_ERROR;
}
@@ -901,14 +986,14 @@ write_packed_revprop(const char **final_
{
fs_fs_data_t *ffd = fs->fsap_data;
packed_revprops_t *revprops;
- apr_int64_t generation = 0;
svn_stream_t *stream;
+ apr_file_t *file;
svn_stringbuf_t *serialized;
- apr_off_t new_total_size;
+ apr_size_t new_total_size;
int changed_index;
/* read contents of the current pack file */
- SVN_ERR(read_pack_revprop(&revprops, fs, rev, generation, TRUE, pool));
+ SVN_ERR(read_pack_revprop(&revprops, fs, rev, TRUE, FALSE, pool));
/* serialize the new revprops */
serialized = svn_stringbuf_create_empty(pool);
@@ -922,7 +1007,7 @@ write_packed_revprop(const char **final_
+ serialized->len
+ (revprops->offsets->nelts + 2) * SVN_INT64_BUFFER_SIZE;
- APR_ARRAY_IDX(revprops->sizes, changed_index, apr_off_t) = serialized->len;
+ APR_ARRAY_IDX(revprops->sizes, changed_index, apr_size_t) = serialized->len;
/* can we put the new data into the same pack as the before? */
if ( new_total_size < ffd->revprop_pack_size
@@ -933,11 +1018,11 @@ write_packed_revprop(const char **final_
*final_path = svn_dirent_join(revprops->folder, revprops->filename,
pool);
- SVN_ERR(svn_stream_open_unique(&stream, tmp_path, revprops->folder,
- svn_io_file_del_none, pool, pool));
+ SVN_ERR(svn_io_open_unique_file3(&file, tmp_path, revprops->folder,
+ svn_io_file_del_none, pool, pool));
SVN_ERR(repack_revprops(fs, revprops, 0, revprops->sizes->nelts,
changed_index, serialized, new_total_size,
- stream, pool));
+ file, pool));
}
else
{
@@ -946,23 +1031,23 @@ write_packed_revprop(const char **final_
int left = 0;
int right = revprops->sizes->nelts - 1;
- apr_off_t left_size = 2 * SVN_INT64_BUFFER_SIZE;
- apr_off_t right_size = 2 * SVN_INT64_BUFFER_SIZE;
+ apr_size_t left_size = 2 * SVN_INT64_BUFFER_SIZE;
+ apr_size_t right_size = 2 * SVN_INT64_BUFFER_SIZE;
/* let left and right side grow such that their size difference
* is minimal after each step. */
while (left <= right)
- if ( left_size + APR_ARRAY_IDX(revprops->sizes, left, apr_off_t)
- < right_size + APR_ARRAY_IDX(revprops->sizes, right, apr_off_t))
+ if ( left_size + APR_ARRAY_IDX(revprops->sizes, left, apr_size_t)
+ < right_size + APR_ARRAY_IDX(revprops->sizes, right, apr_size_t))
{
- left_size += APR_ARRAY_IDX(revprops->sizes, left, apr_off_t)
+ left_size += APR_ARRAY_IDX(revprops->sizes, left, apr_size_t)
+ SVN_INT64_BUFFER_SIZE;
++left;
}
else
{
- right_size += APR_ARRAY_IDX(revprops->sizes, right, apr_off_t)
- + SVN_INT64_BUFFER_SIZE;
+ right_size += APR_ARRAY_IDX(revprops->sizes, right, apr_size_t)
+ + SVN_INT64_BUFFER_SIZE;
--right;
}
@@ -983,50 +1068,51 @@ write_packed_revprop(const char **final_
/* write the new, split files */
if (left_count)
{
- SVN_ERR(repack_stream_open(&stream, fs, revprops, 0,
- left_count, files_to_delete, pool));
+ SVN_ERR(repack_file_open(&file, fs, revprops, 0,
+ left_count, files_to_delete, pool));
SVN_ERR(repack_revprops(fs, revprops, 0, left_count,
changed_index, serialized, new_total_size,
- stream, pool));
+ file, pool));
}
if (left_count + right_count < revprops->sizes->nelts)
{
- SVN_ERR(repack_stream_open(&stream, fs, revprops, changed_index,
- changed_index + 1, files_to_delete,
- pool));
+ SVN_ERR(repack_file_open(&file, fs, revprops, changed_index,
+ changed_index + 1, files_to_delete,
+ pool));
SVN_ERR(repack_revprops(fs, revprops, changed_index,
changed_index + 1,
changed_index, serialized, new_total_size,
- stream, pool));
+ file, pool));
}
if (right_count)
{
- SVN_ERR(repack_stream_open(&stream, fs, revprops,
- revprops->sizes->nelts - right_count,
- revprops->sizes->nelts,
- files_to_delete, pool));
+ SVN_ERR(repack_file_open(&file, fs, revprops,
+ revprops->sizes->nelts - right_count,
+ revprops->sizes->nelts,
+ files_to_delete, pool));
SVN_ERR(repack_revprops(fs, revprops,
revprops->sizes->nelts - right_count,
revprops->sizes->nelts, changed_index,
- serialized, new_total_size, stream,
+ serialized, new_total_size, file,
pool));
}
/* write the new manifest */
*final_path = svn_dirent_join(revprops->folder, PATH_MANIFEST, pool);
- SVN_ERR(svn_stream_open_unique(&stream, tmp_path, revprops->folder,
- svn_io_file_del_none, pool, pool));
-
+ SVN_ERR(svn_io_open_unique_file3(&file, tmp_path, revprops->folder,
+ svn_io_file_del_none, pool, pool));
+ stream = svn_stream_from_aprfile2(file, TRUE, pool);
for (i = 0; i < revprops->manifest->nelts; ++i)
{
const char *filename = APR_ARRAY_IDX(revprops->manifest, i,
const char*);
SVN_ERR(svn_stream_printf(stream, pool, "%s\n", filename));
}
-
SVN_ERR(svn_stream_close(stream));
+ SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
}
return SVN_NO_ERROR;
@@ -1059,6 +1145,9 @@ svn_fs_fs__set_revision_proplist(svn_fs_
SVN_ERR(write_non_packed_revprop(&final_path, &tmp_path,
fs, rev, proplist, pool));
+ /* Previous cache contents is invalid now. */
+ svn_fs_fs__reset_revprop_cache(fs);
+
/* We use the rev file of this revision as the perms reference,
* because when setting revprops for the first time, the revprop
* file won't exist and therefore can't serve as its own reference.
@@ -1165,7 +1254,6 @@ svn_fs_fs__copy_revprops(const char *pac
apr_file_t *pack_file;
svn_revnum_t rev;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- svn_stream_t *stream;
/* create empty data buffer and a write stream on top of it */
svn_stringbuf_t *uncompressed
@@ -1189,6 +1277,7 @@ svn_fs_fs__copy_revprops(const char *pac
for (rev = start_rev; rev <= end_rev; rev++)
{
const char *path;
+ svn_stream_t *stream;
svn_pool_clear(iterpool);
@@ -1207,12 +1296,14 @@ svn_fs_fs__copy_revprops(const char *pac
SVN_ERR(svn_stream_close(pack_stream));
/* compress the content (or just store it for COMPRESSION_LEVEL 0) */
- SVN_ERR(svn__compress(uncompressed, compressed, compression_level));
+ SVN_ERR(svn__compress(uncompressed->data, uncompressed->len,
+ compressed, compression_level));
/* write the pack file content to disk */
- stream = svn_stream_from_aprfile2(pack_file, FALSE, scratch_pool);
- SVN_ERR(svn_stream_write(stream, compressed->data, &compressed->len));
- SVN_ERR(svn_stream_close(stream));
+ SVN_ERR(svn_io_file_write_full(pack_file, compressed->data, compressed->len,
+ NULL, scratch_pool));
+ SVN_ERR(svn_io_file_flush_to_disk(pack_file, scratch_pool));
+ SVN_ERR(svn_io_file_close(pack_file, scratch_pool));
svn_pool_destroy(iterpool);
@@ -1224,19 +1315,24 @@ svn_fs_fs__pack_revprops_shard(const cha
const char *shard_path,
apr_int64_t shard,
int max_files_per_dir,
- apr_off_t max_pack_size,
+ apr_int64_t max_pack_size,
int compression_level,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
const char *manifest_file_path, *pack_filename = NULL;
+ apr_file_t *manifest_file;
svn_stream_t *manifest_stream;
svn_revnum_t start_rev, end_rev, rev;
- apr_off_t total_size;
+ apr_size_t total_size;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_array_header_t *sizes;
+ /* Sanitize config file values. */
+ apr_size_t max_size = (apr_size_t)MIN(MAX(max_pack_size, 1),
+ SVN_MAX_OBJECT_SIZE);
+
/* Some useful paths. */
manifest_file_path = svn_dirent_join(pack_file_dir, PATH_MANIFEST,
scratch_pool);
@@ -1247,8 +1343,12 @@ svn_fs_fs__pack_revprops_shard(const cha
/* Create the new directory and manifest file stream. */
SVN_ERR(svn_io_dir_make(pack_file_dir, APR_OS_DEFAULT, scratch_pool));
- SVN_ERR(svn_stream_open_writable(&manifest_stream, manifest_file_path,
- scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_io_file_open(&manifest_file, manifest_file_path,
+ APR_WRITE | APR_BUFFERED | APR_CREATE | APR_EXCL,
+ APR_OS_DEFAULT, scratch_pool));
+ manifest_stream = svn_stream_from_aprfile2(manifest_file, TRUE,
+ scratch_pool);
/* revisions to handle. Special case: revision 0 */
start_rev = (svn_revnum_t) (shard * max_files_per_dir);
@@ -1260,7 +1360,7 @@ svn_fs_fs__pack_revprops_shard(const cha
works. */
/* initialize the revprop size info */
- sizes = apr_array_make(scratch_pool, max_files_per_dir, sizeof(apr_off_t));
+ sizes = apr_array_make(scratch_pool, max_files_per_dir, sizeof(apr_size_t));
total_size = 2 * SVN_INT64_BUFFER_SIZE;
/* Iterate over the revisions in this shard, determine their size and
@@ -1280,11 +1380,11 @@ svn_fs_fs__pack_revprops_shard(const cha
/* if we already have started a pack file and this revprop cannot be
* appended to it, write the previous pack file. */
if (sizes->nelts != 0 &&
- total_size + SVN_INT64_BUFFER_SIZE + finfo.size > max_pack_size)
+ total_size + SVN_INT64_BUFFER_SIZE + finfo.size > max_size)
{
SVN_ERR(svn_fs_fs__copy_revprops(pack_file_dir, pack_filename,
shard_path, start_rev, rev-1,
- sizes, (apr_size_t)total_size,
+ sizes, total_size,
compression_level, cancel_func,
cancel_baton, iterpool));
@@ -1303,7 +1403,7 @@ svn_fs_fs__pack_revprops_shard(const cha
pack_filename));
/* add to list of files to put into the current pack file */
- APR_ARRAY_PUSH(sizes, apr_off_t) = finfo.size;
+ APR_ARRAY_PUSH(sizes, apr_size_t) = finfo.size;
total_size += SVN_INT64_BUFFER_SIZE + finfo.size;
}
@@ -1315,8 +1415,10 @@ svn_fs_fs__pack_revprops_shard(const cha
compression_level, cancel_func,
cancel_baton, iterpool));
- /* flush the manifest file and update permissions */
+ /* flush the manifest file to disk and update permissions */
SVN_ERR(svn_stream_close(manifest_stream));
+ SVN_ERR(svn_io_file_flush_to_disk(manifest_file, iterpool));
+ SVN_ERR(svn_io_file_close(manifest_file, iterpool));
SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, iterpool));
svn_pool_destroy(iterpool);
@@ -1347,7 +1449,7 @@ svn_fs_fs__delete_revprops_shard(const c
apr_psprintf(iterpool, "%d", i),
iterpool);
if (cancel_func)
- SVN_ERR((*cancel_func)(cancel_baton));
+ SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(svn_io_remove_file2(path, TRUE, iterpool));
}
Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/revprops.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/revprops.h?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/revprops.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/revprops.h Mon Nov 30 10:24:16 2015
@@ -58,15 +58,23 @@ svn_fs_fs__upgrade_cleanup_pack_revprops
void *cancel_baton,
apr_pool_t *scratch_pool);
+/* Invalidate the revprop cache in FS. */
+void
+svn_fs_fs__reset_revprop_cache(svn_fs_t *fs);
+
/* Read the revprops for revision REV in FS and return them in *PROPERTIES_P.
+ * If REFRESH is set, clear the revprop cache before accessing the data.
*
- * Allocations will be done in POOL.
+ * The result will be allocated in RESULT_POOL; SCRATCH_POOL is used for
+ * temporaries.
*/
svn_error_t *
svn_fs_fs__get_revision_proplist(apr_hash_t **proplist_p,
svn_fs_t *fs,
svn_revnum_t rev,
- apr_pool_t *pool);
+ svn_boolean_t refresh,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* Set the revision property list of revision REV in filesystem FS to
PROPLIST. Use POOL for temporary allocations. */
@@ -134,7 +142,7 @@ svn_fs_fs__pack_revprops_shard(const cha
const char *shard_path,
apr_int64_t shard,
int max_files_per_dir,
- apr_off_t max_pack_size,
+ apr_int64_t max_pack_size,
int compression_level,
svn_cancel_func_t cancel_func,
void *cancel_baton,
Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/stats.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/stats.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/stats.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/stats.c Mon Nov 30 10:24:16 2015
@@ -70,8 +70,9 @@ typedef enum rep_kind_t
*/
typedef struct rep_stats_t
{
- /* absolute offset in the file */
- apr_off_t offset;
+ /* offset in the revision file (phys. addressing) /
+ * item index within REVISION (log. addressing) */
+ apr_uint64_t item_index;
/* item length in bytes */
apr_uint64_t size;
@@ -92,8 +93,36 @@ typedef struct rep_stats_t
/* classification of the representation. values of rep_kind_t */
char kind;
+ /* length of the delta chain, including this representation,
+ * saturated to 255 - if need be */
+ apr_byte_t chain_length;
} rep_stats_t;
+/* Represents a link in the rep delta chain. REVISION + ITEM_INDEX points
+ * to BASE_REVISION + BASE_ITEM_INDEX. We collect this info while scanning
+ * a f7 repo in a single pass and resolve it afterwards. */
+typedef struct rep_ref_t
+{
+ /* Revision that contains this representation. */
+ svn_revnum_t revision;
+
+ /* Item index of this rep within REVISION. */
+ apr_uint64_t item_index;
+
+ /* Revision of the representation we deltified against.
+ * -1 if this representation is either PLAIN or a self-delta. */
+ svn_revnum_t base_revision;
+
+ /* Item index of that rep within BASE_REVISION. */
+ apr_uint64_t base_item_index;
+
+ /* Length of the PLAIN / DELTA line in the source file in bytes.
+ * We use this to update the info in the rep stats after scanning the
+ * whole file. */
+ apr_uint16_t header_size;
+
+} rep_ref_t;
+
/* Represents a single revision.
* There will be only one instance per revision. */
typedef struct revision_info_t
@@ -176,23 +205,6 @@ typedef struct query_t
void *cancel_baton;
} query_t;
-/* Return the length of REV_FILE in *FILE_SIZE.
- * Use SCRATCH_POOL for temporary allocations.
- */
-static svn_error_t *
-get_file_size(apr_off_t *file_size,
- svn_fs_fs__revision_file_t *rev_file,
- apr_pool_t *scratch_pool)
-{
- apr_finfo_t finfo;
-
- SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, rev_file->file,
- scratch_pool));
-
- *file_size = finfo.size;
- return SVN_NO_ERROR;
-}
-
/* Initialize the LARGEST_CHANGES member in STATS with a capacity of COUNT
* entries. Allocate the result in RESULT_POOL.
*/
@@ -345,13 +357,13 @@ add_change(svn_fs_fs__stats_t *stats,
/* Comparator used for binary search comparing the absolute file offset
* of a representation to some other offset. DATA is a *rep_stats_t,
- * KEY is a pointer to an apr_off_t.
+ * KEY is a pointer to an apr_uint64_t.
*/
static int
-compare_representation_offsets(const void *data, const void *key)
+compare_representation_item_index(const void *data, const void *key)
{
- apr_off_t lhs = (*(const rep_stats_t *const *)data)->offset;
- apr_off_t rhs = *(const apr_off_t *)key;
+ apr_uint64_t lhs = (*(const rep_stats_t *const *)data)->item_index;
+ apr_uint64_t rhs = *(const apr_uint64_t *)key;
if (lhs < rhs)
return -1;
@@ -362,7 +374,7 @@ compare_representation_offsets(const voi
* return it in *REVISION_INFO. For performance reasons, we skip the
* lookup if the info is already provided.
*
- * In that revision, look for the rep_stats_t object for offset OFFSET.
+ * In that revision, look for the rep_stats_t object for item ITEM_INDEX.
* If it already exists, set *IDX to its index in *REVISION_INFO's
* representations list and return the representation object. Otherwise,
* set the index to where it must be inserted and return NULL.
@@ -372,7 +384,7 @@ find_representation(int *idx,
query_t *query,
revision_info_t **revision_info,
svn_revnum_t revision,
- apr_off_t offset)
+ apr_uint64_t item_index)
{
revision_info_t *info;
*idx = -1;
@@ -392,14 +404,14 @@ find_representation(int *idx,
/* look for the representation */
*idx = svn_sort__bsearch_lower_bound(info->representations,
- &offset,
- compare_representation_offsets);
+ &item_index,
+ compare_representation_item_index);
if (*idx < info->representations->nelts)
{
/* return the representation, if this is the one we were looking for */
rep_stats_t *result
= APR_ARRAY_IDX(info->representations, *idx, rep_stats_t *);
- if (result->offset == offset)
+ if (result->item_index == item_index)
return result;
}
@@ -428,7 +440,7 @@ parse_representation(rep_stats_t **repre
/* look it up */
result = find_representation(&idx, query, &revision_info, rep->revision,
- (apr_off_t)rep->item_index);
+ rep->item_index);
if (!result)
{
/* not parsed, yet (probably a rep in the same revision).
@@ -436,9 +448,8 @@ parse_representation(rep_stats_t **repre
*/
result = apr_pcalloc(result_pool, sizeof(*result));
result->revision = rep->revision;
- result->expanded_size = (rep->expanded_size ? rep->expanded_size
- : rep->size);
- result->offset = (apr_off_t)rep->item_index;
+ result->expanded_size = rep->expanded_size;
+ result->item_index = rep->item_index;
result->size = rep->size;
/* In phys. addressing mode, follow link to the actual representation.
@@ -447,7 +458,8 @@ parse_representation(rep_stats_t **repre
if (!svn_fs_fs__use_log_addressing(query->fs))
{
svn_fs_fs__rep_header_t *header;
- apr_off_t offset = revision_info->offset + result->offset;
+ apr_off_t offset = revision_info->offset
+ + (apr_off_t)rep->item_index;
SVN_ERR_ASSERT(revision_info->rev_file);
SVN_ERR(svn_io_file_seek(revision_info->rev_file->file, APR_SET,
@@ -457,6 +469,23 @@ parse_representation(rep_stats_t **repre
scratch_pool, scratch_pool));
result->header_size = header->header_size;
+
+ /* Determine length of the delta chain. */
+ if (header->type == svn_fs_fs__rep_delta)
+ {
+ int base_idx;
+ rep_stats_t *base_rep
+ = find_representation(&base_idx, query, NULL,
+ header->base_revision,
+ header->base_item_index);
+
+ result->chain_length = 1 + MIN(base_rep->chain_length,
+ (apr_byte_t)0xfe);
+ }
+ else
+ {
+ result->chain_length = 1;
+ }
}
svn_sort__array_insert(revision_info->representations, &result, idx);
@@ -729,12 +758,12 @@ read_phys_pack_file(query_t *query,
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
- apr_off_t file_size = 0;
+ svn_filesize_t file_size = 0;
svn_fs_fs__revision_file_t *rev_file;
SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, base,
scratch_pool, scratch_pool));
- SVN_ERR(get_file_size(&file_size, rev_file, scratch_pool));
+ SVN_ERR(svn_io_file_size_get(&file_size, rev_file->file, scratch_pool));
/* process each revision in the pack file */
for (i = 0; i < query->shard_size; ++i)
@@ -798,7 +827,7 @@ read_phys_revision_file(query_t *query,
apr_pool_t *scratch_pool)
{
revision_info_t *info = apr_pcalloc(result_pool, sizeof(*info));
- apr_off_t file_size = 0;
+ svn_filesize_t file_size = 0;
svn_fs_fs__revision_file_t *rev_file;
/* cancellation support */
@@ -808,7 +837,7 @@ read_phys_revision_file(query_t *query,
/* read the whole pack file into memory */
SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, revision,
scratch_pool, scratch_pool));
- SVN_ERR(get_file_size(&file_size, rev_file, scratch_pool));
+ SVN_ERR(svn_io_file_size_get(&file_size, rev_file->file, scratch_pool));
/* create the revision info for the current rev */
info->representations = apr_array_make(result_pool, 4, sizeof(rep_stats_t*));
@@ -885,6 +914,70 @@ read_item(svn_stringbuf_t **contents,
return SVN_NO_ERROR;
}
+/* Predicate comparing the two rep_ref_t** LHS and RHS by the respective
+ * representation's revision.
+ */
+static int
+compare_representation_refs(const void *lhs, const void *rhs)
+{
+ svn_revnum_t lhs_rev = (*(const rep_ref_t *const *)lhs)->revision;
+ svn_revnum_t rhs_rev = (*(const rep_ref_t *const *)rhs)->revision;
+
+ if (lhs_rev < rhs_rev)
+ return -1;
+ return (lhs_rev > rhs_rev ? 1 : 0);
+}
+
+/* Given all the presentations found in a single rev / pack file as
+ * rep_ref_t * in REP_REFS, update the delta chain lengths in QUERY.
+ * REP_REFS and its contents can then be discarded.
+ */
+static svn_error_t *
+resolve_representation_refs(query_t *query,
+ apr_array_header_t *rep_refs)
+{
+ int i;
+
+ /* Because delta chains can only point to previous revs, after sorting
+ * REP_REFS, all base refs have already been updated. */
+ svn_sort__array(rep_refs, compare_representation_refs);
+
+ /* Build up the CHAIN_LENGTH values. */
+ for (i = 0; i < rep_refs->nelts; ++i)
+ {
+ int idx;
+ rep_ref_t *ref = APR_ARRAY_IDX(rep_refs, i, rep_ref_t *);
+ rep_stats_t *rep = find_representation(&idx, query, NULL,
+ ref->revision, ref->item_index);
+
+ /* No dangling pointers and all base reps have been processed. */
+ SVN_ERR_ASSERT(rep);
+ SVN_ERR_ASSERT(!rep->chain_length);
+
+ /* Set the HEADER_SIZE as we found it during the scan. */
+ rep->header_size = ref->header_size;
+
+ /* The delta chain got 1 element longer. */
+ if (ref->base_revision == SVN_INVALID_REVNUM)
+ {
+ rep->chain_length = 1;
+ }
+ else
+ {
+ rep_stats_t *base;
+
+ base = find_representation(&idx, query, NULL, ref->base_revision,
+ ref->base_item_index);
+ SVN_ERR_ASSERT(base);
+ SVN_ERR_ASSERT(base->chain_length);
+
+ rep->chain_length = 1 + MIN(base->chain_length, (apr_byte_t)0xfe);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* Process the logically addressed revision contents of revisions BASE to
* BASE + COUNT - 1 in QUERY.
*
@@ -905,6 +998,12 @@ read_log_rev_or_packfile(query_t *query,
int i;
svn_fs_fs__revision_file_t *rev_file;
+ /* We collect the delta chain links as we scan the file. Afterwards,
+ * we determine the lengths of those delta chains and throw this
+ * temporary container away. */
+ apr_array_header_t *rep_refs = apr_array_make(scratch_pool, 64,
+ sizeof(rep_ref_t *));
+
/* we will process every revision in the rev / pack file */
for (i = 0; i < count; ++i)
{
@@ -947,6 +1046,8 @@ read_log_rev_or_packfile(query_t *query,
/* process all entries (and later continue with the next block) */
for (i = 0; i < entries->nelts; ++i)
{
+ svn_stringbuf_t *item;
+ revision_info_t *info;
svn_fs_fs__p2l_entry_t *entry
= &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
@@ -959,32 +1060,64 @@ read_log_rev_or_packfile(query_t *query,
continue;
/* read and process interesting items */
+ info = APR_ARRAY_IDX(query->revisions, entry->item.revision,
+ revision_info_t*);
+
if (entry->type == SVN_FS_FS__ITEM_TYPE_NODEREV)
{
- svn_stringbuf_t *item;
- revision_info_t *info = APR_ARRAY_IDX(query->revisions,
- entry->item.revision,
- revision_info_t*);
SVN_ERR(read_item(&item, rev_file, entry, iterpool, iterpool));
SVN_ERR(read_noderev(query, item, info, result_pool, iterpool));
}
else if (entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES)
{
- svn_stringbuf_t *item;
- revision_info_t *info = APR_ARRAY_IDX(query->revisions,
- entry->item.revision,
- revision_info_t*);
SVN_ERR(read_item(&item, rev_file, entry, iterpool, iterpool));
info->change_count
= get_log_change_count(item->data + 0, item->len);
info->changes_len += entry->size;
}
+ else if ( (entry->type == SVN_FS_FS__ITEM_TYPE_FILE_REP)
+ || (entry->type == SVN_FS_FS__ITEM_TYPE_DIR_REP)
+ || (entry->type == SVN_FS_FS__ITEM_TYPE_FILE_PROPS)
+ || (entry->type == SVN_FS_FS__ITEM_TYPE_DIR_PROPS))
+ {
+ /* Collect the delta chain link. */
+ svn_fs_fs__rep_header_t *header;
+ rep_ref_t *ref = apr_pcalloc(scratch_pool, sizeof(*ref));
+
+ SVN_ERR(svn_io_file_aligned_seek(rev_file->file,
+ rev_file->block_size,
+ NULL, entry->offset,
+ iterpool));
+ SVN_ERR(svn_fs_fs__read_rep_header(&header,
+ rev_file->stream,
+ iterpool, iterpool));
+
+ ref->header_size = header->header_size;
+ ref->revision = entry->item.revision;
+ ref->item_index = entry->item.number;
+
+ if (header->type == svn_fs_fs__rep_delta)
+ {
+ ref->base_item_index = header->base_item_index;
+ ref->base_revision = header->base_revision;
+ }
+ else
+ {
+ ref->base_item_index = SVN_FS_FS__ITEM_INDEX_UNUSED;
+ ref->base_revision = SVN_INVALID_REVNUM;
+ }
+
+ APR_ARRAY_PUSH(rep_refs, rep_ref_t *) = ref;
+ }
/* advance offset */
offset += entry->size;
}
}
+ /* Resolve the delta chain links. */
+ SVN_ERR(resolve_representation_refs(query, rep_refs));
+
/* clean up and close file handles */
svn_pool_destroy(iterpool);
@@ -1111,6 +1244,7 @@ add_rep_stats(svn_fs_fs__representation_
stats->references += rep->ref_count;
stats->expanded_size += rep->ref_count * rep->expanded_size;
+ stats->chain_len += rep->chain_length;
}
/* Aggregate the info the in revision_info_t * array REVISIONS into the
Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/structure
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/structure?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/structure (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/structure Mon Nov 30 10:24:16 2015
@@ -198,9 +198,9 @@ Shard packing:
(i.e. same min packed revision)
Addressing:
- Format 1-6: Physical addressing; uses fixed positions within a rev file
+ Format 1+: Physical addressing; uses fixed positions within a rev file
Format 7+: Logical addressing; uses item index that will be translated
- on-the-fly to the actual rev / pack file location
+ on-the-fly to the actual rev / pack file location (default for 7+ created)
Repository IDs:
Format 1+: The first line of db/uuid contains the repository UUID
@@ -525,6 +525,7 @@ A revision file contains a concatenation
* Text and property representations
* Node-revisions
* The changed-path data
+ * Two offsets at the very end (physical addressing only)
* Index data (logical addressing only)
* Revision / pack file footer (logical addressing only)
@@ -578,8 +579,9 @@ defined:
representations may not be handled correctly by SVN before 1.7.20,
1.8.12 and 1.9.0, if they have 0 <size> fields for non-empty contents.
Releases 1.8.0 through 1.8.11 may have falsely created instances of
- that (see issue #4554). Finally, 0 <size> fields are NEVER legal for
- DELTA representations.
+ that (see issue #4554). Finally, 0 <size> fields are only ever legal
+ for DELTA representations if the reconstructed full-text is actually
+ empty.
The predecessor of a node-rev crosses both soft and true copies;
together with the count field, it allows efficient determination of
@@ -756,6 +758,9 @@ Format 7 introduces logical addressing t
to be translated / mapped to physical rev / pack file offsets.
These indexes are appended to the respective rev / pack file.
+The indexes map (revision number, item-index) pairs to absolute file offsets
+and absolute file offsets to (revision number, item-index, item metadata).
+
Details of the binary format used by these index files can be
found in structure-indexes.
Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/structure-indexes
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/structure-indexes?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/structure-indexes (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/structure-indexes Mon Nov 30 10:24:16 2015
@@ -13,10 +13,10 @@ to read and cache any data without trave
Rev and pack files are immutable, so the same is true for index data.
During a transaction or while packing a file, a proto index file gets
-written (actually, one log-to-phys and one phys-to-log). Its format is
-a simple concatenation of runtime structs and as such, an implementation
-detail subject to change. A proto index basically aggregates all the
-information that must later be transformed into the final index.
+written (actually, one log-to-phys and one phys-to-log). They use a
+simpler, less compact format with fixed record lengths. A proto index
+basically aggregates all the information that must later be transformed
+into the final index.
General design concerns
@@ -192,11 +192,11 @@ at the beginning of the file is optional
<bof> /* begin of proto index file for revision r and following */
(0, 0) /* mark start of revision r, optional for first rev */
- (off, item)* /* zero to many mappings in random order */
+ (off, item)* /* zero or more mappings in random order */
(0, 0) /* mark start of revision r + 1 */
- (off, item)* /* zero to many mappings in random order */
+ (off, item)* /* zero or more mappings in random order */
(0, 0) /* mark start of revision r + 2 */
- (off, item)* /* zero to many mappings in random order */
+ (off, item)* /* zero or more mappings in random order */
...
<eof> /* end of file. */
@@ -343,10 +343,12 @@ For performance reasons we use a modifie
h0 = fnv_1a([b0 b4 b8 ..]), ..., h3 = fnv_1a([b3 b7 b11 ..])
-* combine the big endian representation of these checksums plus the
- remnant of the original stream into a 12 to 15 byte long intermediate
+* concatenate the big endian representation of these checksums (4 bytes
+ each) plus the remnant of the original stream into a 16 to 19 byte long
+ intermediate:
- [i0 .. iK], 12 <= K+1 <= 15
+ [i0 .. iK] = [big-endian(h0) ... big-endian(h3) remnant ], 16 <= K+1 <= 19
-* FNV checksum = fnv_1a([i0 .. iK]) in big endian representation
+* fold the variable-length intermediate into a compact 32 bit checksum:
+ FNV checksum = fnv_1a([i0 .. iK])
Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/temp_serializer.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/temp_serializer.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/temp_serializer.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/temp_serializer.c Mon Nov 30 10:24:16 2015
@@ -103,9 +103,7 @@ serialize_svn_string(svn_temp_serializer
if (string == NULL)
return;
- svn_temp_serializer__push(context,
- (const void * const *)s,
- sizeof(*string));
+ svn_temp_serializer__push(context, (const void * const *)s, sizeof(**s));
/* the "string" content may actually be arbitrary binary data.
* Thus, we cannot use svn_temp_serializer__add_string. */
@@ -143,7 +141,7 @@ serialize_representation(svn_temp_serial
/* serialize the representation struct itself */
svn_temp_serializer__add_leaf(context,
(const void * const *)representation,
- sizeof(*rep));
+ sizeof(**representation));
}
/* auxiliary structure representing the content of a directory array */
@@ -153,6 +151,10 @@ typedef struct dir_data_t
* (it's int because the directory is an APR array) */
int count;
+ /** Current length of the in-txn in-disk representation of the directory.
+ * SVN_INVALID_FILESIZE if unknown (i.e. committed data). */
+ svn_filesize_t txn_filesize;
+
/* number of unused dir entry buckets in the index */
apr_size_t over_provision;
@@ -187,7 +189,7 @@ serialize_dir_entry(svn_temp_serializer_
svn_temp_serializer__push(context,
(const void * const *)entry_p,
- sizeof(svn_fs_dirent_t));
+ sizeof(**entry_p));
svn_fs_fs__id_serialize(context, &entry->id);
svn_temp_serializer__add_string(context, &entry->name);
@@ -198,24 +200,27 @@ serialize_dir_entry(svn_temp_serializer_
svn_temp_serializer__pop(context);
}
-/* Utility function to serialize the ENTRIES into a new serialization
+/* Utility function to serialize the DIR into a new serialization
* context to be returned. Allocation will be made form POOL.
*/
static svn_temp_serializer__context_t *
-serialize_dir(apr_array_header_t *entries, apr_pool_t *pool)
+serialize_dir(svn_fs_fs__dir_data_t *dir, apr_pool_t *pool)
{
dir_data_t dir_data;
int i = 0;
svn_temp_serializer__context_t *context;
+ apr_array_header_t *entries = dir->entries;
/* calculate sizes */
int count = entries->nelts;
apr_size_t over_provision = 2 + count / 4;
- apr_size_t entries_len = (count + over_provision) * sizeof(svn_fs_dirent_t*);
- apr_size_t lengths_len = (count + over_provision) * sizeof(apr_uint32_t);
+ apr_size_t total_count = count + over_provision;
+ apr_size_t entries_len = total_count * sizeof(*dir_data.entries);
+ apr_size_t lengths_len = total_count * sizeof(*dir_data.lengths);
/* copy the hash entries to an auxiliary struct of known layout */
dir_data.count = count;
+ dir_data.txn_filesize = dir->txn_filesize;
dir_data.over_provision = over_provision;
dir_data.operations = 0;
dir_data.entries = apr_palloc(pool, entries_len);
@@ -252,24 +257,29 @@ serialize_dir(apr_array_header_t *entrie
return context;
}
-/* Utility function to reconstruct a dir entries array from serialized data
+/* Utility function to reconstruct a dir entries struct from serialized data
* in BUFFER and DIR_DATA. Allocation will be made form POOL.
*/
-static apr_array_header_t *
+static svn_fs_fs__dir_data_t *
deserialize_dir(void *buffer, dir_data_t *dir_data, apr_pool_t *pool)
{
- apr_array_header_t *result
- = apr_array_make(pool, dir_data->count, sizeof(svn_fs_dirent_t *));
+ svn_fs_fs__dir_data_t *result;
apr_size_t i;
apr_size_t count;
svn_fs_dirent_t *entry;
svn_fs_dirent_t **entries;
+ /* Construct empty directory object. */
+ result = apr_pcalloc(pool, sizeof(*result));
+ result->entries
+ = apr_array_make(pool, dir_data->count, sizeof(svn_fs_dirent_t *));
+ result->txn_filesize = dir_data->txn_filesize;
+
/* resolve the reference to the entries array */
svn_temp_deserializer__resolve(buffer, (void **)&dir_data->entries);
entries = dir_data->entries;
- /* fixup the references within each entry and add it to the hash */
+ /* fixup the references within each entry and add it to the RESULT */
for (i = 0, count = dir_data->count; i < count; ++i)
{
svn_temp_deserializer__resolve(entries, (void **)&entries[i]);
@@ -280,7 +290,7 @@ deserialize_dir(void *buffer, dir_data_t
svn_fs_fs__id_deserialize(entry, (svn_fs_id_t **)&entry->id);
/* add the entry to the hash */
- APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = entry;
+ APR_ARRAY_PUSH(result->entries, svn_fs_dirent_t *) = entry;
}
/* return the now complete hash */
@@ -405,7 +415,7 @@ serialize_txdelta_ops(svn_temp_serialize
/* the ops form a contiguous chunk of memory with no further references */
svn_temp_serializer__add_leaf(context,
(const void * const *)ops,
- count * sizeof(svn_txdelta_op_t));
+ count * sizeof(**ops));
}
/* Utility function to serialize W in the given serialization CONTEXT.
@@ -417,9 +427,7 @@ serialize_txdeltawindow(svn_temp_seriali
svn_txdelta_window_t *window = *w;
/* serialize the window struct itself */
- svn_temp_serializer__push(context,
- (const void * const *)w,
- sizeof(svn_txdelta_window_t));
+ svn_temp_serializer__push(context, (const void * const *)w, sizeof(**w));
/* serialize its sub-structures */
serialize_txdelta_ops(context, &window->ops, window->num_ops);
@@ -496,8 +504,7 @@ svn_fs_fs__serialize_manifest(void **dat
apr_array_header_t *manifest = in;
*data_len = sizeof(apr_off_t) *manifest->nelts;
- *data = apr_palloc(pool, *data_len);
- memcpy(*data, manifest->elts, *data_len);
+ *data = apr_pmemdup(pool, manifest->elts, *data_len);
return SVN_NO_ERROR;
}
@@ -592,7 +599,7 @@ svn_fs_fs__serialize_properties(void **d
/* create our auxiliary data structure */
properties.count = apr_hash_count(hash);
properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1));
- properties.values = apr_palloc(pool, sizeof(const char*) * properties.count);
+ properties.values = apr_palloc(pool, sizeof(const svn_string_t *) * properties.count);
/* populate it with the hash entries */
for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i)
@@ -656,6 +663,44 @@ svn_fs_fs__deserialize_properties(void *
}
svn_error_t *
+svn_fs_fs__serialize_revprops(void **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool)
+{
+ svn_string_t *buffer = in;
+
+ *data = (void *)buffer->data;
+ *data_len = buffer->len;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__deserialize_revprops(void **out,
+ void *data,
+ apr_size_t data_len,
+ apr_pool_t *pool)
+{
+ apr_hash_t *properties;
+ svn_stream_t *stream;
+
+ svn_string_t buffer;
+ buffer.data = data;
+ buffer.len = data_len;
+
+ stream = svn_stream_from_string(&buffer, pool);
+ properties = svn_hash__make(pool);
+
+ SVN_ERR(svn_hash_read2(properties, stream, SVN_HASH_TERMINATOR, pool));
+
+ /* done */
+ *out = properties;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
svn_fs_fs__serialize_id(void **data,
apr_size_t *data_len,
void *in,
@@ -764,7 +809,7 @@ svn_fs_fs__serialize_dir_entries(void **
void *in,
apr_pool_t *pool)
{
- apr_array_header_t *dir = in;
+ svn_fs_fs__dir_data_t *dir = in;
/* serialize the dir content into a new serialization context
* and return the serialized data */
@@ -803,6 +848,20 @@ svn_fs_fs__get_sharded_offset(void **out
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_fs_fs__extract_dir_filesize(void **out,
+ const void *data,
+ apr_size_t data_len,
+ void *baton,
+ apr_pool_t *pool)
+{
+ const dir_data_t *dir_data = data;
+
+ *(svn_filesize_t *)out = dir_data->txn_filesize;
+
+ return SVN_NO_ERROR;
+}
+
/* Utility function that returns the lowest index of the first entry in
* *ENTRIES that points to a dir entry with a name equal or larger than NAME.
* If an exact match has been found, *FOUND will be set to TRUE. COUNT is
@@ -857,7 +916,7 @@ svn_fs_fs__extract_dir_entry(void **out,
apr_pool_t *pool)
{
const dir_data_t *dir_data = data;
- const char* name = baton;
+ const extract_dir_entry_baton_t *entry_baton = baton;
svn_boolean_t found;
/* resolve the reference to the entries array */
@@ -870,13 +929,14 @@ svn_fs_fs__extract_dir_entry(void **out,
/* binary search for the desired entry by name */
apr_size_t pos = find_entry((svn_fs_dirent_t **)entries,
- name,
+ entry_baton->name,
dir_data->count,
&found);
- /* de-serialize that entry or return NULL, if no match has been found */
+ /* de-serialize that entry or return NULL, if no match has been found.
+ * Be sure to check that the directory contents is still up-to-date. */
*out = NULL;
- if (found)
+ if (found && dir_data->txn_filesize == entry_baton->txn_filesize)
{
const svn_fs_dirent_t *source =
svn_temp_deserializer__ptr(entries, (const void *const *)&entries[pos]);
@@ -889,8 +949,7 @@ svn_fs_fs__extract_dir_entry(void **out,
apr_size_t size = lengths[pos];
/* copy & deserialize the entry */
- svn_fs_dirent_t *new_entry = apr_palloc(pool, size);
- memcpy(new_entry, source, size);
+ svn_fs_dirent_t *new_entry = apr_pmemdup(pool, source, size);
svn_temp_deserializer__resolve(new_entry, (void **)&new_entry->name);
svn_fs_fs__id_deserialize(new_entry, (svn_fs_id_t **)&new_entry->id);
@@ -911,31 +970,34 @@ slowly_replace_dir_entry(void **data,
{
replace_baton_t *replace_baton = (replace_baton_t *)baton;
dir_data_t *dir_data = (dir_data_t *)*data;
- apr_array_header_t *dir;
+ svn_fs_fs__dir_data_t *dir;
int idx = -1;
svn_fs_dirent_t *entry;
+ apr_array_header_t *entries;
SVN_ERR(svn_fs_fs__deserialize_dir_entries((void **)&dir,
*data,
dir_data->len,
pool));
- entry = svn_fs_fs__find_dir_entry(dir, replace_baton->name, &idx);
+ entries = dir->entries;
+ entry = svn_fs_fs__find_dir_entry(entries, replace_baton->name, &idx);
/* Replacement or removal? */
if (replace_baton->new_entry)
{
/* Replace ENTRY with / insert the NEW_ENTRY */
if (entry)
- APR_ARRAY_IDX(dir, idx, svn_fs_dirent_t *) = replace_baton->new_entry;
+ APR_ARRAY_IDX(entries, idx, svn_fs_dirent_t *)
+ = replace_baton->new_entry;
else
- svn_sort__array_insert(dir, &replace_baton->new_entry, idx);
+ svn_sort__array_insert(entries, &replace_baton->new_entry, idx);
}
else
{
/* Remove the old ENTRY. */
if (entry)
- svn_sort__array_delete(dir, idx, 1);
+ svn_sort__array_delete(entries, idx, 1);
}
return svn_fs_fs__serialize_dir_entries(data, data_len, dir, pool);
@@ -957,6 +1019,12 @@ svn_fs_fs__replace_dir_entry(void **data
svn_temp_serializer__context_t *context;
+ /* update the cached file length info.
+ * Because we are writing to the cache, it is fair to assume that the
+ * caller made sure that the current contents is consistent with the
+ * previous state of the directory file. */
+ dir_data->txn_filesize = replace_baton->txn_filesize;
+
/* after quite a number of operations, let's re-pack everything.
* This is to limit the number of wasted space as we cannot overwrite
* existing data but must always append. */
@@ -1046,6 +1114,18 @@ svn_fs_fs__replace_dir_entry(void **data
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_fs_fs__reset_txn_filesize(void **data,
+ apr_size_t *data_len,
+ void *baton,
+ apr_pool_t *pool)
+{
+ dir_data_t *dir_data = (dir_data_t *)*data;
+ dir_data->txn_filesize = SVN_INVALID_FILESIZE;
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_fs_fs__serialize_rep_header(void **data,
apr_size_t *data_len,
@@ -1055,7 +1135,7 @@ svn_fs_fs__serialize_rep_header(void **d
svn_fs_fs__rep_header_t *copy = apr_palloc(pool, sizeof(*copy));
*copy = *(svn_fs_fs__rep_header_t *)in;
- *data_len = sizeof(svn_fs_fs__rep_header_t);
+ *data_len = sizeof(*copy);
*data = copy;
return SVN_NO_ERROR;
Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/temp_serializer.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/temp_serializer.h?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/temp_serializer.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/temp_serializer.h Mon Nov 30 10:24:16 2015
@@ -156,6 +156,26 @@ svn_fs_fs__deserialize_properties(void *
apr_pool_t *pool);
/**
+ * Implements #svn_cache__serialize_func_t for a properties hash
+ * (@a in is an #apr_hash_t of svn_string_t elements, keyed by const char*).
+ */
+svn_error_t *
+svn_fs_fs__serialize_revprops(void **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool);
+
+/**
+ * Implements #svn_cache__deserialize_func_t for a properties hash
+ * (@a *out is an #apr_hash_t of svn_string_t elements, keyed by const char*).
+ */
+svn_error_t *
+svn_fs_fs__deserialize_revprops(void **out,
+ void *data,
+ apr_size_t data_len,
+ apr_pool_t *pool);
+
+/**
* Implements #svn_cache__serialize_func_t for #svn_fs_id_t
*/
svn_error_t *
@@ -192,7 +212,7 @@ svn_fs_fs__deserialize_node_revision(voi
apr_pool_t *pool);
/**
- * Implements #svn_cache__serialize_func_t for a directory contents array
+ * Implements #svn_cache__serialize_func_t for a #svn_fs_fs__dir_data_t
*/
svn_error_t *
svn_fs_fs__serialize_dir_entries(void **data,
@@ -201,7 +221,7 @@ svn_fs_fs__serialize_dir_entries(void **
apr_pool_t *pool);
/**
- * Implements #svn_cache__deserialize_func_t for a directory contents array
+ * Implements #svn_cache__deserialize_func_t for a #svn_fs_fs__dir_data_t
*/
svn_error_t *
svn_fs_fs__deserialize_dir_entries(void **out,
@@ -221,9 +241,38 @@ svn_fs_fs__get_sharded_offset(void **out
apr_pool_t *pool);
/**
+ * Implements #svn_cache__partial_getter_func_t.
+ * Set (svn_filesize_t) @a *out to the filesize info stored with the
+ * serialized directory in @a data of @a data_len. @a baton is unused.
+ */
+svn_error_t *
+svn_fs_fs__extract_dir_filesize(void **out,
+ const void *data,
+ apr_size_t data_len,
+ void *baton,
+ apr_pool_t *pool);
+
+/**
+ * Describes the entry to be found in a directory: Identifies the entry
+ * by @a name and requires the directory file size to be @a filesize.
+ */
+typedef struct extract_dir_entry_baton_t
+{
+ /** name of the directory entry to return */
+ const char *name;
+
+ /** Current length of the in-txn in-disk representation of the directory.
+ * SVN_INVALID_FILESIZE if unknown. */
+ svn_filesize_t txn_filesize;
+} extract_dir_entry_baton_t;
+
+
+/**
* Implements #svn_cache__partial_getter_func_t for a single
* #svn_fs_dirent_t within a serialized directory contents hash,
- * identified by its name (const char @a *baton).
+ * identified by its name (in (extract_dir_entry_baton_t *) @a *baton).
+ * If the filesize specified in the baton does not match the cached
+ * value for this directory, @a *out will be NULL as well.
*/
svn_error_t *
svn_fs_fs__extract_dir_entry(void **out,
@@ -236,7 +285,10 @@ svn_fs_fs__extract_dir_entry(void **out,
* Describes the change to be done to a directory: Set the entry
* identify by @a name to the value @a new_entry. If the latter is
* @c NULL, the entry shall be removed if it exists. Otherwise it
- * will be replaced or automatically added, respectively.
+ * will be replaced or automatically added, respectively. The
+ * @a filesize allows readers to identify stale cache data (e.g.
+ * due to concurrent access to txns); writers use it to update the
+ * cached file size info.
*/
typedef struct replace_baton_t
{
@@ -245,6 +297,10 @@ typedef struct replace_baton_t
/** directory entry to insert instead */
svn_fs_dirent_t *new_entry;
+
+ /** Current length of the in-txn in-disk representation of the directory.
+ * SVN_INVALID_FILESIZE if unknown. */
+ svn_filesize_t txn_filesize;
} replace_baton_t;
/**
@@ -259,6 +315,17 @@ svn_fs_fs__replace_dir_entry(void **data
apr_pool_t *pool);
/**
+ * Implements #svn_cache__partial_setter_func_t for a #svn_fs_fs__dir_data_t
+ * at @a *data, resetting its txn_filesize field to SVN_INVALID_FILESIZE.
+ * &a baton should be NULL.
+ */
+svn_error_t *
+svn_fs_fs__reset_txn_filesize(void **data,
+ apr_size_t *data_len,
+ void *baton,
+ apr_pool_t *pool);
+
+/**
* Implements #svn_cache__serialize_func_t for a #svn_fs_fs__rep_header_t.
*/
svn_error_t *