You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/07/19 23:21:36 UTC

svn commit: r1505026 - in /subversion/trunk: ./ subversion/include/private/ subversion/libsvn_fs_fs/ subversion/libsvn_subr/ subversion/tests/libsvn_subr/ tools/server-side/

Author: stefan2
Date: Fri Jul 19 21:21:36 2013
New Revision: 1505026

URL: http://svn.apache.org/r1505026
Log:
Merge revisions r1442051,1442504,1443803,1444690,1477166,1478055,1490674
from branches/fsfs-format7.  These introduce the notion of priorities to our 
caches and plus comprise all other caching improvements.

A few conflicts had to be resolved.

Modified:
    subversion/trunk/   (props changed)
    subversion/trunk/subversion/include/private/svn_cache.h
    subversion/trunk/subversion/libsvn_fs_fs/caching.c
    subversion/trunk/subversion/libsvn_subr/cache-inprocess.c
    subversion/trunk/subversion/libsvn_subr/cache-membuffer.c
    subversion/trunk/subversion/libsvn_subr/cache-memcache.c
    subversion/trunk/subversion/libsvn_subr/cache.c
    subversion/trunk/subversion/libsvn_subr/cache.h
    subversion/trunk/subversion/tests/libsvn_subr/cache-test.c
    subversion/trunk/tools/server-side/fsfs-stats.c

Propchange: subversion/trunk/
------------------------------------------------------------------------------
  Merged /subversion/branches/fsfs-format7:r1442051,1442504,1443803,1444690,1477166,1478055,1490674

