Author: brane
Date: Fri Feb  6 13:28:51 2015
New Revision: 1657802

URL: http://svn.apache.org/r1657802
Log:
On the reuse-ra-session branch: Limit the size of the inactive list
in the RA session cache, and the time an inactive session is retained.

* BRANCH-README: Update status.

* subversion/libsvn_client/ra_cache.c: Include apr_time.h
  (MAX_INACTIVE_SESSIONS, INACTIVE_SESSION_TIMEOUT): New constants.
  (cache_entry_t): New member 'released', used to detect session expiry.
  (svn_client__ra_cache_t): New member 'inactive_count', used to
   limit the size of the inactive list. Also added two now stats
   counters, 'expunge' and 'expire'.
  (cleanup_ra_cache): Update docstring and extend stats output.
  (close_ra_session): Manage inactive list size. Add docstring.
  (remove_inactive_entry, expunge_cache_entries): New.
  (find_session_by_url): Do not return, and remove, expired sessions.
  (svn_client__ra_cache_open_session): Manage inactive list size.
  (svn_client__ra_cache_release_session): Manage inactive list size
   and remove expired sessions from the cache.

* tools/dev/ra-cache-summary.py
  (stat_rx): Update the regular expression to match new stats.

Modified:
    subversion/branches/reuse-ra-session/BRANCH-README
    subversion/branches/reuse-ra-session/subversion/libsvn_client/ra_cache.c
    subversion/branches/reuse-ra-session/tools/dev/ra-cache-summary.py

Modified: subversion/branches/reuse-ra-session/BRANCH-README
URL: 
http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/BRANCH-README?rev=1657802&r1=1657801&r2=1657802&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/BRANCH-README (original)
+++ subversion/branches/reuse-ra-session/BRANCH-README Fri Feb  6 13:28:51 2015
@@ -14,13 +14,13 @@ DONE:
 - Separate active and inactive session lists.
 - Introduce explicit session reuse and closing.
 - Add explicit session reuse throughout libsvn_client.
+- Expire and close idle sessions after a given timeout.
+- Limit the number of idle open sessions in the cache.
 
 TODO:
 - Add explicit session reuse in the MTCC implementation.
 - Add new RA method (svn_ra__ping?) to verify that a session
   about to be reused is valid.
-- Expire and close idle sessions after a given timeout.
-- Limit the number of idle open sessions in the cache.
 - Run performance comparisons between trunk and branch to prove that
   the RA session cache does in fact speed things up.
 - Find and resolve all 'RA_CACHE TODO' comments.

Modified: 
subversion/branches/reuse-ra-session/subversion/libsvn_client/ra_cache.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_client/ra_cache.c?rev=1657802&r1=1657801&r2=1657802&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_client/ra_cache.c 
(original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_client/ra_cache.c 
Fri Feb  6 13:28:51 2015
@@ -23,6 +23,7 @@
 
 #include <assert.h>
 #include <apr_pools.h>
+#include <apr_time.h>
 
 #include "svn_dirent_uri.h"
 #include "private/svn_ra_private.h"
@@ -68,10 +69,19 @@
  * The session cache.
  */
 
+/* The maximum number of inactive sessions allowed in the cache. */
+/* RA_CACHE TODO: pick a better value */
+#define MAX_INACTIVE_SESSIONS    13
+
+/* Inactive session expiry time. */
+/* RA_CACHE TODO: pick a better value */
+#define INACTIVE_SESSION_TIMEOUT apr_time_from_sec(5*60)
+
+
 /* Cache entry */
 typedef struct cache_entry_t
 {
-  /* The free-list link for this session. */
+  /* The inactive-list link for this session. */
   APR_RING_ENTRY(cache_entry_t) freelist;
 
   /* The cache that owns this session. Will be set to NULL in the
@@ -100,6 +110,9 @@ typedef struct cache_entry_t
   /* Accumulated progress since last session open. */
   apr_off_t progress;
 
+  /* The time when this cache entry was released to the inactive list. */
+  apr_time_t released;
+
   /* ID of RA session. Used only for diagnostics. */
   int id;
 } cache_entry_t;
@@ -121,6 +134,9 @@ struct svn_client__ra_cache_t
   /* List of inactive sessions available for reuse. */
   APR_RING_HEAD(, cache_entry_t) freelist;
 
+  /* The number of entries in the inactive session list. */
+  int inactive_count;
+
   /* Next ID for RA sessions. Used only for diagnostics purpose. */
   int next_id;
 
@@ -130,6 +146,9 @@ struct svn_client__ra_cache_t
     apr_uint64_t close;         /* number of calls to svn_ra__close */
     apr_uint64_t release;       /* number of releases to cache */
     apr_uint64_t reuse;         /* number of reuses from cache */
+    apr_uint64_t expunge;       /* number of inactive sessions closed
+                                   to limit cache size */
+    apr_uint64_t expire;        /* number of expired inactive sessions */
   } stat;)
 };
 
