Author: stefan2
Date: Tue Aug 26 13:34:19 2014
New Revision: 1620597
URL: http://svn.apache.org/r1620597
Log:
On the revprop-caching-ng branch: Sync'ing FSX with FSFS:
Merge revisions r1619782-1620595 from subversion/libsvn_fs_fs
into subversion/libsvn_fs_x and resolve a few trivial text conflicts.
Rename svn_fs_fs_* functions appropriately.
This ports the new revprop caching infrastructure from FSFS to FSX.
Modified:
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/ (props
changed)
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/caching.c
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.c
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.h
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs_x.c
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/hotcopy.c
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/recovery.c
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.c
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.h
Propchange: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
Merged
/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs:r1619782-1620595
Modified:
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/caching.c
URL:
http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/caching.c?rev=1620597&r1=1620596&r2=1620597&view=diff
==============================================================================
--- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/caching.c
(original)
+++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/caching.c Tue
Aug 26 13:34:19 2014
@@ -126,19 +126,17 @@ read_config(const char **cache_namespace
* Revprop caching significantly speeds up operations like
* svn ls -v. However, it requires synchronization that may
* not be available or efficient in the current server setup.
- *
- * If the caller chose option "2", enable revprop caching if
- * the required API support is there to make it efficient.
+ * Option "2" is equivalent to "1".
*/
if (strcmp(svn_hash__get_cstring(fs->config,
SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
""), "2"))
*cache_revprops
= svn_hash__get_bool(fs->config,
- SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
- FALSE);
+ SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
+ FALSE);
else
- *cache_revprops = svn_named_atomic__is_efficient();
+ *cache_revprops = TRUE;
return SVN_NO_ERROR;
}
Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.c
URL:
http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.c?rev=1620597&r1=1620596&r2=1620597&view=diff
==============================================================================
--- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.c
(original)
+++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.c Tue Aug
26 13:34:19 2014
@@ -190,13 +190,26 @@ x_info(const void **fsx_info,
return SVN_NO_ERROR;
}
+/* Wrapper around svn_fs_x__get_revision_proplist() adapting between function
+ signatures. */
+static svn_error_t *
+x_revision_proplist(apr_hash_t **proplist_p,
+ svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_pool_t *pool)
+{
+ /* No need to bypass the caches for r/o access to revprops. */
+ return svn_error_trace(svn_fs_x__get_revision_proplist(proplist_p, fs,
+ rev, FALSE, pool));
+}
+
/* The vtable associated with a specific open filesystem. */
static fs_vtable_t fs_vtable = {
svn_fs_x__youngest_rev,
svn_fs_x__revision_prop,
- svn_fs_x__revision_proplist,
+ x_revision_proplist,
svn_fs_x__change_rev_prop,
svn_fs_x__set_uuid,
svn_fs_x__revision_root,
Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.h
URL:
http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.h?rev=1620597&r1=1620596&r2=1620597&view=diff
==============================================================================
--- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.h
(original)
+++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs.h Tue Aug
26 13:34:19 2014
@@ -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"
@@ -288,17 +287,9 @@ typedef struct fs_x_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;
+ the first access. May be also get closed and set to NULL again. */
+ apr_file_t *revprop_generation_file;
/* Revision property cache. Maps from (rev,generation) to apr_hash_t. */
svn_cache__t *revprop_cache;
Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs_x.c
URL:
http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs_x.c?rev=1620597&r1=1620596&r2=1620597&view=diff
==============================================================================
--- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs_x.c
(original)
+++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/fs_x.c Tue
Aug 26 13:34:19 2014
@@ -664,7 +664,7 @@ svn_fs_x__revision_proplist(apr_hash_t *
svn_revnum_t rev,
apr_pool_t *pool)
{
- SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, pool));
+ SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, FALSE, pool));
return SVN_NO_ERROR;
}
@@ -960,6 +960,9 @@ svn_fs_x__create(svn_fs_t *fs,
SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_txn_current_lock(fs, pool),
pool));
+ /* Initialize the revprop caching info. */
+ SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, pool));
+
/* This filesystem is ready. Stamp it with a format number. */
SVN_ERR(svn_fs_x__write_format(fs, FALSE, pool));
Modified:
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/hotcopy.c
URL:
http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/hotcopy.c?rev=1620597&r1=1620596&r2=1620597&view=diff
==============================================================================
--- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/hotcopy.c
(original)
+++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/hotcopy.c Tue
Aug 26 13:34:19 2014
@@ -891,12 +891,7 @@ hotcopy_body(void *baton, apr_pool_t *po
* 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_x__path_revprop_generation(src_fs, pool),
- &kind, pool));
- if (kind == svn_node_file)
- SVN_ERR(svn_fs_x__write_revprop_generation_file(dst_fs, 0, pool));
-
- SVN_ERR(svn_fs_x__cleanup_revprop_namespace(dst_fs));
+ SVN_ERR(svn_fs_x__reset_revprop_generation_file(dst_fs, pool));
return SVN_NO_ERROR;
}
Modified:
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/recovery.c
URL:
http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/recovery.c?rev=1620597&r1=1620596&r2=1620597&view=diff
==============================================================================
--- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/recovery.c
(original)
+++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/recovery.c
Tue Aug 26 13:34:19 2014
@@ -122,7 +122,7 @@ recover_body(void *baton, apr_pool_t *po
svn_node_kind_t youngest_revprops_kind;
/* Lose potentially corrupted data in temp files */
- SVN_ERR(svn_fs_x__cleanup_revprop_namespace(fs));
+ SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, pool));
/* We need to know the largest revision in the filesystem. */
SVN_ERR(recover_get_largest_revision(fs, &max_rev, pool));
Modified:
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.c
URL:
http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.c?rev=1620597&r1=1620596&r2=1620597&view=diff
==============================================================================
--- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.c
(original)
+++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.c
Tue Aug 26 13:34:19 2014
@@ -21,6 +21,7 @@
*/
#include <assert.h>
+#include <apr_md5.h>
#include "svn_pools.h"
#include "svn_hash.h"
@@ -42,11 +43,15 @@
process got aborted and that we have re-read revprops. */
#define REVPROP_CHANGE_TIMEOUT (10 * 1000000)
-/* The following are names of atomics that will be used to communicate
- * revprop updates across all processes on this machine. */
-#define ATOMIC_REVPROP_GENERATION "rev-prop-generation"
-#define ATOMIC_REVPROP_TIMEOUT "rev-prop-timeout"
-#define ATOMIC_REVPROP_NAMESPACE "rev-prop-atomics"
+/* In case of an inconsistent read, close the generation file, yield,
+ re-open and re-read. This is the number of times we try this before
+ giving up. */
+#define GENERATION_READ_RETRY_COUNT 100
+
+/* Maximum size of the generation number file contents (including NUL). */
+#define CHECKSUMMED_NUMBER_BUFFER_LEN \
+ (SVN_INT64_BUFFER_SIZE + 3 + APR_MD5_DIGESTSIZE * 2)
+
svn_error_t *
svn_fs_x__upgrade_pack_revprops(svn_fs_t *fs,
@@ -147,179 +152,251 @@ svn_fs_x__upgrade_cleanup_pack_revprops(
*
* Revprop caching needs to be activated and will be deactivated for the
* respective FS instance if the necessary infrastructure could not be
- * initialized. In deactivated mode, there is almost no runtime overhead
- * associated with revprop caching. As long as no revprops are being read
- * or changed, revprop caching imposes no overhead.
+ * initialized. As long as no revprops are being read or changed, revprop
+ * caching imposes no overhead.
*
* When activated, we cache revprops using (revision, generation) pairs
* as keys with the generation being incremented upon every revprop change.
* Since the cache is process-local, the generation needs to be tracked
* for at least as long as the process lives but may be reset afterwards.
*
- * To track the revprop generation, we use two-layer approach. On the lower
- * level, we use named atomics to have a system-wide consistent value for
- * the current revprop generation. However, those named atomics will only
- * remain valid for as long as at least one process / thread in the system
- * accesses revprops in the respective repository. The underlying shared
- * memory gets cleaned up afterwards.
- *
- * On the second level, we will use a persistent file to track the latest
- * revprop generation. It will be written upon each revprop change but
- * only be read if we are the first process to initialize the named atomics
- * with that value.
- *
- * The overhead for the second and following accesses to revprops is
- * almost zero on most systems.
- *
- *
- * Tech aspects:
- * -------------
- *
- * A problem is that we need to provide a globally available file name to
- * back the SHM implementation on OSes that need it. We can only assume
- * write access to some file within the respective repositories. Because
- * a given server process may access thousands of repositories during its
- * lifetime, keeping the SHM data alive for all of them is also not an
- * option.
- *
- * So, we store the new revprop generation on disk as part of each
- * setrevprop call, i.e. this write will be serialized and the write order
- * be guaranteed by the repository write lock.
- *
- * The only racy situation occurs when the data is being read again by two
- * processes concurrently but in that situation, the first process to
- * finish that procedure is guaranteed to be the only one that initializes
- * the SHM data. Since even writers will first go through that
- * initialization phase, they will never operate on stale data.
+ * We track the revprop generation in a persistent, unbuffered file that
+ * we may keep open for the lifetime of the svn_fs_t. It is the OS'
+ * responsibility to provide us with the latest contents upon read. To
+ * detect incomplete updates due to non-atomic reads, we put a MD5 checksum
+ * next to the actual generation number and verify that it matches.
+ *
+ * Since we cannot guarantee that the OS will provide us with up-to-date
+ * data buffers for open files, we re-open and re-read the file before
+ * modifying it. This will prevent lost updates.
+ *
+ * A race condition exists between switching to the modified revprop data
+ * and bumping the generation number. In particular, the process may crash
+ * just after switching to the new revprop data and before bumping the
+ * generation. To be able to detect this scenario, we bump the generation
+ * twice per revprop change: once immediately before (creating an odd number)
+ * and once after the atomic switch (even generation).
+ *
+ * A writer holding the write lock can immediately assume a crashed writer
+ * in case of an odd generation or they would not have been able to acquire
+ * the lock. A reader detecting an odd generation will use that number and
+ * be forced to re-read any revprop data - usually getting the new revprops
+ * already. If the generation file modification timestamp is too old, the
+ * reader will assume a crashed writer, acquire the write lock and bump
+ * the generation if it is still odd. So, for about REVPROP_CHANGE_TIMEOUT
+ * after the crash, reader caches may be stale.
*/
-/* Read revprop generation as stored on disk for repository FS. The result
- * is returned in *CURRENT. Default to 2 if no such file is available.
+/* If the revprop generation file in FS is open, close it. This is a no-op
+ * if the file is not open.
*/
static svn_error_t *
-read_revprop_generation_file(apr_int64_t *current,
- svn_fs_t *fs,
- apr_pool_t *pool)
+close_revprop_generation_file(svn_fs_t *fs,
+ apr_pool_t *scratch_pool)
{
- svn_error_t *err;
- apr_file_t *file;
- char buf[80];
- apr_size_t len;
- const char *path = svn_fs_x__path_revprop_generation(fs, pool);
-
- err = svn_io_file_open(&file, path,
- APR_READ | APR_BUFFERED,
- APR_OS_DEFAULT, pool);
- if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+ fs_x_data_t *ffd = fs->fsap_data;
+ if (ffd->revprop_generation_file)
{
- svn_error_clear(err);
- *current = 2;
-
- return SVN_NO_ERROR;
+ SVN_ERR(svn_io_file_close(ffd->revprop_generation_file, scratch_pool));
+ ffd->revprop_generation_file = NULL;
}
- SVN_ERR(err);
-
- len = sizeof(buf);
- SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
- /* Check that the first line contains only digits. */
- SVN_ERR(svn_fs_x__check_file_buffer_numeric(buf, 0, path,
- "Revprop Generation", pool));
- SVN_ERR(svn_cstring_atoi64(current, buf));
-
- return svn_io_file_close(file, pool);
+ return SVN_NO_ERROR;
}
-/* Write the CURRENT revprop generation to disk for repository FS.
+/* Make sure the revprop_generation member in FS is set. If READ_ONLY is
+ * set, open the file w/o write permission if the file is not open yet.
+ * The file is kept open if it has sufficient rights (or more) but will be
+ * closed and re-opened if it provided insufficient access rights.
+ *
+ * Call only for repos that support revprop caching.
*/
-svn_error_t *
-svn_fs_x__write_revprop_generation_file(svn_fs_t *fs,
- apr_int64_t current,
- apr_pool_t *pool)
+static svn_error_t *
+open_revprop_generation_file(svn_fs_t *fs,
+ svn_boolean_t read_only,
+ apr_pool_t *scratch_pool)
{
- char buf[SVN_INT64_BUFFER_SIZE];
- apr_size_t len = svn__i64toa(buf, current);
- buf[len] = '\n';
+ fs_x_data_t *ffd = fs->fsap_data;
+ apr_int32_t flags = read_only ? APR_READ : (APR_READ | APR_WRITE);
- SVN_ERR(svn_io_write_atomic(svn_fs_x__path_revprop_generation(fs, pool),
- buf, len + 1,
- NULL /* copy_perms */, pool));
+ /* Close the current file handle if it has insufficient rights. */
+ if ( ffd->revprop_generation_file
+ && (apr_file_flags_get(ffd->revprop_generation_file) & flags) != flags)
+ SVN_ERR(close_revprop_generation_file(fs, scratch_pool));
+
+ /* If not open already, open with sufficient rights. */
+ if (ffd->revprop_generation_file == NULL)
+ {
+ const char *path = svn_fs_x__path_revprop_generation(fs, scratch_pool);
+ SVN_ERR(svn_io_file_open(&ffd->revprop_generation_file, path,
+ flags, APR_OS_DEFAULT, fs->pool));
+ }
return SVN_NO_ERROR;
}
-/* Make sure the revprop_namespace member in FS is set. */
+/* Return the textual representation of NUMBER and its checksum in *BUFFER.
+ */
static svn_error_t *
-ensure_revprop_namespace(svn_fs_t *fs)
+checkedsummed_number(svn_stringbuf_t **buffer,
+ apr_int64_t number,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- fs_x_data_t *ffd = fs->fsap_data;
+ svn_checksum_t *checksum;
+ const char *digest;
+
+ char str[SVN_INT64_BUFFER_SIZE];
+ apr_size_t len = svn__i64toa(str, number);
+ str[len] = 0;
- return ffd->revprop_namespace == NULL
- ? svn_atomic_namespace__create(&ffd->revprop_namespace,
- svn_dirent_join(fs->path,
- ATOMIC_REVPROP_NAMESPACE,
- fs->pool),
- fs->pool)
- : SVN_NO_ERROR;
+ SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, str, len, scratch_pool));
+ digest = svn_checksum_to_cstring_display(checksum, scratch_pool);
+
+ *buffer = svn_stringbuf_createf(result_pool, "%s %s\n", digest, str);
+
+ return SVN_NO_ERROR;
}
-svn_error_t *
-svn_fs_x__cleanup_revprop_namespace(svn_fs_t *fs)
+/* Extract the generation number from the text BUFFER of LEN bytes and
+ * verify it against the checksum in the same BUFFER. If they match, return
+ * the generation in *NUMBER. Otherwise, return an error.
+ * BUFFER does not need to be NUL-terminated.
+ */
+static svn_error_t *
+verify_extract_number(apr_int64_t *number,
+ const char *buffer,
+ apr_size_t len,
+ apr_pool_t *scratch_pool)
{
- const char *name = svn_dirent_join(fs->path,
- ATOMIC_REVPROP_NAMESPACE,
- fs->pool);
- return svn_error_trace(svn_atomic_namespace__cleanup(name, fs->pool));
+ const char *digest_end = strchr(buffer, ' ');
+
+ /* Does the buffer even contain checksum _and_ number? */
+ if (digest_end != NULL)
+ {
+ svn_checksum_t *expected;
+ svn_checksum_t *actual;
+
+ SVN_ERR(svn_checksum_parse_hex(&expected, svn_checksum_md5, buffer,
+ scratch_pool));
+ SVN_ERR(svn_checksum(&actual, svn_checksum_md5, digest_end + 1,
+ (buffer + len) - (digest_end + 1), scratch_pool));
+
+ if (svn_checksum_match(expected, actual))
+ return svn_error_trace(svn_cstring_atoi64(number, digest_end + 1));
+ }
+
+ /* Incomplete buffer or not a match. */
+ return svn_error_create(SVN_ERR_FS_INVALID_GENERATION, NULL,
+ _("Invalid generation number data."));
}
-/* Make sure the revprop_generation member in FS is set and, if necessary,
- * initialized with the latest value stored on disk.
+/* Read revprop generation as stored on disk for repository FS. The result is
+ * returned in *CURRENT. Call only for repos that support revprop caching.
*/
static svn_error_t *
-ensure_revprop_generation(svn_fs_t *fs, apr_pool_t *pool)
+read_revprop_generation_file(apr_int64_t *current,
+ svn_fs_t *fs,
+ apr_pool_t *scratch_pool)
{
fs_x_data_t *ffd = fs->fsap_data;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ char buf[CHECKSUMMED_NUMBER_BUFFER_LEN];
+ apr_size_t len;
+ apr_off_t offset = 0;
+ int i;
+ svn_error_t *err = SVN_NO_ERROR;
- SVN_ERR(ensure_revprop_namespace(fs));
- if (ffd->revprop_generation == NULL)
+ /* Retry in case of incomplete file buffer updates. */
+ for (i = 0; i < GENERATION_READ_RETRY_COUNT; ++i)
{
- apr_int64_t current;
+ svn_error_clear(err);
+ svn_pool_clear(iterpool);
- SVN_ERR(svn_named_atomic__get(&ffd->revprop_generation,
- ffd->revprop_namespace,
- ATOMIC_REVPROP_GENERATION,
- TRUE));
-
- /* If the generation is at 0, we just created a new namespace
- * (it would be at least 2 otherwise). Read the latest generation
- * from disk and if we are the first one to initialize the atomic
- * (i.e. is still 0), set it to the value just gotten.
+ /* If we can't even access the data, things are very wrong.
+ * Don't retry in that case.
*/
- SVN_ERR(svn_named_atomic__read(¤t, ffd->revprop_generation));
- if (current == 0)
- {
- SVN_ERR(read_revprop_generation_file(¤t, fs, pool));
- SVN_ERR(svn_named_atomic__cmpxchg(NULL, current, 0,
- ffd->revprop_generation));
- }
+ SVN_ERR(open_revprop_generation_file(fs, TRUE, iterpool));
+ SVN_ERR(svn_io_file_seek(ffd->revprop_generation_file, APR_SET, &offset,
+ iterpool));
+
+ len = sizeof(buf);
+ SVN_ERR(svn_io_read_length_line(ffd->revprop_generation_file, buf, &len,
+ iterpool));
+
+ /* Some data has been read. It will most likely be complete and
+ * consistent. Extract and verify anyway. */
+ err = verify_extract_number(current, buf, len, iterpool);
+ if (!err)
+ break;
+
+ /* Got unlucky and data was invalid. Retry. */
+ SVN_ERR(close_revprop_generation_file(fs, iterpool));
+
+#if APR_HAS_THREADS
+ apr_thread_yield();
+#else
+ apr_sleep(0);
+#endif
}
- return SVN_NO_ERROR;
+ svn_pool_destroy(iterpool);
+
+ /* If we had to give up, propagate the error. */
+ return svn_error_trace(err);
}
-/* Make sure the revprop_timeout member in FS is set. */
+/* Write the CURRENT revprop generation to disk for repository FS.
+ * Call only for repos that support revprop caching.
+ */
static svn_error_t *
-ensure_revprop_timeout(svn_fs_t *fs)
+write_revprop_generation_file(svn_fs_t *fs,
+ apr_int64_t current,
+ apr_pool_t *scratch_pool)
{
fs_x_data_t *ffd = fs->fsap_data;
+ svn_stringbuf_t *buffer;
+ apr_off_t offset = 0;
+
+ SVN_ERR(checkedsummed_number(&buffer, current, scratch_pool, scratch_pool));
+
+ SVN_ERR(open_revprop_generation_file(fs, FALSE, scratch_pool));
+ SVN_ERR(svn_io_file_seek(ffd->revprop_generation_file, APR_SET, &offset,
+ scratch_pool));
+ SVN_ERR(svn_io_file_write_full(ffd->revprop_generation_file, buffer->data,
+ buffer->len, NULL, scratch_pool));
+ SVN_ERR(svn_io_file_flush_to_disk(ffd->revprop_generation_file,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_x__reset_revprop_generation_file(svn_fs_t *fs,
+ apr_pool_t *scratch_pool)
+{
+ const char *path = svn_fs_x__path_revprop_generation(fs, scratch_pool);
+ svn_stringbuf_t *buffer;
- SVN_ERR(ensure_revprop_namespace(fs));
- return ffd->revprop_timeout == NULL
- ? svn_named_atomic__get(&ffd->revprop_timeout,
- ffd->revprop_namespace,
- ATOMIC_REVPROP_TIMEOUT,
- TRUE)
- : SVN_NO_ERROR;
+ /* Unconditionally close the revprop generation file.
+ * Don't care about FS formats. This ensures consistent internal state. */
+ SVN_ERR(close_revprop_generation_file(fs, scratch_pool));
+
+ /* Unconditionally remove any old revprop generation file.
+ * Don't care about FS formats. This ensures consistent on-disk state
+ * for old format repositories. */
+ SVN_ERR(svn_io_remove_file2(path, TRUE, scratch_pool));
+
+ /* Write the initial revprop generation file contents, if supported by
+ * the current format. This ensures consistent on-disk state for new
+ * format repositories. */
+ SVN_ERR(checkedsummed_number(&buffer, 0, scratch_pool, scratch_pool));
+ SVN_ERR(svn_io_write_atomic(path, buffer->data, buffer->len, NULL,
+ scratch_pool));
+
+ /* ffd->revprop_generation_file will be re-opened on demand. */
+
+ return SVN_NO_ERROR;
}
/* Create an error object with the given MESSAGE and pass it to the
@@ -344,7 +421,8 @@ log_revprop_cache_init_warning(svn_fs_t
/* Test whether revprop cache and necessary infrastructure are
available in FS. */
static svn_boolean_t
-has_revprop_cache(svn_fs_t *fs, apr_pool_t *pool)
+has_revprop_cache(svn_fs_t *fs,
+ apr_pool_t *scratch_pool)
{
fs_x_data_t *ffd = fs->fsap_data;
svn_error_t *error;
@@ -353,23 +431,8 @@ has_revprop_cache(svn_fs_t *fs, apr_pool
if (ffd->revprop_cache == NULL)
return FALSE;
- /* is it efficient? */
- if (!svn_named_atomic__is_efficient())
- {
- /* access to it would be quite slow
- * -> disable the revprop cache for good
- */
- ffd->revprop_cache = NULL;
- log_revprop_cache_init_warning(fs, NULL,
- "Revprop caching for '%s' disabled"
- " because it would be inefficient.",
- pool);
-
- return FALSE;
- }
-
- /* try to access our SHM-backed infrastructure */
- error = ensure_revprop_generation(fs, pool);
+ /* try initialize our file-backed infrastructure */
+ error = open_revprop_generation_file(fs, TRUE, scratch_pool);
if (error)
{
/* failure -> disable revprop cache for good */
@@ -377,9 +440,9 @@ has_revprop_cache(svn_fs_t *fs, apr_pool
ffd->revprop_cache = NULL;
log_revprop_cache_init_warning(fs, error,
"Revprop caching for '%s' disabled "
- "because SHM infrastructure for revprop "
+ "because infrastructure for revprop "
"caching failed to initialize.",
- pool);
+ scratch_pool);
return FALSE;
}
@@ -393,8 +456,8 @@ typedef struct revprop_generation_fixup_
/* revprop generation to read */
apr_int64_t *generation;
- /* containing the revprop_generation member to query */
- fs_x_data_t *ffd;
+ /* file system context */
+ svn_fs_t *fs;
} revprop_generation_upgrade_t;
/* If the revprop generation has an odd value, it means the original writer
@@ -406,23 +469,31 @@ typedef struct revprop_generation_fixup_
*/
static svn_error_t *
revprop_generation_fixup(void *void_baton,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
revprop_generation_upgrade_t *baton = void_baton;
- assert(baton->ffd->has_write_lock);
+ fs_x_data_t *ffd = baton->fs->fsap_data;
+ assert(ffd->has_write_lock);
+
+ /* Make sure we don't operate on stale OS buffers. */
+ SVN_ERR(close_revprop_generation_file(baton->fs, scratch_pool));
/* Maybe, either the original revprop writer or some other reader has
already corrected / bumped the revprop generation. Thus, we need
- to read it again. */
- SVN_ERR(svn_named_atomic__read(baton->generation,
- baton->ffd->revprop_generation));
+ to read it again. However, we will now be the only ones changing
+ the file contents due to us holding the write lock. */
+ SVN_ERR(read_revprop_generation_file(baton->generation, baton->fs,
+ scratch_pool));
/* Cause everyone to re-read revprops upon their next access, if the
last revprop write did not complete properly. */
- while (*baton->generation % 2)
- SVN_ERR(svn_named_atomic__add(baton->generation,
- 1,
- baton->ffd->revprop_generation));
+ if (*baton->generation % 2)
+ {
+ ++*baton->generation;
+ SVN_ERR(write_revprop_generation_file(baton->fs,
+ *baton->generation,
+ scratch_pool));
+ }
return SVN_NO_ERROR;
}
@@ -433,42 +504,46 @@ revprop_generation_fixup(void *void_bato
static svn_error_t *
read_revprop_generation(apr_int64_t *generation,
svn_fs_t *fs,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
apr_int64_t current = 0;
fs_x_data_t *ffd = fs->fsap_data;
/* read the current revprop generation number */
- SVN_ERR(ensure_revprop_generation(fs, pool));
- SVN_ERR(svn_named_atomic__read(¤t, ffd->revprop_generation));
+ SVN_ERR(read_revprop_generation_file(¤t, fs, scratch_pool));
/* is an unfinished revprop write under the way? */
if (current % 2)
{
- apr_int64_t timeout = 0;
+ svn_boolean_t timeout = FALSE;
- /* read timeout for the write operation */
- SVN_ERR(ensure_revprop_timeout(fs));
- SVN_ERR(svn_named_atomic__read(&timeout, ffd->revprop_timeout));
-
- /* has the writer process been aborted,
- * i.e. has the timeout been reached?
+ /* Has the writer process been aborted?
+ * Either by timeout or by us being the writer now.
*/
- if (apr_time_now() > timeout)
+ if (!ffd->has_write_lock)
+ {
+ apr_time_t mtime;
+ SVN_ERR(svn_io_file_affected_time(&mtime,
+ svn_fs_x__path_revprop_generation(fs, scratch_pool),
+ scratch_pool));
+ timeout = apr_time_now() > mtime + REVPROP_CHANGE_TIMEOUT;
+ }
+
+ if (ffd->has_write_lock || timeout)
{
revprop_generation_upgrade_t baton;
baton.generation = ¤t;
- baton.ffd = ffd;
+ baton.fs = fs;
/* Ensure that the original writer process no longer exists by
* acquiring the write lock to this repository. Then, fix up
* the revprop generation.
*/
if (ffd->has_write_lock)
- SVN_ERR(revprop_generation_fixup(&baton, pool));
+ SVN_ERR(revprop_generation_fixup(&baton, scratch_pool));
else
SVN_ERR(svn_fs_x__with_write_lock(fs, revprop_generation_fixup,
- &baton, pool));
+ &baton, scratch_pool));
}
}
@@ -477,64 +552,54 @@ read_revprop_generation(apr_int64_t *gen
return SVN_NO_ERROR;
}
-/* Set the revprop generation to the next odd number to indicate that
- there is a revprop write process under way. If that times out,
- readers shall recover from that state & re-read revprops.
- Use the access object in FS to set the shared mem value. */
+/* Set the revprop generation in FS to the next odd number to indicate
+ that there is a revprop write process under way. Return that value
+ in *GENERATION. If the change times out, readers shall recover from
+ that state & re-read revprops.
+ This is a no-op for repo formats that don't support revprop caching. */
static svn_error_t *
-begin_revprop_change(svn_fs_t *fs, apr_pool_t *pool)
+begin_revprop_change(apr_int64_t *generation,
+ svn_fs_t *fs,
+ apr_pool_t *scratch_pool)
{
- apr_int64_t current;
fs_x_data_t *ffd = fs->fsap_data;
+ SVN_ERR_ASSERT(ffd->has_write_lock);
- /* set the timeout for the write operation */
- SVN_ERR(ensure_revprop_timeout(fs));
- SVN_ERR(svn_named_atomic__write(NULL,
- apr_time_now() + REVPROP_CHANGE_TIMEOUT,
- ffd->revprop_timeout));
+ /* Close and re-open to make sure we read the latest data. */
+ SVN_ERR(close_revprop_generation_file(fs, scratch_pool));
+ SVN_ERR(open_revprop_generation_file(fs, FALSE, scratch_pool));
- /* set the revprop generation to an odd value to indicate
- * that a write is in progress
+ /* Set the revprop generation to an odd value to indicate
+ * that a write is in progress.
*/
- SVN_ERR(ensure_revprop_generation(fs, pool));
- do
- {
- SVN_ERR(svn_named_atomic__add(¤t,
- 1,
- ffd->revprop_generation));
- }
- while (current % 2 == 0);
+ SVN_ERR(read_revprop_generation(generation, fs, scratch_pool));
+ ++*generation;
+ SVN_ERR(write_revprop_generation_file(fs, *generation, scratch_pool));
return SVN_NO_ERROR;
}
-/* Set the revprop generation to the next even number to indicate that
+/* Set the revprop generation in FS to the next even generation after
+ the odd value in GENERATION to indicate that
a) readers shall re-read revprops, and
- b) the write process has been completed (no recovery required)
- Use the access object in FS to set the shared mem value. */
+ b) the write process has been completed (no recovery required).
+ This is a no-op for repo formats that don't support revprop caching. */
static svn_error_t *
-end_revprop_change(svn_fs_t *fs, apr_pool_t *pool)
+end_revprop_change(svn_fs_t *fs,
+ apr_int64_t generation,
+ apr_pool_t *scratch_pool)
{
- apr_int64_t current = 1;
fs_x_data_t *ffd = fs->fsap_data;
+ SVN_ERR_ASSERT(ffd->has_write_lock);
+ SVN_ERR_ASSERT(generation % 2);
- /* set the revprop generation to an even value to indicate
- * that a write has been completed
+ /* Set the revprop generation to an even value to indicate
+ * that a write has been completed. Since we held the write
+ * lock, nobody else could have updated the file contents.
*/
- SVN_ERR(ensure_revprop_generation(fs, pool));
- do
- {
- SVN_ERR(svn_named_atomic__add(¤t,
- 1,
- ffd->revprop_generation));
- }
- while (current % 2);
-
- /* Save the latest generation to disk. FS is currently in a "locked"
- * state such that we can be sure the be the only ones to write that
- * file.
- */
- return svn_fs_x__write_revprop_generation_file(fs, current, pool);
+ SVN_ERR(write_revprop_generation_file(fs, generation + 1, scratch_pool));
+
+ return SVN_NO_ERROR;
}
/* Container for all data required to access the packed revprop file
@@ -943,6 +1008,7 @@ svn_error_t *
svn_fs_x__get_revision_proplist(apr_hash_t **proplist_p,
svn_fs_t *fs,
svn_revnum_t rev,
+ svn_boolean_t bypass_cache,
apr_pool_t *pool)
{
fs_x_data_t *ffd = fs->fsap_data;
@@ -955,7 +1021,7 @@ svn_fs_x__get_revision_proplist(apr_hash
SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, pool));
/* Try cache lookup first. */
- if (has_revprop_cache(fs, pool))
+ if (!bypass_cache && has_revprop_cache(fs, pool))
{
svn_boolean_t is_cached;
pair_cache_key_t key = { 0 };
@@ -1054,17 +1120,19 @@ switch_to_new_revprop(svn_fs_t *fs,
svn_boolean_t bump_generation,
apr_pool_t *pool)
{
+ apr_int64_t generation;
+
/* Now, we may actually be replacing revprops. Make sure that all other
threads and processes will know about this. */
if (bump_generation)
- SVN_ERR(begin_revprop_change(fs, pool));
+ SVN_ERR(begin_revprop_change(&generation, fs, pool));
SVN_ERR(svn_fs_x__move_into_place(tmp_path, final_path, perms_reference,
pool));
/* Indicate that the update (if relevant) has been completed. */
if (bump_generation)
- SVN_ERR(end_revprop_change(fs, pool));
+ SVN_ERR(end_revprop_change(fs, generation, pool));
/* Clean up temporary files, if necessary. */
if (files_to_delete)
@@ -1433,20 +1501,18 @@ svn_fs_x__set_revision_proplist(svn_fs_t
is_packed = svn_fs_x__is_packed_revprop(fs, rev);
/* Test whether revprops already exist for this revision.
- * Only then will we need to bump the revprop generation. */
- if (has_revprop_cache(fs, pool))
+ * Only then will we need to bump the revprop generation.
+ * The fact that they did not yet exist is never cached. */
+ if (is_packed)
{
- if (is_packed)
- {
- bump_generation = TRUE;
- }
- else
- {
- svn_node_kind_t kind;
- SVN_ERR(svn_io_check_path(svn_fs_x__path_revprops(fs, rev, pool),
- &kind, pool));
- bump_generation = kind != svn_node_none;
- }
+ bump_generation = TRUE;
+ }
+ else
+ {
+ svn_node_kind_t kind;
+ SVN_ERR(svn_io_check_path(svn_fs_x__path_revprops(fs, rev, pool),
+ &kind, pool));
+ bump_generation = kind != svn_node_none;
}
/* Serialize the new revprop data */
Modified:
subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.h
URL:
http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.h?rev=1620597&r1=1620596&r2=1620597&view=diff
==============================================================================
--- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.h
(original)
+++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x/revprops.h
Tue Aug 26 13:34:19 2014
@@ -29,17 +29,14 @@
extern "C" {
#endif /* __cplusplus */
-/* Write the CURRENT revprop generation to disk for repository FS.
+/* Auto-create / replace the revprop generation file in FS with its
+ * initial contents. In any case, FS will not hold an open handle to
+ * it after this function succeeds.
*/
svn_error_t *
-svn_fs_x__write_revprop_generation_file(svn_fs_t *fs,
- apr_int64_t current,
+svn_fs_x__reset_revprop_generation_file(svn_fs_t *fs,
apr_pool_t *pool);
-/* Make sure the revprop_namespace member in FS is set. */
-svn_error_t *
-svn_fs_x__cleanup_revprop_namespace(svn_fs_t *fs);
-
/* In the filesystem FS, pack all revprop shards up to min_unpacked_rev.
*
* NOTE: Keep the old non-packed shards around until after the format bump.
@@ -77,6 +74,7 @@ svn_fs_x__upgrade_cleanup_pack_revprops(
apr_pool_t *scratch_pool);
/* Read the revprops for revision REV in FS and return them in *PROPERTIES_P.
+ * If BYPASS_CACHE is set, don't consult the disks but always read from disk.
*
* Allocations will be done in POOL.
*/
@@ -84,6 +82,7 @@ svn_error_t *
svn_fs_x__get_revision_proplist(apr_hash_t **proplist_p,
svn_fs_t *fs,
svn_revnum_t rev,
+ svn_boolean_t bypass_cache,
apr_pool_t *pool);
/* Set the revision property list of revision REV in filesystem FS to