Modified: subversion/trunk/subversion/include/private/svn_cache.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_cache.h?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_cache.h (original)
+++ subversion/trunk/subversion/include/private/svn_cache.h Fri Jul 19 21:21:36 2013
@@ -305,6 +305,33 @@ svn_cache__membuffer_cache_create(svn_me
                                   apr_pool_t *result_pool);
 
 /**
+ * @defgroup Standard priority classes for #svn_cache__create_membuffer_cache.
+ * @{
+ */
+
+/**
+ * Data in this priority class should not be removed from the cache unless
+ * absolutely necessary.  Use of this should be very restricted.
+ */
+#define SVN_CACHE__MEMBUFFER_HIGH_PRIORITY   10000
+
+/**
+ * Data in this priority class has a good chance to remain in cache unless
+ * there is more data in this class than the cache's capcity.  Use of this
+ * as the default for all information that is costly to fetch from disk.
+ */
+#define SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY 1000
+
+/**
+ * Data in this priority class will be removed as soon as the cache starts
+ * filling up.  Use of this for ephemeral data that can easisly be acquired
+ * again from other sources.
+ */
+#define SVN_CACHE__MEMBUFFER_LOW_PRIORITY      100
+
+/** @} */
+
+/**
  * Creates a new cache in @a *cache_p, storing the data in a potentially
  * shared @a membuffer object.  The elements in the cache will be indexed
  * by keys of length @a klen, which may be APR_HASH_KEY_STRING if they
@@ -312,7 +339,9 @@ svn_cache__membuffer_cache_create(svn_me
  * serialize_func and deserialized using @a deserialize_func.  Because
  * the same memcache object may cache many different kinds of values
  * form multiple caches, @a prefix should be specified to differentiate
- * this cache from other caches.  @a *cache_p will be allocated in @a result_pool.
+ * this cache from other caches.  All entries written through this cache
+ * interface will be assigned into the given @a priority class.  @a *cache_p
+ * will be allocated in @a result_pool.
  *
  * If @a deserialize_func is NULL, then the data is returned as an
  * svn_string_t; if @a serialize_func is NULL, then the data is
@@ -331,6 +360,7 @@ svn_cache__create_membuffer_cache(svn_ca
                                   svn_cache__deserialize_func_t deserialize,
                                   apr_ssize_t klen,
                                   const char *prefix,
+                                  apr_uint32_t priority,
                                   svn_boolean_t thread_safe,
                                   apr_pool_t *result_pool);
 
@@ -377,6 +407,18 @@ svn_cache__get(void **value,
                apr_pool_t *result_pool);
 
 /**
+ * Looks for an entry indexed by @a key in @a cache,  setting @a *found
+ * to TRUE if an entry has been found and FALSE otherwise.  @a key may be
+ * NULL in which case @a *found will be FALSE.  Temporary allocations will
+ * be made from @a scratch_pool.
+ */
+svn_error_t *
+svn_cache__has_key(svn_boolean_t *found,
+                   svn_cache__t *cache,
+                   const void *key,
+                   apr_pool_t *scratch_pool);
+
+/**
  * Stores the value @a value under the key @a key in @a cache.  Uses @a
  * scratch_pool for temporary allocations.  The cache makes copies of
  * @a key and @a value if necessary (that is, @a key and @a value may

Modified: subversion/trunk/subversion/libsvn_fs_fs/caching.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/caching.c?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/caching.c (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/caching.c Fri Jul 19 21:21:36 2013
@@ -305,7 +305,8 @@ init_callbacks(svn_cache__t *cache,
  * Creates memcache if MEMCACHE is not NULL. Creates membuffer cache if
  * MEMBUFFER is not NULL. Fallbacks to inprocess cache if MEMCACHE and
  * MEMBUFFER are NULL and pages is non-zero.  Sets *CACHE_P to NULL
- * otherwise.
+ * otherwise.  Use the given PRIORITY class for the new cache.  If it
+ * is 0, then use the default priority class.
  *
  * Unless NO_HANDLER is true, register an error handler that reports errors
  * as warnings to the FS warning callback.
@@ -322,6 +323,7 @@ create_cache(svn_cache__t **cache_p,
              svn_cache__deserialize_func_t deserializer,
              apr_ssize_t klen,
              const char *prefix,
+             apr_uint32_t priority,
              svn_fs_t *fs,
              svn_boolean_t no_handler,
              apr_pool_t *pool)
@@ -329,6 +331,8 @@ create_cache(svn_cache__t **cache_p,
   svn_cache__error_handler_t error_handler = no_handler
                                            ? NULL
                                            : warn_and_fail_on_cache_errors;
+  if (priority == 0)
+    priority = SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY;
 
   if (memcache)
     {
@@ -343,7 +347,7 @@ create_cache(svn_cache__t **cache_p,
     {
       SVN_ERR(svn_cache__create_membuffer_cache(
                 cache_p, membuffer, serializer, deserializer,
-                klen, prefix, FALSE, pool));
+                klen, prefix, priority, FALSE, pool));
     }
   else if (pages)
     {
@@ -421,6 +425,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                        svn_fs_fs__deserialize_id,
                        sizeof(svn_revnum_t),
                        apr_pstrcat(pool, prefix, "RRI", (char *)NULL),
+                       0,
                        fs,
                        no_handler,
                        fs->pool));
@@ -435,6 +440,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                        svn_fs_fs__dag_deserialize,
                        APR_HASH_KEY_STRING,
                        apr_pstrcat(pool, prefix, "DAG", (char *)NULL),
+                       0,
                        fs,
                        no_handler,
                        fs->pool));
@@ -451,6 +457,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                        svn_fs_fs__deserialize_dir_entries,
                        APR_HASH_KEY_STRING,
                        apr_pstrcat(pool, prefix, "DIR", (char *)NULL),
+                       0,
                        fs,
                        no_handler,
                        fs->pool));
@@ -466,6 +473,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                        sizeof(svn_revnum_t),
                        apr_pstrcat(pool, prefix, "PACK-MANIFEST",
                                    (char *)NULL),
+                       0,
                        fs,
                        no_handler,
                        fs->pool));
@@ -479,6 +487,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                        svn_fs_fs__deserialize_node_revision,
                        sizeof(pair_cache_key_t),
                        apr_pstrcat(pool, prefix, "NODEREVS", (char *)NULL),
+                       0,
                        fs,
                        no_handler,
                        fs->pool));
@@ -492,6 +501,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                        svn_fs_fs__deserialize_changes,
                        sizeof(svn_revnum_t),
                        apr_pstrcat(pool, prefix, "CHANGES", (char *)NULL),
+                       0,
                        fs,
                        no_handler,
                        fs->pool));
@@ -507,6 +517,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            NULL, NULL,
                            sizeof(pair_cache_key_t),
                            apr_pstrcat(pool, prefix, "TEXT", (char *)NULL),
+                           SVN_CACHE__MEMBUFFER_LOW_PRIORITY,
                            fs,
                            no_handler,
                            fs->pool));
@@ -520,6 +531,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            sizeof(pair_cache_key_t),
                            apr_pstrcat(pool, prefix, "PROP",
                                        (char *)NULL),
+                           0,
                            fs,
                            no_handler,
                            fs->pool));
@@ -533,6 +545,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            APR_HASH_KEY_STRING,
                            apr_pstrcat(pool, prefix, "MERGEINFO",
                                        (char *)NULL),
+                           0,
                            fs,
                            no_handler,
                            fs->pool));
@@ -546,6 +559,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            APR_HASH_KEY_STRING,
                            apr_pstrcat(pool, prefix, "HAS_MERGEINFO",
                                        (char *)NULL),
+                           0,
                            fs,
                            no_handler,
                            fs->pool));
@@ -570,6 +584,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            sizeof(pair_cache_key_t),
                            apr_pstrcat(pool, prefix, "REVPROP",
                                        (char *)NULL),
+                           0,
                            fs,
                            no_handler,
                            fs->pool));
@@ -591,6 +606,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            APR_HASH_KEY_STRING,
                            apr_pstrcat(pool, prefix, "TXDELTA_WINDOW",
                                        (char *)NULL),
+                           0,
                            fs,
                            no_handler,
                            fs->pool));
@@ -604,6 +620,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            APR_HASH_KEY_STRING,
                            apr_pstrcat(pool, prefix, "COMBINED_WINDOW",
                                        (char *)NULL),
+                           SVN_CACHE__MEMBUFFER_LOW_PRIORITY,
                            fs,
                            no_handler,
                            fs->pool));
@@ -707,6 +724,7 @@ svn_fs_fs__initialize_txn_caches(svn_fs_
                        APR_HASH_KEY_STRING,
                        apr_pstrcat(pool, prefix, "TXNDIR",
                                    (char *)NULL),
+                       0,
                        fs,
                        TRUE,
                        pool));

Modified: subversion/trunk/subversion/libsvn_subr/cache-inprocess.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cache-inprocess.c?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/cache-inprocess.c (original)
+++ subversion/trunk/subversion/libsvn_subr/cache-inprocess.c Fri Jul 19 21:21:36 2013
@@ -246,6 +246,37 @@ inprocess_cache_get(void **value_p,
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+inprocess_cache_has_key_internal(svn_boolean_t *found,
+                                 inprocess_cache_t *cache,
+                                 const void *key,
+                                 apr_pool_t *scratch_pool)
+{
+  *found = apr_hash_get(cache->hash, key, cache->klen) != NULL;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+inprocess_cache_has_key(svn_boolean_t *found,
+                        void *cache_void,
+                        const void *key,
+                        apr_pool_t *scratch_pool)
+{
+  inprocess_cache_t *cache = cache_void;
+
+  if (key)
+    SVN_MUTEX__WITH_LOCK(cache->mutex,
+                         inprocess_cache_has_key_internal(found,
+                                                          cache,
+                                                          key,
+                                                          scratch_pool));
+  else
+    *found = FALSE;
+
+  return SVN_NO_ERROR;
+}
+
 /* Removes PAGE from the LRU list, removes all of its entries from
  * CACHE's hash, clears its pool, and sets its entry pointer to NULL.
  * Finally, puts it in the "partial page" slot in the cache and sets
@@ -604,6 +635,7 @@ inprocess_cache_get_info(void *cache_voi
 
 static svn_cache__vtable_t inprocess_cache_vtable = {
   inprocess_cache_get,
+  inprocess_cache_has_key,
   inprocess_cache_set,
   inprocess_cache_iter,
   inprocess_cache_is_cachable,

Modified: subversion/trunk/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cache-membuffer.c?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/trunk/subversion/libsvn_subr/cache-membuffer.c Fri Jul 19 21:21:36 2013
@@ -361,6 +361,10 @@ typedef struct entry_t
    */
   apr_uint32_t previous;
 