@@ -294,6 +313,10 @@ open_tunnel_func(svn_stream_t **request,
  * Cache management
  */
 
+/* Pool cleanup handler for the RA session cache.
+   Iterates through the active and inactive lists in the cache and
+   sets the cache_entry_t::ra_cache back-pointers to NULL, so that
+   close_ra_session does not attempt to access freed memory. */
 static apr_status_t
 cleanup_ra_cache(void *data)
 {
@@ -320,12 +343,16 @@ cleanup_ra_cache(void *data)
                           " open:%"APR_UINT64_T_FMT
                           " close:%"APR_UINT64_T_FMT
                           " release:%"APR_UINT64_T_FMT
-                          " reuse:%"APR_UINT64_T_FMT"\n",
+                          " reuse:%"APR_UINT64_T_FMT
+                          " expunge:%"APR_UINT64_T_FMT
+                          " expire:%"APR_UINT64_T_FMT"\n",
                           ra_cache->stat.request,
                           ra_cache->stat.open,
                           ra_cache->stat.close,
                           ra_cache->stat.release,
-                          ra_cache->stat.reuse)));
+                          ra_cache->stat.reuse
+                          ra_cache->stat.expunge
+                          ra_cache->stat.expire)));
 
   return APR_SUCCESS;
 }
@@ -355,6 +382,9 @@ svn_client__ra_cache_init(svn_client__pr
  * Session management
  */
 
+/* Pool cleanup handler for the session.
+   This handler is called when the pool that was intended to be the
+   session's owner is cleared or destroyed. */
 static apr_status_t
 close_ra_session(void *data)
 {
@@ -371,8 +401,13 @@ close_ra_session(void *data)
       RA_CACHE_DBG(("close_ra_session: removed from active:         %"
                     DBG_PTR_FMT"\n", (apr_uint64_t)session));
 
-      APR_RING_REMOVE(cache_entry, freelist);
-      APR_RING_ELEM_INIT(cache_entry, freelist);
+      if (cache_entry != APR_RING_NEXT(cache_entry, freelist)
+          && cache_entry != APR_RING_PREV(cache_entry, freelist))
+        {
+          APR_RING_REMOVE(cache_entry, freelist);
+          APR_RING_ELEM_INIT(cache_entry, freelist);
+          --ra_cache->inactive_count;
+        }
 
       /* Close and invalidate the session. */
       cache_entry->session = NULL;
@@ -393,23 +428,91 @@ close_ra_session(void *data)
   return APR_SUCCESS;
 }
 
+/* Removes CACHE_ENTRY from the inactive list of RA_CACHE.
+   Callers must make sure that the CACHE_ENTRY is indeed in the
+   inactive list. if EXPIRED is true, the session expired; otherwise,
+   it is being removed in order to limit the size of the inactive
+   list. */
+static void
+remove_inactive_entry(svn_client__ra_cache_t *ra_cache,
+                      cache_entry_t *cache_entry,
+                      svn_boolean_t expired)
+{
+  svn_ra_session_t *const session = cache_entry->session;
+
+  APR_RING_REMOVE(cache_entry, freelist);
+  APR_RING_ELEM_INIT(cache_entry, freelist);
+  --ra_cache->inactive_count;
+
+  /* Close and invalidate the session. */
+  cache_entry->session = NULL;
+  svn_ra__close(session);
+
+  RA_CACHE_LOG(("SESSION(%d): Closed (%s)\n", cache_entry->id,
+                (expired ? "expired" : "expunged")));
+  RA_CACHE_STATS(
+      if (expired)
+        ++ra_cache->stat.expired;
+      else
+        ++ra_cache->stat.expunged);
+}
+
+/* Limit the size of the inactive session list in RA_CACHE and remove
+   all remaining sessions that have expired as of NOW. */
+static void
+expunge_cache_entries(svn_client__ra_cache_t *ra_cache, apr_time_t now)
+{
+  cache_entry_t *cache_entry;
+
+  /* Limit the size of the inactive list. */
+  while (ra_cache->inactive_count > MAX_INACTIVE_SESSIONS)
+    {
+      cache_entry = APR_RING_LAST(&ra_cache->freelist);
+      remove_inactive_entry(ra_cache, cache_entry, FALSE);
+    }
+
+  /* Remove expired inactive cache entries. */
+  cache_entry = APR_RING_LAST(&ra_cache->freelist);
+  while (ra_cache->inactive_count > 0
+         && now > cache_entry->released + INACTIVE_SESSION_TIMEOUT)
+    {
+      remove_inactive_entry(ra_cache, cache_entry, TRUE);
+      cache_entry = APR_RING_LAST(&ra_cache->freelist);
+    }
+}
+
+/* Find an inactive session in RA_CACHE that can be reused to connect
+   to URL. Set *CACHE_ENTRY_P to the point to the session's cache
+   entry, or to NULL if a suitable session was not found. */
 static svn_error_t *
 find_session_by_url(cache_entry_t **cache_entry_p,
                     svn_client__ra_cache_t *ra_cache,
                     const char *url,
                     apr_pool_t *scratch_pool)
 {
+  const apr_time_t now = apr_time_now();
+  cache_entry_t *found_entry = NULL;
   cache_entry_t *cache_entry;
 
-  /* Try to find RA session with exact session URL match first because
-   * the svn_ra_reparent() for svn:// protocol requires network round-trip.
-   */
   APR_RING_FOREACH(cache_entry, &ra_cache->freelist,
                    cache_entry_t, freelist)
     {
       const char *session_url;
       SVN_ERR_ASSERT(cache_entry->owner_pool == NULL);
 
+      /* Do not use the session if it has expired. Since the inactive
+         list is sorted by descending release time, once we find an
+         expired session, we know that all the following sessions in
+         the inactive list have expired, too. */
+      if (now > cache_entry->released + INACTIVE_SESSION_TIMEOUT)
+        {
+          expunge_cache_entries(ra_cache, now);
+          break;
+        }
+
+      /* Try to find RA session with exact session URL match first
+        because the svn_ra_reparent() for svn:// protocol requires
+        network round-trip. */
       SVN_ERR(svn_ra_get_session_url(cache_entry->session, &session_url,
                                      scratch_pool));
       if (strcmp(session_url, url) == 0)
@@ -417,21 +520,13 @@ find_session_by_url(cache_entry_t **cach
           *cache_entry_p = cache_entry;
           return SVN_NO_ERROR;
         }
-    }
-
-  APR_RING_FOREACH(cache_entry, &ra_cache->freelist,
-                   cache_entry_t, freelist)
-    {
-      SVN_ERR_ASSERT(cache_entry->owner_pool == NULL);
 
-      if (svn_uri__is_ancestor(cache_entry->root_url, url))
-        {
-          *cache_entry_p = cache_entry;
-          return SVN_NO_ERROR;
-        }
+      /* If such a session can't be found, use the first matching session. */
+      if (!found_entry && svn_uri__is_ancestor(cache_entry->root_url, url))
+        found_entry = cache_entry;
     }
 
-  *cache_entry_p = NULL;
+  *cache_entry_p = found_entry;
   return SVN_NO_ERROR;
 }
 
@@ -501,6 +596,7 @@ svn_client__ra_cache_open_session(svn_ra
       /* Remove the session from the freelist. */
       APR_RING_REMOVE(cache_entry, freelist);
       APR_RING_ELEM_INIT(cache_entry, freelist);
+      --ra_cache->inactive_count;
 
       RA_CACHE_LOG(("SESSION(%d): Reused\n", cache_entry->id));
       RA_CACHE_STATS(++ra_cache->stat.reuse);
@@ -615,11 +711,15 @@ svn_client__ra_cache_release_session(svn
 
   APR_RING_INSERT_HEAD(&ra_cache->freelist, cache_entry,
                        cache_entry_t, freelist);
+  ++ra_cache->inactive_count;
 
   cache_entry->owner_pool = NULL;
   cache_entry->cb_table = NULL;
   cache_entry->cb_baton = NULL;
+  cache_entry->released = apr_time_now();
 
   RA_CACHE_LOG(("SESSION(%d): Released\n", cache_entry->id));
   RA_CACHE_STATS(++ra_cache->stat.release);
+
+  expunge_cache_entries(ra_cache, cache_entry->released);
 }

Modified: subversion/branches/reuse-ra-session/tools/dev/ra-cache-summary.py
URL: 
http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/tools/dev/ra-cache-summary.py?rev=1657802&r1=1657801&r2=1657802&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/tools/dev/ra-cache-summary.py 
(original)
+++ subversion/branches/reuse-ra-session/tools/dev/ra-cache-summary.py Fri Feb  
6 13:28:51 2015
@@ -32,13 +32,18 @@
 import re
 import sys
 
-stat_rx = re.compile(r'^DBG:\s.+\sRA_CACHE_STATS:\s+(?:'
+stat_rx = re.compile(r'^DBG:\s.+\sRA_CACHE_STATS:\s+'
+                     r'(?:'
                      r'request:(?P<request>\d+)\s+'
                      r'open:(?P<open>\d+)\s+'
                      r'close:(?P<close>\d+)\s+'
                      r'release:(?P<release>\d+)\s+'
-                     r'reuse:(?P<reuse>\d+)|'
-                     r'cleanup:(?P<cleanup>\d+))\s*$')
+                     r'reuse:(?P<reuse>\d+)\s+'
+                     r'expunge:(?P<reuse>\d+)\s+'
+                     r'expire:(?P<reuse>\d+)'
+                     r'|'
+                     r'cleanup:(?P<cleanup>\d+)'
+                     r')\s*$')
 
 request = open = close = release = reuse = cleanup = 0
 


Reply via email to