Report a revision's size on disk.
This adds an 'svnadmin rev-size' CLI and a libsvn_fs ioctl API to report
the total size in bytes of the representation on disk of a revision,
including rev-props, excluding FSFS indexes.
$ svnadmin rev-size /path/to/repo -r1
1337 bytes in revision 1
* subversion/svnadmin/svnadmin.c
(cmd_table): Add and document the 'rev-size' command.
(revision_size,
subcommand_rev_size): New.
* subversion/include/private/svn_fs_fs_private.h
(svn_fs_fs__ioctl_revision_size_input_t,
svn_fs_fs__ioctl_revision_size_output_t,
SVN_FS_FS__IOCTL_REVISION_SIZE): New.
* subversion/libsvn_fs_fs/fs.c
(fs_ioctl): Handle SVN_FS_FS__IOCTL_REVISION_SIZE.
* subversion/libsvn_fs_fs/fs_fs.h
* subversion/libsvn_fs_fs/stats.c
(svn_fs_fs__revision_size,
rev_size_index_entry_cb,
rev_size_baton_t): New.
* subversion/libsvn_fs_fs/revprops.h
* subversion/libsvn_fs_fs/revprops.c
(svn_fs_fs__get_revision_props_size): New.
* tools/client-side/bash_completion
(_svnadmin): Add 'rev-size'.
git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1857624 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/include/private/svn_fs_fs_private.h b/subversion/include/private/svn_fs_fs_private.h
index dfc03ce..de57558 100644
--- a/subversion/include/private/svn_fs_fs_private.h
+++ b/subversion/include/private/svn_fs_fs_private.h
@@ -340,6 +340,19 @@
SVN_FS_DECLARE_IOCTL_CODE(SVN_FS_FS__IOCTL_LOAD_INDEX, SVN_FS_TYPE_FSFS, 1002);
+typedef struct svn_fs_fs__ioctl_revision_size_input_t
+{
+ svn_revnum_t revision;
+} svn_fs_fs__ioctl_revision_size_input_t;
+
+typedef struct svn_fs_fs__ioctl_revision_size_output_t
+{
+ apr_off_t rev_size;
+} svn_fs_fs__ioctl_revision_size_output_t;
+
+/* See svn_fs_fs__revision_size(). */
+SVN_FS_DECLARE_IOCTL_CODE(SVN_FS_FS__IOCTL_REVISION_SIZE, SVN_FS_TYPE_FSFS, 1003);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/libsvn_fs_fs/fs.c b/subversion/libsvn_fs_fs/fs.c
index 3c0133e..c1dd42a 100644
--- a/subversion/libsvn_fs_fs/fs.c
+++ b/subversion/libsvn_fs_fs/fs.c
@@ -297,6 +297,17 @@
scratch_pool));
*output_p = NULL;
}
+ else if (ctlcode.code == SVN_FS_FS__IOCTL_REVISION_SIZE.code)
+ {
+ svn_fs_fs__ioctl_revision_size_input_t *input = input_void;
+ svn_fs_fs__ioctl_revision_size_output_t *output
+ = apr_pcalloc(result_pool, sizeof(*output));
+
+ SVN_ERR(svn_fs_fs__revision_size(&output->rev_size,
+ fs, input->revision,
+ scratch_pool));
+ *output_p = output;
+ }
else
return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
}
diff --git a/subversion/libsvn_fs_fs/fs_fs.h b/subversion/libsvn_fs_fs/fs_fs.h
index 0e981a3..f3e356b 100644
--- a/subversion/libsvn_fs_fs/fs_fs.h
+++ b/subversion/libsvn_fs_fs/fs_fs.h
@@ -347,4 +347,13 @@
apr_array_header_t *entries,
apr_pool_t *scratch_pool);
+/* Set *REV_SIZE to the total size of objects belonging to revision REVISION
+ * in FS. The size includes revision properties and excludes indexes.
+ */
+svn_error_t *
+svn_fs_fs__revision_size(apr_off_t *rev_size,
+ svn_fs_t *fs,
+ svn_revnum_t revision,
+ apr_pool_t *scratch_pool);
+
#endif
diff --git a/subversion/libsvn_fs_fs/revprops.c b/subversion/libsvn_fs_fs/revprops.c
index 6d41fd8..da11727 100644
--- a/subversion/libsvn_fs_fs/revprops.c
+++ b/subversion/libsvn_fs_fs/revprops.c
@@ -672,6 +672,64 @@
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_fs_fs__get_revision_props_size(apr_off_t *props_size_p,
+ svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_pool_t *scratch_pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ /* should they be available at all? */
+ SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, scratch_pool));
+
+ /* 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
+ * shard reads. */
+ if (!svn_fs_fs__is_packed_revprop(fs, rev))
+ {
+ const char *path = svn_fs_fs__path_revprops(fs, rev, scratch_pool);
+ svn_error_t *err;
+ apr_file_t *file;
+ svn_filesize_t file_size;
+
+ err = svn_io_file_open(&file, path, APR_FOPEN_READ, APR_OS_DEFAULT,
+ scratch_pool);
+ if (!err)
+ err = svn_io_file_size_get(&file_size, file, scratch_pool);
+ if (!err)
+ {
+ *props_size_p = (apr_off_t)file_size;
+ return SVN_NO_ERROR;
+ }
+ else if (!APR_STATUS_IS_ENOENT(err->apr_err)
+ || ffd->format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
+ {
+ return svn_error_trace(err);
+ }
+
+ /* fall through: maybe the revision got packed while we were looking */
+ svn_error_clear(err);
+ }
+
+ /* Try reading packed revprops. If that fails, REV is most
+ * likely invalid (or its revprops highly contested). */
+ {
+ packed_revprops_t *revprops;
+
+ /* ### This is inefficient -- reading all the revprops in a pack. We
+ should just read the index. */
+ SVN_ERR(read_pack_revprop(&revprops, fs, rev,
+ TRUE /*read_all*/, FALSE /*populate_cache*/,
+ scratch_pool));
+ *props_size_p = (apr_off_t)APR_ARRAY_IDX(revprops->sizes,
+ rev - revprops->start_revision,
+ apr_size_t);
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* Read the revprops for revision REV in FS and return them in *PROPERTIES_P.
*
* Allocations will be done in POOL.
diff --git a/subversion/libsvn_fs_fs/revprops.h b/subversion/libsvn_fs_fs/revprops.h
index 37063f9..32df183 100644
--- a/subversion/libsvn_fs_fs/revprops.h
+++ b/subversion/libsvn_fs_fs/revprops.h
@@ -62,6 +62,15 @@
void
svn_fs_fs__reset_revprop_cache(svn_fs_t *fs);
+/* Set *PROPS_SIZE_P to the size in bytes on disk of the revprops for
+ * revision REV in FS. The size excludes indexes.
+ */
+svn_error_t *
+svn_fs_fs__get_revision_props_size(apr_off_t *props_size_p,
+ svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_pool_t *scratch_pool);
+
/* 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.
*
diff --git a/subversion/libsvn_fs_fs/stats.c b/subversion/libsvn_fs_fs/stats.c
index 9377b33..a26a79d 100644
--- a/subversion/libsvn_fs_fs/stats.c
+++ b/subversion/libsvn_fs_fs/stats.c
@@ -36,6 +36,7 @@
#include "fs_fs.h"
#include "cached_data.h"
#include "low_level.h"
+#include "revprops.h"
#include "../libsvn_fs/fs-loader.h"
@@ -1396,3 +1397,96 @@
return SVN_NO_ERROR;
}
+
+/* Baton for rev_size_index_entry_cb. */
+struct rev_size_baton_t {
+ svn_revnum_t revision;
+ apr_off_t rev_size;
+};
+
+/* Implements svn_fs_fs__dump_index_func_t, summing object sizes for
+ * revision BATON->revision into BATON->rev_size.
+ */
+static svn_error_t *
+rev_size_index_entry_cb(const svn_fs_fs__p2l_entry_t *entry,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ struct rev_size_baton_t *b = baton;
+
+ if (entry->item.revision == b->revision)
+ b->rev_size += entry->size;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__revision_size(apr_off_t *rev_size,
+ svn_fs_t *fs,
+ svn_revnum_t revision,
+ apr_pool_t *scratch_pool)
+{
+ /* Get the size of the revision (excluding rev-props) */
+ if (svn_fs_fs__use_log_addressing(fs))
+ {
+ /* This works for a packed or a non-packed revision.
+ We could provide an optimized case for a non-packed revision
+ using svn_fs_fs__p2l_get_max_offset(). */
+ struct rev_size_baton_t b = { 0, 0 };
+
+ b.revision = revision;
+ SVN_ERR(svn_fs_fs__dump_index(fs, revision,
+ rev_size_index_entry_cb, &b,
+ NULL, NULL, scratch_pool));
+ *rev_size = b.rev_size;
+ }
+ else
+ {
+ svn_fs_fs__revision_file_t *rev_file;
+ svn_revnum_t min_unpacked_rev;
+
+ SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, revision,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_fs_fs__min_unpacked_rev(&min_unpacked_rev, fs,
+ scratch_pool));
+ if (revision < min_unpacked_rev)
+ {
+ int shard_size = svn_fs_fs__shard_size(fs);
+ apr_off_t start_offset, end_offset;
+
+ SVN_ERR(svn_fs_fs__get_packed_offset(&start_offset, fs, revision,
+ scratch_pool));
+ if (((revision + 1) % shard_size) == 0)
+ {
+ svn_filesize_t file_size;
+
+ SVN_ERR(svn_io_file_size_get(&file_size, rev_file->file, scratch_pool));
+ end_offset = (apr_off_t)file_size;
+ }
+ else
+ {
+ SVN_ERR(svn_fs_fs__get_packed_offset(&end_offset, fs,
+ revision + 1, scratch_pool));
+ }
+ *rev_size = (end_offset - start_offset);
+ }
+ else
+ {
+ svn_filesize_t file_size;
+
+ SVN_ERR(svn_io_file_size_get(&file_size, rev_file->file, scratch_pool));
+ *rev_size = (apr_off_t)file_size;
+ }
+
+ SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
+ }
+
+ /* Add the size of the rev-props */
+ {
+ apr_off_t size;
+
+ SVN_ERR(svn_fs_fs__get_revision_props_size(&size, fs, revision, scratch_pool));
+ *rev_size += size;
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnadmin/svnadmin.c b/subversion/svnadmin/svnadmin.c
index 0301046..85a6279 100644
--- a/subversion/svnadmin/svnadmin.c
+++ b/subversion/svnadmin/svnadmin.c
@@ -42,6 +42,7 @@
#include "svn_time.h"
#include "svn_user.h"
#include "svn_xml.h"
+#include "svn_fs.h"
#include "private/svn_cmdline_private.h"
#include "private/svn_opt_private.h"
@@ -49,6 +50,7 @@
#include "private/svn_subr_private.h"
#include "private/svn_cmdline_private.h"
#include "private/svn_fspath.h"
+#include "private/svn_fs_fs_private.h"
#include "svn_private_config.h"
@@ -114,6 +116,7 @@
subcommand_lstxns,
subcommand_pack,
subcommand_recover,
+ subcommand_rev_size,
subcommand_rmlocks,
subcommand_rmtxns,
subcommand_setlog,
@@ -518,6 +521,17 @@
)},
{svnadmin__wait} },
+ {"rev-size", subcommand_rev_size, {0}, {N_(
+ "usage: svnadmin rev-size REPOS_PATH -r REVISION\n"
+ "\n"), N_(
+ "Print the total size in bytes of the representation on disk of\n"
+ "revision REVISION.\n"
+ "\n"), N_(
+ "The size includes revision properties and excludes FSFS indexes.\n"
+ )},
+ {'r', 'q', 'M'},
+ { {'q', "print only the size and a newline"} } },
+
{"rmlocks", subcommand_rmlocks, {0}, {N_(
"usage: svnadmin rmlocks REPOS_PATH LOCKED_PATH...\n"
"\n"), N_(
@@ -2847,6 +2861,68 @@
}
+/* Set *REV_SIZE to the total size in bytes of the representation on disk
+ * of revision REVISION in FS.
+ *
+ * This is implemented only for FSFS repositories, and otherwise returns
+ * an SVN_ERR_UNSUPPORTED_FEATURE error.
+ *
+ * The size includes revision properties and excludes FSFS indexes.
+ */
+static svn_error_t *
+revision_size(apr_off_t *rev_size,
+ svn_fs_t *fs,
+ svn_revnum_t revision,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ svn_fs_fs__ioctl_revision_size_input_t input = {0};
+ svn_fs_fs__ioctl_revision_size_output_t *output;
+
+ input.revision = revision;
+ err = svn_fs_ioctl(fs, SVN_FS_FS__IOCTL_REVISION_SIZE,
+ &input, (void **)&output,
+ check_cancel, NULL, scratch_pool, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE)
+ {
+ return svn_error_quick_wrapf(err,
+ _("Revision size query is not implemented "
+ "for the filesytem type found in '%s'"),
+ svn_fs_path(fs, scratch_pool));
+ }
+ SVN_ERR(err);
+
+ *rev_size = output->rev_size;
+ return SVN_NO_ERROR;
+}
+
+/* This implements `svn_opt_subcommand_t'. */
+svn_error_t *
+subcommand_rev_size(apr_getopt_t *os, void *baton, apr_pool_t *pool)
+{
+ struct svnadmin_opt_state *opt_state = baton;
+ svn_revnum_t revision;
+ apr_off_t rev_size;
+ svn_repos_t *repos;
+
+ if (opt_state->start_revision.kind != svn_opt_revision_number
+ || opt_state->end_revision.kind != svn_opt_revision_unspecified)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Invalid revision specifier"));
+ revision = opt_state->start_revision.value.number;
+
+ SVN_ERR(open_repos(&repos, opt_state->repository_path, opt_state, pool));
+ SVN_ERR(revision_size(&rev_size, svn_repos_fs(repos), revision, pool));
+
+ if (opt_state->quiet)
+ SVN_ERR(svn_cmdline_printf(pool, "%"APR_OFF_T_FMT"\n", rev_size));
+ else
+ SVN_ERR(svn_cmdline_printf(pool, _("%12"APR_OFF_T_FMT" bytes in revision %ld\n"),
+ rev_size, revision));
+
+ return SVN_NO_ERROR;
+}
+
/** Main. **/
diff --git a/tools/client-side/bash_completion b/tools/client-side/bash_completion
index d2095ca..44d15bb 100644
--- a/tools/client-side/bash_completion
+++ b/tools/client-side/bash_completion
@@ -1139,7 +1139,7 @@
# Possible expansions, without pure-prefix abbreviations such as "h".
cmds='crashtest create delrevprop deltify dump dump-revprops freeze \
help hotcopy info list-dblogs list-unused-dblogs \
- load load-revprops lock lslocks lstxns pack recover rmlocks \
+ load load-revprops lock lslocks lstxns pack recover rev-size rmlocks \
rmtxns setlog setrevprop setuuid unlock upgrade verify --version'
if [[ $COMP_CWORD -eq 1 ]] ; then
@@ -1211,6 +1211,9 @@
recover)
cmdOpts="--wait"
;;
+ rev-size)
+ cmdOpts="-r --revision -M --memory-cache-size -q --quiet"
+ ;;
rmlocks)
cmdOpts="-q --quiet"
;;