+  /* Priority of this entry.  This entry will not be replaced by lower-
+   * priority items.
+   */
+  apr_uint32_t priority;
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
   /* Remember type, content and key hashes.
    */
@@ -1013,13 +1017,15 @@ find_entry(svn_membuffer_t *cache,
       if (group->used == GROUP_SIZE)
         {
           /* every entry gets the same chance of being removed.
-           * Otherwise, we free the first entry, fill it and
-           * remove it again on the next occasion without considering
-           * the other entries in this group.
+           * Otherwise, we free the first entry, fill it and remove it
+           * again on the next occasion without considering the other
+           * entries in this group.  Also, apply priorities strictly.
            */
           entry = &group->entries[rand() % GROUP_SIZE];
           for (i = 1; i < GROUP_SIZE; ++i)
-            if (entry->hit_count > group->entries[i].hit_count)
+            if (   (entry->priority > group->entries[i].priority)
+                || (   entry->priority == group->entries[i].priority
+                    && entry->hit_count > group->entries[i].hit_count))
               entry = &group->entries[i];
 
           /* for the entries that don't have been removed,
@@ -1128,6 +1134,16 @@ ensure_data_insertable_l2(svn_membuffer_
   apr_uint64_t average_hit_value;
   apr_uint64_t threshold;
 
+  /* accumulated size of the entries that have been removed to make
+   * room for the new one.
+   */
+  apr_size_t moved_size = 0;
+
+  /* count the number of entries that got moved.  A single large entry
+   * being moved is not enough to reject an insertion.
+   */
+  apr_size_t moved_count = 0;
+
   /* accumulated "worth" of items dropped so far */
   apr_size_t drop_hits = 0;
 
@@ -1159,10 +1175,16 @@ ensure_data_insertable_l2(svn_membuffer_
       if (end >= to_fit_in->size + cache->l2.current_data)
         return TRUE;
 
+      /* Don't be too eager to cache data.  If a lot of data has been 
+       * moved around, the current item has probably a relatively low
+       * priority.  So, give up after some time.
+       */
+      if (moved_size > 8 * to_fit_in->size && moved_count > 3)
+        return FALSE;
+
       /* if the net worth (in hits) of items removed is already larger
        * than what we want to insert, reject TO_FIT_IN because it still
-       * does not fit in.
-       */
+       * does not fit in. */
       if (drop_hits > to_fit_in->hit_count)
         return FALSE;
 
@@ -1180,8 +1202,15 @@ ensure_data_insertable_l2(svn_membuffer_
         }
       else
         {
+          svn_boolean_t keep;
           entry = get_entry(cache, cache->l2.next);
 
+          /* Reject insertion for entries with low priority, if the current
+           * entry has seen recent hits. */
+          if (   entry->hit_count
+              && to_fit_in->priority < SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY)
+            return FALSE;
+
           /* Keep entries that are very small. Those are likely to be data
            * headers or similar management structures. So, they are probably
            * important while not occupying much space.
@@ -1190,56 +1219,75 @@ ensure_data_insertable_l2(svn_membuffer_
           if (   (apr_uint64_t)entry->size * cache->used_entries
                < cache->data_used / 8)
             {
-              move_entry(cache, entry);
+              keep = TRUE;
             }
           else if (cache->l2.next / GROUP_SIZE == idx / GROUP_SIZE)
             {
               /* Special case: we cannot drop entries that are in the same
-              * group as TO_FIT_IN because that might the latter to become
-              * invalidated it it happens to be the highest used entry in
-              * the group.  So, we must keep ENTRY unconditionally.
-              * (this is a very rare condition)
-              */
-              move_entry(cache, entry);
+               * group as TO_FIT_IN because that might the latter to become
+               * invalidated it it happens to be the highest used entry in
+               * the group.  So, we must keep ENTRY unconditionally.
+               * (this is a very rare condition)
+               */
+              keep = TRUE;
             }
-          else
+          else if (   entry->priority < SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY
+                   && to_fit_in->priority > entry->priority)
+            {
+              /* Be quick to evict low-priority entries if the one to insert
+               * is of higher priority.
+               */
+              keep = FALSE;
+            }
+          else if (to_fit_in->priority != entry->priority)
+            {
+              /* Not the same priority but the lower prio side is not a
+               * clear loser either (already checked those cases above).
+               * Keep the current entry if it has seen more hits recently
+               * or is smaller than the one to insert - both relative to
+               * their respective priority.
+               */
+              keep = to_fit_in->hit_count * to_fit_in->priority
+                   < entry->hit_count * entry->priority
+                  || to_fit_in->size * to_fit_in->priority
+                   < entry->size * entry->priority;
+            }
+          else if (cache->hit_count > cache->used_entries)
             {
-              svn_boolean_t keep;
+              /* Roll the dice and determine a threshold somewhere
+               * from 0 up to 2 times the average hit count.
+               */
+              average_hit_value = cache->hit_count / cache->used_entries;
+              threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
 
-              if (cache->hit_count > cache->used_entries)
-                {
-                  /* Roll the dice and determine a threshold somewhere from
-                   * 0 up to 2 times the average hit count.
-                   */
-                  average_hit_value = cache->hit_count / cache->used_entries;
-                  threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
+              keep = entry->hit_count > threshold;
+            }
+          else
+            {
+              /* general hit count is low. Keep everything that got hit
+               * at all and assign some 50% survival chance to everything
+               * else.
+               */
+              keep = rand() & 1;
+            }
 
-                  keep = entry->hit_count >= threshold;
-                }
-              else
-                {
-                  /* general hit count is low. Keep everything that got
-                   * hit at all and assign some 50% survival chance to
-                   * everything else.
-                   */
-                  keep = (entry->hit_count > 0) || (rand() & 1);
-                }
+          /* keepers or destroyers? */
+          if (keep)
+            {
+              /* Moving entries around is not for free -> track costs. */
+              moved_size += entry->size;
+              moved_count++;
 
-              /* keepers or destroyers? */
-              if (keep)
-                {
-                 /* Keep ENTRY and move the insertion window.
-                  */
-                  move_entry(cache, entry);
-                }
-              else
-                {
-                 /* Drop the entry from the end of the insertion window,
-                  * because it had been hit less than the threshold.
-                  */
-                  drop_hits += entry->hit_count;
-                  drop_entry(cache, entry);
-                }
+              move_entry(cache, entry);
+            }
+          else
+            {
+              /* Drop the entry from the end of the insertion window, if it
+               * has been hit less than the threshold. Otherwise, keep it and
+               * move the insertion window one entry further.
+               */
+              drop_hits += entry->hit_count;
+              drop_entry(cache, entry);
             }
         }
     }
@@ -1582,6 +1630,7 @@ membuffer_cache_set_internal(svn_membuff
                              apr_uint32_t group_index,
                              char *buffer,
                              apr_size_t size,
+                             apr_uint32_t priority,
                              DEBUG_CACHE_MEMBUFFER_TAG_ARG
                              apr_pool_t *scratch_pool)
 {
@@ -1594,6 +1643,7 @@ membuffer_cache_set_internal(svn_membuff
     {
       cache->data_used += size - entry->size;
       entry->size = size;
+      entry->priority = priority;
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
@@ -1624,6 +1674,7 @@ membuffer_cache_set_internal(svn_membuff
       entry = find_entry(cache, group_index, to_find, TRUE);
       entry->size = size;
       entry->offset = cache->l1.current_data;
+      entry->priority = priority;
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
@@ -1673,6 +1724,7 @@ membuffer_cache_set(svn_membuffer_t *cac
                     entry_key_t key,
                     void *item,
                     svn_cache__serialize_func_t serializer,
+                    apr_uint32_t priority,
                     DEBUG_CACHE_MEMBUFFER_TAG_ARG
                     apr_pool_t *scratch_pool)
 {
@@ -1697,6 +1749,7 @@ membuffer_cache_set(svn_membuffer_t *cac
                                                group_index,
                                                buffer,
                                                size,
+                                               priority,
                                                DEBUG_CACHE_MEMBUFFER_TAG
                                                scratch_pool));
   return SVN_NO_ERROR;
@@ -1808,6 +1861,43 @@ membuffer_cache_get(svn_membuffer_t *cac
 }
 
 /* Look for the cache entry in group GROUP_INDEX of CACHE, identified
+ * by the hash value TO_FIND.  If no item has been stored for KEY, *FOUND
+ * will be FALSE and TRUE otherwise.
+ */
+static svn_error_t *
+membuffer_cache_has_key_internal(svn_membuffer_t *cache,
+                                 apr_uint32_t group_index,
+                                 entry_key_t to_find,
+                                 svn_boolean_t *found)
+{
+  *found = find_entry(cache, group_index, to_find, FALSE) != NULL;
+
+  return SVN_NO_ERROR;
+}
+
+/* Look for an entry identified by KEY.  If no item has been stored
+ * for KEY, *FOUND will be set to FALSE and TRUE otherwise.
+ */
+/* Implements svn_cache__has_key for membuffer caches.
+ */
+static svn_error_t *
+membuffer_cache_has_key(svn_membuffer_t *cache,
+                        entry_key_t key,
+                        svn_boolean_t *found)
+{
+  /* find the entry group that will hold the key.
+   */
+  apr_uint32_t group_index = get_group_index(&cache, key);
+  WITH_READ_LOCK(cache,
+                 membuffer_cache_has_key_internal(cache,
+                                                  group_index,
+                                                  key,
+                                                  found));
+
+  return SVN_NO_ERROR;
+}
+
+/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
  * by the hash value TO_FIND. FOUND indicates whether that entry exists.
  * If not found, *ITEM will be NULL.
  *
@@ -2097,6 +2187,9 @@ typedef struct svn_membuffer_cache_t
    */
   apr_ssize_t key_len;
 
+  /* priority class for all items written through this interface */
+  apr_uint32_t priority;
+  
   /* Temporary buffer containing the hash key for the current access
    */
   entry_key_t combined_key;
@@ -2291,6 +2384,39 @@ svn_membuffer_cache_get(void **value_p,
 
   /* return result */
   *found = *value_p != NULL;
+
+  return SVN_NO_ERROR;
+}
+
+/* Implement svn_cache__vtable_t.has_key (not thread-safe)
+ */
+static svn_error_t *
+svn_membuffer_cache_has_key(svn_boolean_t *found,
+                            void *cache_void,
+                            const void *key,
+                            apr_pool_t *scratch_pool)
+{
+  svn_membuffer_cache_t *cache = cache_void;
+
+  /* special case */
+  if (key == NULL)
+    {
+      *found = FALSE;
+
+      return SVN_NO_ERROR;
+    }
+
+  /* construct the full, i.e. globally unique, key by adding
+   * this cache instances' prefix
+   */
+  combine_key(cache, key, cache->key_len);
+
+  /* Look the item up. */
+  SVN_ERR(membuffer_cache_has_key(cache->membuffer,
+                                  cache->combined_key,
+                                  found));
+
+  /* return result */
   return SVN_NO_ERROR;
 }
 
@@ -2332,6 +2458,7 @@ svn_membuffer_cache_set(void *cache_void
                              cache->combined_key,
                              value,
                              cache->serializer,
+                             cache->priority,
                              DEBUG_CACHE_MEMBUFFER_TAG
                              cache->pool);
 }
@@ -2488,6 +2615,7 @@ svn_membuffer_cache_get_info(void *cache
  */
 static svn_cache__vtable_t membuffer_cache_vtable = {
   svn_membuffer_cache_get,
+  svn_membuffer_cache_has_key,
   svn_membuffer_cache_set,
   svn_membuffer_cache_iter,
   svn_membuffer_cache_is_cachable,
@@ -2516,6 +2644,24 @@ svn_membuffer_cache_get_synced(void **va
   return SVN_NO_ERROR;
 }
 
+/* Implement svn_cache__vtable_t.has_key and serialize all cache access.
+ */
+static svn_error_t *
+svn_membuffer_cache_has_key_synced(svn_boolean_t *found,
+                                   void *cache_void,
+                                   const void *key,
+                                   apr_pool_t *result_pool)
+{
+  svn_membuffer_cache_t *cache = cache_void;
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       svn_membuffer_cache_has_key(found,
+                                                   cache_void,
+                                                   key,
+                                                   result_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Implement svn_cache__vtable_t.set and serialize all cache access.
  */
 static svn_error_t *
@@ -2582,6 +2728,7 @@ svn_membuffer_cache_set_partial_synced(v
  */
 static svn_cache__vtable_t membuffer_cache_synced_vtable = {
   svn_membuffer_cache_get_synced,
+  svn_membuffer_cache_has_key_synced,
   svn_membuffer_cache_set_synced,
   svn_membuffer_cache_iter,               /* no sync required */
   svn_membuffer_cache_is_cachable,        /* no sync required */
@@ -2636,6 +2783,7 @@ svn_cache__create_membuffer_cache(svn_ca
                                   svn_cache__deserialize_func_t deserializer,
                                   apr_ssize_t klen,
                                   const char *prefix,
+                                  apr_uint32_t priority,
                                   svn_boolean_t thread_safe,
                                   apr_pool_t *pool)
 {
@@ -2656,6 +2804,7 @@ svn_cache__create_membuffer_cache(svn_ca
                       ? deserializer
                       : deserialize_svn_stringbuf;
   cache->full_prefix = apr_pstrdup(pool, prefix);
+  cache->priority = priority;
   cache->key_len = klen;
   cache->pool = svn_pool_create(pool);
   cache->alloc_counter = 0;

Modified: subversion/trunk/subversion/libsvn_subr/cache-memcache.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cache-memcache.c?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/cache-memcache.c (original)
+++ subversion/trunk/subversion/libsvn_subr/cache-memcache.c Fri Jul 19 21:21:36 2013
@@ -213,6 +213,26 @@ memcache_get(void **value_p,
   return SVN_NO_ERROR;
 }
 
+/* Implement vtable.has_key in terms of the getter.
+ */
+static svn_error_t *
+memcache_has_key(svn_boolean_t *found,
+                 void *cache_void,
+                 const void *key,
+                 apr_pool_t *scratch_pool)
+{
+  char *data;
+  apr_size_t data_len;
+  SVN_ERR(memcache_internal_get(&data,
+                                &data_len,
+                                found,
+                                cache_void,
+                                key,
+                                scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Core functionality of our setter functions: store LENGH bytes of DATA
  * to be identified by KEY in the memcached given by CACHE_VOID. Use POOL
  * for temporary allocations.
@@ -371,6 +391,7 @@ memcache_get_info(void *cache_void,
 
 static svn_cache__vtable_t memcache_vtable = {
   memcache_get,
+  memcache_has_key,
   memcache_set,
   memcache_iter,
   memcache_is_cachable,

Modified: subversion/trunk/subversion/libsvn_subr/cache.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cache.c?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/cache.c (original)
+++ subversion/trunk/subversion/libsvn_subr/cache.c Fri Jul 19 21:21:36 2013
@@ -96,6 +96,21 @@ svn_cache__get(void **value_p,
 }
 
 svn_error_t *
+svn_cache__has_key(svn_boolean_t *found,
+                   svn_cache__t *cache,
+                   const void *key,
+                   apr_pool_t *scratch_pool)
+{
+  *found = FALSE;
+  return handle_error(cache,
+                      (cache->vtable->has_key)(found,
+                                               cache->cache_internal,
+                                               key,
+                                               scratch_pool),
+                      scratch_pool);
+}
+
+svn_error_t *
 svn_cache__set(svn_cache__t *cache,
                const void *key,
                void *value,

Modified: subversion/trunk/subversion/libsvn_subr/cache.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cache.h?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/cache.h (original)
+++ subversion/trunk/subversion/libsvn_subr/cache.h Fri Jul 19 21:21:36 2013
@@ -38,6 +38,12 @@ typedef struct svn_cache__vtable_t {
                       const void *key,
                       apr_pool_t *result_pool);
 
+  /* See svn_cache__has_key(). */
+  svn_error_t *(*has_key)(svn_boolean_t *found,
+                          void *cache_implementation,
+                          const void *key,
+                          apr_pool_t *scratch_pool);
+
   /* See svn_cache__set(). */
   svn_error_t *(*set)(void *cache_implementation,
                       const void *key,

Modified: subversion/trunk/subversion/tests/libsvn_subr/cache-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/cache-test.c?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/cache-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/cache-test.c Fri Jul 19 21:21:36 2013
@@ -193,6 +193,7 @@ test_membuffer_cache_basic(apr_pool_t *p
                                             deserialize_revnum,
                                             APR_HASH_KEY_STRING,
                                             "cache:",
+                                            SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
                                             FALSE,
                                             pool));
 

Modified: subversion/trunk/tools/server-side/fsfs-stats.c
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/server-side/fsfs-stats.c?rev=1505026&r1=1505025&r2=1505026&view=diff
==============================================================================
--- subversion/trunk/tools/server-side/fsfs-stats.c (original)
+++ subversion/trunk/tools/server-side/fsfs-stats.c Fri Jul 19 21:21:36 2013
@@ -1573,7 +1573,9 @@ read_revisions(fs_fs_t **fs,
                                             svn_cache__get_global_membuffer_cache(),
                                             NULL, NULL,
                                             sizeof(window_cache_key_t),
-                                            "", FALSE, pool));
+                                            "",
+                                            SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
+                                            FALSE, pool));
 
   /* read all packed revs */
   for ( revision = start_revision