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 2011/10/29 02:33:21 UTC

svn commit: r1190714 - in /subversion/branches/file-handle-cache: ./ subversion/include/private/ subversion/libsvn_fs_fs/ subversion/libsvn_subr/ subversion/mod_dav_svn/ subversion/svnadmin/ subversion/svnserve/

Author: stefan2
Date: Sat Oct 29 00:33:21 2011
New Revision: 1190714

URL: http://svn.apache.org/viewvc?rev=1190714&view=rev
Log:
Merging relevant changes from the performance branch, part 4:
r982057,982360,982375,982391,982417,983385,983437,985602,985670,987875,
992905,1003430,1029082,1029381,1029408,1030759,1037466,1037470,1037548,
1037552,1037586,1037588,1067129-1067130
and resolved numerous intermediate conflicts


Modified:
    subversion/branches/file-handle-cache/   (props changed)
    subversion/branches/file-handle-cache/build.conf
    subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h   (contents, props changed)
    subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c
    subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs.h
    subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c
    subversion/branches/file-handle-cache/subversion/libsvn_subr/stream.c
    subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c   (contents, props changed)
    subversion/branches/file-handle-cache/subversion/mod_dav_svn/mod_dav_svn.c
    subversion/branches/file-handle-cache/subversion/svnadmin/main.c
    subversion/branches/file-handle-cache/subversion/svnserve/main.c
    subversion/branches/file-handle-cache/subversion/svnserve/server.h

Propchange: subversion/branches/file-handle-cache/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Sat Oct 29 00:33:21 2011
@@ -38,7 +38,7 @@
 /subversion/branches/log-g-performance:870941-871032
 /subversion/branches/merge-skips-obstructions:874525-874615
 /subversion/branches/nfc-nfd-aware-client:870276,870376
-/subversion/branches/performance:979193,980118,981087,981090-981091,981189,981194,981204,981287,981665,981684,981827-981828,982043,982355,983398,983406,983430,983474,983488,983490,983760,983764,983766,983770,984927,984973,984984,985014,985037,985046,985472,985477,985482,985487-985488,985493,985497,985500,985514,985601,985603,985606,985669,985673,985695,985697,986453,986465,986485,986491-986492,986517,986521,986605,986608,986817,986832,987865,987868-987869,987872,987886-987888,987893,988319,988898,990330,990533,990535-990537,990541,990568,990572,990574-990575,990600,990759,992899,992904,992911,993127,993141,994956,995478,995507,995603,998012,998858,999098,1001413,1001417,1004291,1022668,1022670,1022676,1022715,1022719,1025660,1025672,1027193,1027203,1027206,1027214,1027227,1028077,1028092,1028094,1028104,1028107,1028111,1028354,1029038,1029042-1029043,1029054-1029055,1029062-1029063,1029078,1029080,1029090,1029092-1029093,1029111,1029151,1029158,1029229-1029230,1029232,102933
 5-1029336,1029339-1029340,1029342,1029344,1030763,1030827,1031203,1031235,1032285,1032333,1033040,1033057,1033294,1035869,1035882,1039511,1043705,1053735,1056015,1066452,1067683,1067697-1078365
+/subversion/branches/performance:979193,980118,981087,981090-981091,981189,981194,981204,981287,981665,981684,981827-981828,982043,982057,982355,982360,982375,982391,982417,983385,983398,983406,983430,983437,983474,983488,983490,983760,983764,983766,983770,984927,984973,984984,985014,985037,985046,985472,985477,985482,985487-985488,985493,985497,985500,985514,985601-985603,985606,985669-985670,985673,985695,985697,986453,986465,986485,986491-986492,986517,986521,986605,986608,986817,986832,987865,987868-987869,987872,987875,987886-987888,987893,988319,988898,990330,990533,990535-990537,990541,990568,990572,990574-990575,990600,990759,992899,992904-992905,992911,993127,993141,994956,995478,995507,995603,998012,998858,999098,1001413,1001417,1003430,1004291,1022668,1022670,1022676,1022715,1022719,1025660,1025672,1027193,1027203,1027206,1027214,1027227,1028077,1028092,1028094,1028104,1028107,1028111,1028354,1029038,1029042-1029043,1029054-1029055,1029062-1029063,1029078,1029080,
 1029082,1029090,1029092-1029093,1029111,1029151,1029158,1029229-1029230,1029232,1029335-1029336,1029339-1029340,1029342,1029344,1029381,1029408,1030759,1030763,1030827,1031203,1031235,1032285,1032333,1033040,1033057,1033294,1035869,1035882,1037466,1037470,1037548,1037552,1037586,1037588,1039511,1043705,1053735,1056015,1066452,1067129-1067130,1067683,1067697-1078365
 /subversion/branches/py-tests-as-modules:956579-1033052
 /subversion/branches/ra_serf-digest-authn:875693-876404
 /subversion/branches/reintegrate-improvements:873853-874164

Modified: subversion/branches/file-handle-cache/build.conf
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/build.conf?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/build.conf (original)
+++ subversion/branches/file-handle-cache/build.conf Sat Oct 29 00:33:21 2011
@@ -332,6 +332,7 @@ msvc-export = 
         private\svn_temp_serializer.h private\svn_io_private.h
         private\svn_string_private.h private\svn_magic.h
         private\svn_subr_private.h private\svn_mutex.h
+        private\svn_temp_serializer.h private\svn_file_handle_cache.h
 
 # Working copy management lib
 [libsvn_wc]

Modified: subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h (original)
+++ subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h Sat Oct 29 00:33:21 2011
@@ -24,8 +24,7 @@
  * @brief File handle cache API
  */
 
-#include <apr_file_io.h>
-#include "svn_types.h"
+#include "svn_io.h"
 
 /**
  * An opaque structure representing a cache for open file handles.
@@ -86,6 +85,21 @@ svn_file_handle_cache__get_apr_handle(sv
 const char *
 svn_file_handle_cache__get_name(svn_file_handle_cache__handle_t *f);
 
+/** Create a stream from a cached file handle.  For convenience, if @a file
+ * is @c NULL, an empty stream created by svn_stream_empty() is returned.
+ *
+ * This function should normally be called with @a disown set to FALSE,
+ * in which case closing the stream will also return the file handle to
+ * the respective cache object.
+ *
+ * If @a disown is TRUE, the stream will disown the file handle, meaning 
+ * that svn_stream_close() will not close the cached file handle.
+ */
+svn_stream_t *
+svn_stream__from_cached_file_handle(svn_file_handle_cache__handle_t *file,
+                                    svn_boolean_t disown,
+                                    apr_pool_t *pool);
+
 /**
  * Return the cached file handle @a f to the cache. Depending on the number
  * of open handles, the underlying handle may actually get closed. If @a f
@@ -113,3 +127,4 @@ svn_file_handle_cache__create_cache(svn_
                                     size_t max_handles,
                                     svn_boolean_t thread_safe,
                                     apr_pool_t *pool);
+

Propchange: subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Sat Oct 29 00:33:21 2011
@@ -1 +1 @@
-/subversion/branches/performance/subversion/include/private/svn_file_handle_cache.h:981665,981828
+/subversion/branches/performance/subversion/include/private/svn_file_handle_cache.h:981665,981828,983385,983437

Modified: subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c (original)
+++ subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c Sat Oct 29 00:33:21 2011
@@ -92,6 +92,34 @@ warn_on_cache_errors(svn_error_t *err,
   return SVN_NO_ERROR;
 }
 
+static apr_status_t
+close_unused_file_handles(void *cache_void)
+{
+  svn_file_handle_cache_t *cache = cache_void;
+  apr_status_t result = APR_SUCCESS;
+
+  /* Close all file handles that are not in use anymore. So, as long as
+   * no requests to a given repository are being processed, we may change
+   * the file content and / or structure. However, content that has been
+   * cached *above* the APR layer (e.g. fulltext caches) will *not* be
+   * changed and may become inconsistent with the disk content.
+   *
+   * This will cause concurrent threads to perform somewhat slower because
+   * they might have put those handles to good use a few moments later.
+   * They now have to actually re-open the respective files.
+   */
+  svn_error_t *err = svn_file_handle_cache__flush(cache);
+
+  /* process error returns */
+  if (err)
+  {
+    result = err->apr_err;
+    svn_error_clear(err);
+  }
+
+  return result;
+}
+
 #ifdef SVN_DEBUG_CACHE_DUMP_STATS
 /* Baton to be used for the dump_cache_statistics() pool cleanup function, */
 struct dump_cache_baton_t
@@ -189,6 +217,24 @@ init_callbacks(svn_cache__t *cache,
   return SVN_NO_ERROR;
 }
 
+/* Access the process-global (singleton) open file handle cache. The first
+ * call will automatically allocate the cache using the current cache config.
+ * Even for file handle limit of 0, a cache object will be returned.
+ */
+static svn_file_handle_cache_t *
+get_global_file_handle_cache(void)
+{
+  static svn_file_handle_cache_t *cache = NULL;
+
+  if (!cache)
+    svn_file_handle_cache__create_cache(&cache,
+                                        cache_settings.file_handle_count,
+                                        !cache_settings.single_threaded,
+                                        svn_pool_create(NULL));
+
+  return cache;
+}
+
 /* Sets *CACHE_P to cache instance based on provided options.
  * Creates memcache if MEMCACHE is not NULL. Creates membuffer cache if
  * MEMBUFFER is not NULL. Fallbacks to inprocess cache if MEMCACHE and
@@ -260,11 +306,17 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
 
   membuffer = svn_cache__get_global_membuffer_cache();
 
-  /* Make the cache for revision roots.  For the vast majority of
-   * commands, this is only going to contain a few entries (svnadmin
-   * dump/verify is an exception here), so to reduce overhead let's
-   * try to keep it to just one page.  I estimate each entry has about
-   * 72 bytes of overhead (svn_revnum_t key, svn_fs_id_t +
+  /* Schedule file handle cache cleanup after finishing the request. */
+  apr_pool_cleanup_register(pool,
+                            svn_fs__get_global_file_handle_cache(),
+                            close_unused_file_handles,
+                            apr_pool_cleanup_null);
+
+  /* Make the caches for non-packed and packed revision roots.  For the
+   * vastmajority of commands, this is only going to contain a few entries
+   * (svnadmin dump/verify is an exception here), so to reduce overhead
+   * let's try to keep it to just one page.  I estimate each entry has
+   * about 72 bytes of overhead (svn_revnum_t key, svn_fs_id_t +
    * id_private_t + 3 strings for value, and the cache_entry); the
    * default pool size is 8192, so about a hundred should fit
    * comfortably. */
@@ -373,6 +425,9 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
 
   SVN_ERR(init_callbacks(ffd->node_revision_cache, fs, no_handler, pool));
 
+  /* initialize file handle cache as configured */
+  ffd->file_handle_cache = get_global_file_handle_cache();
+
   return SVN_NO_ERROR;
 }
 

Modified: subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs.h?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs.h Sat Oct 29 00:33:21 2011
@@ -34,6 +34,7 @@
 #include "private/svn_fs_private.h"
 #include "private/svn_sqlite.h"
 #include "private/svn_mutex.h"
+#include "private/svn_file_handle_cache.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -221,9 +222,15 @@ typedef struct fs_fs_data_t
      multiple svn_fs_t's for the same filesystem.) */
 
   /* A cache of revision root IDs, mapping from (svn_revnum_t *) to
-     (svn_fs_id_t *).  (Not threadsafe.) */
+     (svn_fs_id_t *). Some of the IDs may belong to non-packed revs
+     but these will never be read. This is guaranteed by the fact
+     that the transition from non-packed to packed is irreversable. */
   svn_cache__t *rev_root_id_cache;
 
+  /* Similar to @ref rev_root_id_cache but all IDs are guaranteed
+     to belong to packed revisions. */
+  svn_cache__t *packed_rev_root_id_cache;
+
   /* DAG node cache for immutable nodes */
   svn_cache__t *rev_node_cache;
 
@@ -254,6 +261,9 @@ typedef struct fs_fs_data_t
      unparsed FS ID to ###x.  NULL outside transactions. */
   svn_cache__t *txn_dir_cache;
 
+  /* Reference to the process-global open file handle cache */
+  svn_file_handle_cache_t *file_handle_cache;
+
   /* Data shared between all svn_fs_t objects for a given filesystem. */
   fs_fs_shared_data_t *shared;
 

Modified: subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c Sat Oct 29 00:33:21 2011
@@ -114,6 +114,17 @@
 #define REP_PLAIN          "PLAIN"
 #define REP_DELTA          "DELTA"
 
+/* Cookies used to classify cached file handle usage */
+/* Used whenever no other specific region of the rev file is being read. */
+#define DEFAULT_FILE_COOKIE 0
+
+/* Used when reading representation data.
+ * Since this is often interleaved with other reads, use a separate 
+ * cookie (hence a separate file handle) for the reps.  That way, rep
+ * access can often be satisfied from the APR read buffer.  The same
+ * applies to the meta data because it is not rep data. */
+#define REP_FILE_COOKIE     1
+
 /* Notes:
 
 To avoid opening and closing the rev-files all the time, it would
@@ -611,6 +622,17 @@ with_txn_current_lock(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
+/* A frequently used utility method: close all cached, idle file handles.
+ * Call this at the end of write transactions to ensure that successive
+ * reads will see the new file content.
+ */
+static svn_error_t *
+sync_file_handle_cache(svn_fs_t *fs)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  return svn_file_handle_cache__flush(ffd->file_handle_cache);
+}
+
 /* A structure used by unlock_proto_rev() and unlock_proto_rev_body(),
    which see. */
 struct unlock_proto_rev_baton
@@ -1769,7 +1791,9 @@ ensure_revision_exists(svn_fs_t *fs,
 /* Open the correct revision file for REV.  If the filesystem FS has
    been packed, *FILE will be set to the packed file; otherwise, set *FILE
    to the revision file for REV.  Return SVN_ERR_FS_NO_SUCH_REVISION if the
-   file doesn't exist.
+   file doesn't exist.  Move the file pointer of OFFSET, if the latter is
+   not -1.  Prefer cached file handles that share the same COOKIE (again,
+   if not -1).
 
    TODO: Consider returning an indication of whether this is a packed rev
          file, so the caller need not rely on is_packed_rev() which in turn
@@ -1778,15 +1802,18 @@ ensure_revision_exists(svn_fs_t *fs,
 
    Use POOL for allocations. */
 static svn_error_t *
-open_pack_or_rev_file(apr_file_t **file,
+open_pack_or_rev_file(svn_file_handle_cache__handle_t **file,
                       svn_fs_t *fs,
                       svn_revnum_t rev,
+                      apr_off_t offset,
+                      int cookie,
                       apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   svn_error_t *err;
   const char *path;
   svn_boolean_t retry = FALSE;
+  fs_fs_data_t *ffd = fs->fsap_data;
 
   do
     {
@@ -1794,8 +1821,14 @@ open_pack_or_rev_file(apr_file_t **file,
 
       /* open the revision file in buffered r/o mode */
       if (! err)
-        err = svn_io_file_open(file, path,
-                              APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
+        err = svn_file_handle_cache__open(file,
+                                          ffd->file_handle_cache,
+                                          path,
+                                          APR_READ | APR_BUFFERED,
+                                          APR_OS_DEFAULT,
+                                          offset,
+                                          cookie,
+                                          pool);
 
       if (err && APR_STATUS_IS_ENOENT(err->apr_err)
           && ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
@@ -1895,20 +1928,18 @@ get_packed_offset(apr_off_t *rev_offset,
 
 /* Open the revision file for revision REV in filesystem FS and store
    the newly opened file in FILE.  Seek to location OFFSET before
-   returning.  Perform temporary allocations in POOL. */
+   returning.  Prefer cached file handles with the specified COOKIE
+   (if not -1).  Perform temporary allocations in POOL. */
 static svn_error_t *
-open_and_seek_revision(apr_file_t **file,
+open_and_seek_revision(svn_file_handle_cache__handle_t **file,
                        svn_fs_t *fs,
                        svn_revnum_t rev,
                        apr_off_t offset,
+                       int cookie,
                        apr_pool_t *pool)
 {
-  apr_file_t *rev_file;
-
+  /* none of the following requires the file handle */
   SVN_ERR(ensure_revision_exists(fs, rev, pool));
-
-  SVN_ERR(open_pack_or_rev_file(&rev_file, fs, rev, pool));
-
   if (is_packed_rev(fs, rev))
     {
       apr_off_t rev_offset;
@@ -1917,53 +1948,55 @@ open_and_seek_revision(apr_file_t **file
       offset += rev_offset;
     }
 
-  SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
-
-  *file = rev_file;
-
-  return SVN_NO_ERROR;
+  /* So, open the revision file and position the pointer here in one go. */
+  return open_pack_or_rev_file(file, fs, rev, offset, cookie, pool);
 }
 
 /* Open the representation for a node-revision in transaction TXN_ID
    in filesystem FS and store the newly opened file in FILE.  Seek to
-   location OFFSET before returning.  Perform temporary allocations in
+   location OFFSET before returning.  Prefer cached file handles witt
+   the specified COOKIE (if not -1).  Perform temporary allocations in
    POOL.  Only appropriate for file contents, nor props or directory
    contents. */
 static svn_error_t *
-open_and_seek_transaction(apr_file_t **file,
+open_and_seek_transaction(svn_file_handle_cache__handle_t **file,
                           svn_fs_t *fs,
                           const char *txn_id,
                           representation_t *rep,
+                          int cookie,
                           apr_pool_t *pool)
 {
-  apr_file_t *rev_file;
-  apr_off_t offset;
-
-  SVN_ERR(svn_io_file_open(&rev_file, path_txn_proto_rev(fs, txn_id, pool),
-                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
-
-  offset = rep->offset;
-  SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
-
-  *file = rev_file;
+  fs_fs_data_t *ffd = fs->fsap_data;
 
-  return SVN_NO_ERROR;
+  /* open & seek in one call */
+  return svn_file_handle_cache__open(file,
+                                     ffd->file_handle_cache,
+                                     path_txn_proto_rev(fs, txn_id, pool),
+                                     APR_READ | APR_BUFFERED, 
+                                     APR_OS_DEFAULT, 
+                                     rep->offset,
+                                     cookie,
+                                     pool);
 }
 
 /* Given a node-id ID, and a representation REP in filesystem FS, open
    the correct file and seek to the correction location.  Store this
    file in *FILE_P.  Perform any allocations in POOL. */
 static svn_error_t *
-open_and_seek_representation(apr_file_t **file_p,
+open_and_seek_representation(svn_file_handle_cache__handle_t **file_p,
                              svn_fs_t *fs,
                              representation_t *rep,
                              apr_pool_t *pool)
 {
+  /* representation headers tend to cluster. Therefore, use separate
+   * file handles for them (controlled by the cookie) to maximize APR 
+   * buffer effectiveness. */
   if (! rep->txn_id)
     return open_and_seek_revision(file_p, fs, rep->revision, rep->offset,
-                                  pool);
+                                  REP_FILE_COOKIE, pool);
   else
-    return open_and_seek_transaction(file_p, fs, rep->txn_id, rep, pool);
+    return open_and_seek_transaction(file_p, fs, rep->txn_id, rep, 
+                                     REP_FILE_COOKIE, pool);
 }
 
 /* Parse the description of a representation from STRING and store it
@@ -2170,7 +2203,7 @@ get_node_revision_body(node_revision_t *
                        const svn_fs_id_t *id,
                        apr_pool_t *pool)
 {
-  apr_file_t *revision_file;
+  svn_file_handle_cache__handle_t *revision_file;
   svn_error_t *err;
   svn_boolean_t is_cached = FALSE;
 
@@ -2182,8 +2215,15 @@ get_node_revision_body(node_revision_t *
   if (svn_fs_fs__id_txn_id(id))
     {
       /* This is a transaction node-rev. */
-      err = svn_io_file_open(&revision_file, path_txn_node_rev(fs, id, pool),
-                             APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
+      fs_fs_data_t *ffd = fs->fsap_data;
+      err = svn_file_handle_cache__open(&revision_file,
+                                        ffd->file_handle_cache,
+                                        path_txn_node_rev(fs, id, pool),
+                                        APR_READ | APR_BUFFERED,
+                                        APR_OS_DEFAULT,
+                                        0,
+                                        DEFAULT_FILE_COOKIE,
+                                        pool);
     }
   else
     {
@@ -2191,6 +2231,7 @@ get_node_revision_body(node_revision_t *
       err = open_and_seek_revision(&revision_file, fs,
                                    svn_fs_fs__id_rev(id),
                                    svn_fs_fs__id_offset(id),
+                                   DEFAULT_FILE_COOKIE,
                                    pool);
     }
 
@@ -2206,9 +2247,11 @@ get_node_revision_body(node_revision_t *
     }
 
   SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
-                                  svn_stream_from_aprfile2(revision_file, FALSE,
-                                                           pool),
-                                  pool));
+                                 svn_stream__from_cached_file_handle
+                                     (revision_file,
+                                      FALSE,
+                                      pool),
+                                 pool));
 
   /* The noderev is not in cache, yet. Add it, if caching has been enabled. */
   return set_cached_node_revision_body(*noderev_p, fs, id, pool);
@@ -2525,7 +2568,10 @@ svn_fs_fs__put_node_revision(svn_fs_t *f
                                    svn_fs_fs__fs_supports_mergeinfo(fs),
                                    pool));
 
-  return svn_io_file_close(noderev_file, pool);
+  SVN_ERR(svn_io_file_close(noderev_file, pool));
+
+  /* we wrote to the db -> sync file contents */
+  return sync_file_handle_cache(fs);
 }
 
 
@@ -2869,28 +2915,45 @@ svn_fs_fs__rev_get_root(svn_fs_id_t **ro
                         apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
-  apr_file_t *revision_file;
+  svn_file_handle_cache__handle_t *revision_file;
+  apr_file_t *apr_rev_file;
   apr_off_t root_offset;
+  svn_cache__t *cache = NULL;
   svn_fs_id_t *root_id = NULL;
   svn_boolean_t is_cached;
 
   SVN_ERR(ensure_revision_exists(fs, rev, pool));
 
+  /* Try to find the ID in our caches.  Once we tried is_packed_rev
+     returned true, we will never try to use the cache for non-packed
+     revs again.  Also, if we find the entry in the cache, this 
+     function cannot be racy because we don't need to access the file. */
+  cache = is_packed_rev(fs, rev)
+        ? ffd->packed_rev_root_id_cache
+        : ffd->rev_root_id_cache;
   SVN_ERR(svn_cache__get((void **) root_id_p, &is_cached,
-                         ffd->rev_root_id_cache, &rev, pool));
+                         cache, &rev, pool));
   if (is_cached)
     return SVN_NO_ERROR;
 
-  SVN_ERR(open_pack_or_rev_file(&revision_file, fs, rev, pool));
-  SVN_ERR(get_root_changes_offset(&root_offset, NULL, revision_file, fs, rev,
+  /* we don't care about the file pointer position */
+  SVN_ERR(open_pack_or_rev_file(&revision_file, fs, rev, -1,
+                                DEFAULT_FILE_COOKIE, pool));
+  apr_rev_file = svn_file_handle_cache__get_apr_handle(revision_file);
+
+  /* it will moved here anyways */
+  SVN_ERR(get_root_changes_offset(&root_offset, NULL, apr_rev_file, fs, rev,
                                   pool));
 
-  SVN_ERR(get_fs_id_at_offset(&root_id, revision_file, fs, rev,
-                              root_offset, pool));
+  SVN_ERR(get_fs_id_at_offset(&root_id, apr_rev_file, fs, rev, root_offset, 
+                              pool));
 
-  SVN_ERR(svn_io_file_close(revision_file, pool));
+  SVN_ERR(svn_file_handle_cache__close(revision_file));
 
-  SVN_ERR(svn_cache__set(ffd->rev_root_id_cache, &rev, root_id, pool));
+  /* At this point, the revision might have already gotten packed
+     but cache is still the one for non-packed IDs.  In that case,
+     it will never be looked up here, again.  So, we are safe. */
+  SVN_ERR(svn_cache__set(cache, &rev, root_id, pool));
 
   *root_id_p = root_id;
 
@@ -3019,7 +3082,10 @@ svn_fs_fs__revision_proplist(apr_hash_t 
    representation is. */
 struct rep_state
 {
-  apr_file_t *file;
+  svn_file_handle_cache__handle_t *file;
+                    /* For convenience, store the APR file handle
+                       along with the surrounding cached file handle. */
+  apr_file_t *apr_file;
                     /* The txdelta window cache to use or NULL. */
   svn_cache__t *window_cache;
   apr_off_t start;  /* The starting offset for the raw
@@ -3044,10 +3110,11 @@ create_rep_state_body(struct rep_state *
   unsigned char buf[4];
 
   SVN_ERR(open_and_seek_representation(&rs->file, fs, rep, pool));
+  rs->apr_file = svn_file_handle_cache__get_apr_handle(rs->file);
   rs->window_cache = ffd->txdelta_window_cache;
 
-  SVN_ERR(read_rep_line(&ra, rs->file, pool));
-  SVN_ERR(get_file_offset(&rs->start, rs->file, pool));
+  SVN_ERR(read_rep_line(&ra, rs->apr_file, pool));
+  SVN_ERR(get_file_offset(&rs->start, rs->apr_file, pool));
   rs->off = rs->start;
   rs->end = rs->start + rep->size;
   *rep_state = rs;
@@ -3058,7 +3125,7 @@ create_rep_state_body(struct rep_state *
     return SVN_NO_ERROR;
 
   /* We are dealing with a delta, find out what version. */
-  SVN_ERR(svn_io_file_read_full2(rs->file, buf, sizeof(buf),
+  SVN_ERR(svn_io_file_read_full2(rs->apr_file, buf, sizeof(buf),
                                  NULL, NULL, pool));
   /* ### Layering violation */
   if (! ((buf[0] == 'S') && (buf[1] == 'V') && (buf[2] == 'N')))
@@ -3373,9 +3440,9 @@ read_window(svn_txdelta_window_t **nwin,
   /* Skip windows to reach the current chunk if we aren't there yet. */
   while (rs->chunk_index < this_chunk)
     {
-      SVN_ERR(svn_txdelta_skip_svndiff_window(rs->file, rs->ver, pool));
+      SVN_ERR(svn_txdelta_skip_svndiff_window(rs->apr_file, rs->ver, pool));
       rs->chunk_index++;
-      SVN_ERR(get_file_offset(&rs->off, rs->file, pool));
+      SVN_ERR(get_file_offset(&rs->off, rs->apr_file, pool));
       if (rs->off >= rs->end)
         return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                                 _("Reading one svndiff window read "
@@ -3390,10 +3457,10 @@ read_window(svn_txdelta_window_t **nwin,
 
   /* Actually read the next window. */
   old_offset = rs->off;
-  stream = svn_stream_from_aprfile2(rs->file, TRUE, pool);
+  stream = svn_stream__from_cached_file_handle(rs->file, TRUE, pool);
   SVN_ERR(svn_txdelta_read_svndiff_window(nwin, stream, rs->ver, pool));
   rs->chunk_index++;
-  SVN_ERR(get_file_offset(&rs->off, rs->file, pool));
+  SVN_ERR(get_file_offset(&rs->off, rs->apr_file, pool));
 
   if (rs->off > rs->end)
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
@@ -3489,7 +3556,7 @@ get_contents(struct rep_read_baton *rb,
       rs = rb->src_state;
       if (((apr_off_t) copy_len) > rs->end - rs->off)
         copy_len = (apr_size_t) (rs->end - rs->off);
-      SVN_ERR(svn_io_file_read_full2(rs->file, cur, copy_len, NULL,
+      SVN_ERR(svn_io_file_read_full2(rs->apr_file, cur, copy_len, NULL,
                                      NULL, rb->pool));
       rs->off += copy_len;
       *len = copy_len;
@@ -3562,10 +3629,10 @@ get_contents(struct rep_read_baton *rb,
                   if ((rs->start + lwindow->sview_offset) != rs->off)
                     {
                       rs->off = rs->start + lwindow->sview_offset;
-                      SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off,
+                      SVN_ERR(svn_io_file_seek(rs->apr_file, APR_SET, &rs->off,
                                                rb->pool));
                     }
-                  SVN_ERR(svn_io_file_read_full2(rs->file, sbuf,
+                  SVN_ERR(svn_io_file_read_full2(rs->apr_file, sbuf,
                                                  lwindow->sview_len,
                                                  NULL, NULL, rb->pool));
                   rs->off += lwindow->sview_len;
@@ -3798,7 +3865,7 @@ svn_fs_fs__get_file_delta_stream(svn_txd
           return SVN_NO_ERROR;
         }
       else
-        SVN_ERR(svn_io_file_close(rep_state->file, pool));
+        SVN_ERR(svn_file_handle_cache__close(rep_state->file));
     }
 
   /* Read both fulltexts and construct a delta. */
@@ -4624,24 +4691,29 @@ svn_fs_fs__paths_changed(apr_hash_t **ch
 {
   apr_off_t changes_offset;
   apr_hash_t *changed_paths;
-  apr_file_t *revision_file;
+  svn_file_handle_cache__handle_t *revision_file;
+  apr_file_t *apr_revision_file;
 
   SVN_ERR(ensure_revision_exists(fs, rev, pool));
 
-  SVN_ERR(open_pack_or_rev_file(&revision_file, fs, rev, pool));
-
-  SVN_ERR(get_root_changes_offset(NULL, &changes_offset, revision_file, fs,
-                                  rev, pool));
+  /* we don't care about the file pointer position */
+  SVN_ERR(open_pack_or_rev_file(&revision_file, fs, rev, -1, 
+                                DEFAULT_FILE_COOKIE, pool));
+  apr_revision_file = svn_file_handle_cache__get_apr_handle(revision_file);
+
+  /* it will moved here anyways */
+  SVN_ERR(get_root_changes_offset(NULL, &changes_offset, apr_revision_file, 
+                                  fs, rev, pool));
 
-  SVN_ERR(svn_io_file_seek(revision_file, APR_SET, &changes_offset, pool));
+  SVN_ERR(svn_io_file_seek(apr_revision_file, APR_SET, &changes_offset, pool));
 
   changed_paths = apr_hash_make(pool);
 
-  SVN_ERR(fetch_all_changes(changed_paths, copyfrom_cache, revision_file,
+  SVN_ERR(fetch_all_changes(changed_paths, copyfrom_cache, apr_revision_file,
                             TRUE, pool));
 
   /* Close the revision file. */
-  SVN_ERR(svn_io_file_close(revision_file, pool));
+  SVN_ERR(svn_file_handle_cache__close(revision_file));
 
   *changed_paths_p = changed_paths;
 
@@ -5011,7 +5083,10 @@ write_next_ids(svn_fs_t *fs,
   SVN_ERR(svn_stream_printf(out_stream, pool, "%s %s\n", node_id, copy_id));
 
   SVN_ERR(svn_stream_close(out_stream));
-  return svn_io_file_close(file, pool);
+  SVN_ERR(svn_io_file_close(file, pool));
+
+  /* we wrote to the db -> sync file contents */
+  return sync_file_handle_cache(fs);
 }
 
 /* Find out what the next unique node-id and copy-id are for
@@ -5249,7 +5324,10 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
                                 strlen(name), name));
     }
 
-  return svn_io_file_close(file, pool);
+  SVN_ERR(svn_io_file_close(file, pool));
+
+  /* we wrote to the db -> sync file contents */
+  return sync_file_handle_cache(fs);
 }
 
 /* Write a single change entry, path PATH, change CHANGE, and copyfrom
@@ -5350,7 +5428,10 @@ svn_fs_fs__add_change(svn_fs_t *fs,
 
   SVN_ERR(write_change_entry(file, path, change, TRUE, pool));
 
-  return svn_io_file_close(file, pool);
+  SVN_ERR(svn_io_file_close(file, pool));
+
+  /* we wrote to the db -> sync file contents */
+  return sync_file_handle_cache(fs);
 }
 
 /* This baton is used by the representation writing streams.  It keeps
@@ -5634,7 +5715,8 @@ rep_write_contents_close(void *baton)
   SVN_ERR(unlock_proto_rev(b->fs, rep->txn_id, b->lockcookie, b->pool));
   svn_pool_destroy(b->pool);
 
-  return SVN_NO_ERROR;
+  /* we wrote to the db -> sync file contents */
+  return sync_file_handle_cache(b->fs);
 }
 
 /* Store a writable stream in *CONTENTS_P that will receive all data
@@ -5736,7 +5818,8 @@ svn_fs_fs__set_proplist(svn_fs_t *fs,
       SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
     }
 
-  return SVN_NO_ERROR;
+  /* we wrote to the db -> sync file contents */
+  return sync_file_handle_cache(fs);
 }
 
 /* Read the 'current' file for filesystem FS and store the next
@@ -6357,6 +6440,9 @@ commit_body(void *baton, apr_pool_t *poo
   SVN_ERR(svn_io_file_flush_to_disk(proto_file, pool));
   SVN_ERR(svn_io_file_close(proto_file, pool));
 
+  /* we wrote to the db -> sync file contents */
+  SVN_ERR(sync_file_handle_cache(cb->fs));
+
   /* We don't unlock the prototype revision file immediately to avoid a
      race with another caller writing to the prototype revision file
      before we commit it. */
@@ -6704,9 +6790,12 @@ recover_get_largest_revision(svn_fs_t *f
   while (1)
     {
       svn_error_t *err;
-      apr_file_t *file;
+      svn_file_handle_cache__handle_t *file;
 
-      err = open_pack_or_rev_file(&file, fs, right, iterpool);
+      /* We don't care about the file pointer position as long as the file 
+         itself exists. */
+      err = open_pack_or_rev_file(&file, fs, right, -1, 
+                                  DEFAULT_FILE_COOKIE, iterpool);
       svn_pool_clear(iterpool);
 
       if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
@@ -6728,9 +6817,11 @@ recover_get_largest_revision(svn_fs_t *f
     {
       svn_revnum_t probe = left + ((right - left) / 2);
       svn_error_t *err;
-      apr_file_t *file;
+      svn_file_handle_cache__handle_t *file;
 
-      err = open_pack_or_rev_file(&file, fs, probe, iterpool);
+      /* Again, ignore the file pointer position. */
+      err = open_pack_or_rev_file(&file, fs, probe, -1, 
+                                  DEFAULT_FILE_COOKIE, iterpool);
       svn_pool_clear(iterpool);
 
       if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
@@ -7010,7 +7101,8 @@ recover_body(void *baton, apr_pool_t *po
 
       for (rev = 0; rev <= max_rev; rev++)
         {
-          apr_file_t *rev_file;
+          svn_file_handle_cache__handle_t *rev_file;
+          apr_file_t *apr_rev_file;
           apr_off_t root_offset;
 
           svn_pool_clear(iterpool);
@@ -7018,12 +7110,18 @@ recover_body(void *baton, apr_pool_t *po
           if (b->cancel_func)
             SVN_ERR(b->cancel_func(b->cancel_baton));
 
-          SVN_ERR(open_pack_or_rev_file(&rev_file, fs, rev, iterpool));
-          SVN_ERR(get_root_changes_offset(&root_offset, NULL, rev_file, fs, rev,
+          /* Any file pointer position will do ... */
+          SVN_ERR(open_pack_or_rev_file(&rev_file, fs, rev, -1,
+                                        DEFAULT_FILE_COOKIE, iterpool));
+          apr_rev_file = svn_file_handle_cache__get_apr_handle(rev_file);
+
+          /* ... because it gets set here explicitly */
+          SVN_ERR(get_root_changes_offset(&root_offset, NULL, 
+                                          apr_rev_file, fs, rev,
                                           iterpool));
-          SVN_ERR(recover_find_max_ids(fs, rev, rev_file, root_offset,
+          SVN_ERR(recover_find_max_ids(fs, rev, apr_rev_file, root_offset,
                                        max_node_id, max_copy_id, iterpool));
-          SVN_ERR(svn_io_file_close(rev_file, iterpool));
+          SVN_ERR(svn_file_handle_cache__close(rev_file));
         }
       svn_pool_destroy(iterpool);
 

Modified: subversion/branches/file-handle-cache/subversion/libsvn_subr/stream.c
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_subr/stream.c?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/libsvn_subr/stream.c (original)
+++ subversion/branches/file-handle-cache/subversion/libsvn_subr/stream.c Sat Oct 29 00:33:21 2011
@@ -44,7 +44,7 @@
 #include "svn_path.h"
 #include "private/svn_eol_private.h"
 #include "private/svn_io_private.h"
-
+#include "private/svn_file_handle_cache.h"
 
 struct svn_stream_t {
   void *baton;
@@ -732,6 +732,10 @@ svn_stream_disown(svn_stream_t *stream, 
 struct baton_apr {
   apr_file_t *file;
   apr_pool_t *pool;
+
+  /* If not NULL, file is actually wrapped into this cached file handle
+   * and should be returned to the file handle cache asap. */
+  svn_file_handle_cache__handle_t *cached_handle;
 };
 
 /* svn_stream_mark_t for streams backed by APR files. */
@@ -751,7 +755,7 @@ read_handler_apr(void *baton, char *buff
       err = svn_io_file_getc(buffer, btn->file, btn->pool);
       if (err)
         {
-          *len = 0;
+        *len = 0;
           if (APR_STATUS_IS_EOF(err->apr_err))
             {
               svn_error_clear(err);
@@ -793,6 +797,17 @@ write_handler_apr(void *baton, const cha
   return err;
 }
 
+/* Returns the cached file handle back to the respective cache object.
+ * This is call is allowed even for btn->cached_handle == NULL.
+ */
+static svn_error_t *
+close_handler_cached_handle(void *baton)
+{
+  struct baton_apr *btn = baton;
+
+  return svn_file_handle_cache__close(btn->cached_handle);
+}
+
 static svn_error_t *
 close_handler_apr(void *baton)
 {
@@ -886,6 +901,36 @@ svn_stream_open_unique(svn_stream_t **st
   return SVN_NO_ERROR;
 }
 
+/* Common initialization code for svn_stream_from_aprfile2() and
+ * svn_stream__from_cached_file_handle().
+ */
+static svn_stream_t *
+stream_from_aprfile(struct baton_apr **baton,
+                    apr_file_t *file,
+                    apr_pool_t *pool)
+{
+  struct baton_apr *new_baton;
+  svn_stream_t *stream;
+
+  /* create and fully initialize the baton */
+  new_baton = apr_palloc(pool, sizeof(*new_baton));
+  new_baton->file = file;
+  new_baton->cached_handle = NULL; /* default */
+  new_baton->pool = pool;
+
+  /* construct the stream vtable, except for the close() function */
+  stream = svn_stream_create(new_baton, pool);
+  svn_stream_set_read(stream, read_handler_apr);
+  svn_stream_set_write(stream, write_handler_apr);
+  svn_stream_set_skip(stream, skip_handler_apr);
+  svn_stream_set_mark(stream, mark_handler_apr);
+  svn_stream_set_seek(stream, seek_handler_apr);
+
+  /* return structures */
+  *baton = new_baton;
+
+  return stream;
+}
 
 svn_stream_t *
 svn_stream_from_aprfile2(apr_file_t *file,
@@ -895,26 +940,46 @@ svn_stream_from_aprfile2(apr_file_t *fil
   struct baton_apr *baton;
   svn_stream_t *stream;
 
+  /* having no file at all is a special case */
   if (file == NULL)
     return svn_stream_empty(pool);
 
-  baton = apr_palloc(pool, sizeof(*baton));
-  baton->file = file;
-  baton->pool = pool;
-  stream = svn_stream_create(baton, pool);
-  svn_stream_set_read(stream, read_handler_apr);
-  svn_stream_set_write(stream, write_handler_apr);
-  svn_stream_set_skip(stream, skip_handler_apr);
-  svn_stream_set_mark(stream, mark_handler_apr);
-  svn_stream_set_seek(stream, seek_handler_apr);
+  /* construct and init the default stream structures */
+  stream = stream_from_aprfile(&baton, file, pool);
   svn_stream__set_is_buffered(stream, is_buffered_handler_apr);
 
+  /* make sure to close the file handle after use if we own it */
   if (! disown)
     svn_stream_set_close(stream, close_handler_apr);
 
   return stream;
 }
 
+svn_stream_t *
+svn_stream__from_cached_file_handle(svn_file_handle_cache__handle_t *file,
+                                    svn_boolean_t disown,
+                                    apr_pool_t *pool)
+{
+  struct baton_apr *baton;
+  svn_stream_t *stream;
+
+  /* having no file at all is a special case (file == NULL is legal, too) */
+  apr_file_t *apr_file = svn_file_handle_cache__get_apr_handle(file);
+  if (apr_file == NULL)
+    return svn_stream_empty(pool);
+
+  /* construct and init the default stream structures */
+  stream = stream_from_aprfile(&baton, apr_file, pool);
+
+  /* store the cached file handle and return it to the cache after use,
+   * if we own it */
+  baton->cached_handle = file;
+  if (! disown)
+    svn_stream_set_close(stream, close_handler_cached_handle);
+
+  return stream;
+}
+
 
 /* Compressed stream support */
 

Modified: subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c (original)
+++ subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c Sat Oct 29 00:33:21 2011
@@ -363,7 +363,7 @@ find_first(svn_file_handle_cache_t *cach
 
   /* the index must contain only used entries, i.e. those that actually
    * contain an open APR file handle. */
-  assert (!result || result->file);
+  assert(!result || result->file);
   return result;
 }
 
@@ -526,6 +526,7 @@ close_handle_before_cleanup(void *handle
 {
   svn_file_handle_cache__handle_t *f = handle_void;
   svn_error_t *err = SVN_NO_ERROR;
+  apr_status_t result = APR_SUCCESS;
 
   /* if this hasn't been done before: 
    * "close" the handle, i.e. return it to the cache 
@@ -537,7 +538,14 @@ close_handle_before_cleanup(void *handle
   f->entry = NULL;
   f->cache = NULL;
 
-  return err ? err->apr_err : APR_SUCCESS;
+  /* process error returns */
+  if (err)
+    {
+      result = err->apr_err;
+      svn_error_clear(err);
+    }
+
+  return result;
 }
 
 /* Create a cached file handle to be returned to the application in F for
@@ -552,7 +560,7 @@ open_entry(svn_file_handle_cache__handle
            apr_pool_t *pool)
 {
   /* any entry can be handed out to the application only once at any time */
-  assert (! entry->open_handle);
+  assert(!entry->open_handle);
 
   /* the entry will no longer be idle */
   remove_from_list(&cache->idle_entries, &entry->idle_link);
@@ -887,10 +895,11 @@ svn_file_handle_cache__create_cache(svn_
   init_list(&new_cache->unused_entries);
 
   new_cache->first_by_name = apr_hash_make(new_cache->pool);
+
+#if APR_HAS_THREADS
   new_cache->mutex = NULL;
 
   /* synchronization support may or may not be needed or available */
-#if APR_HAS_THREADS
   if (thread_safe)
     {
       apr_status_t status = apr_thread_mutex_create(&(new_cache->mutex),

Propchange: subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Sat Oct 29 00:33:21 2011
@@ -1 +1 @@
-/subversion/branches/performance/subversion/libsvn_subr/svn_file_handle_cache.c:981665,981828
+/subversion/branches/performance/subversion/libsvn_subr/svn_file_handle_cache.c:981665,981828,992905,1003430,1029082,1037466,1037470

Modified: subversion/branches/file-handle-cache/subversion/mod_dav_svn/mod_dav_svn.c
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/mod_dav_svn/mod_dav_svn.c?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/mod_dav_svn/mod_dav_svn.c (original)
+++ subversion/branches/file-handle-cache/subversion/mod_dav_svn/mod_dav_svn.c Sat Oct 29 00:33:21 2011
@@ -458,19 +458,29 @@ SVNCacheFullTexts_cmd(cmd_parms *cmd, vo
   return NULL;
 }
 
-static const char *
-SVNInMemoryCacheSize_cmd(cmd_parms *cmd, void *config, const char *arg1)
+static apr_uint64_t
+parse_number(const char *arg)
 {
-  svn_cache_config_t settings = *svn_cache_config_get();
-
   apr_uint64_t value = 0;
-  svn_error_t *err = svn_cstring_atoui64(&value, arg1);
+  svn_error_t *err = svn_cstring_atoui64(&value, arg);
   if (err)
     {
       svn_error_clear(err);
-      return "Invalid decimal number for the SVN cache size.";
+      return (apr_uint64_t)(-1);
     }
 
+  return value;
+}
+
+static const char *
+SVNInMemoryCacheSize_cmd(cmd_parms *cmd, void *config, const char *arg1)
+{
+  svn_cache_config_t settings = *svn_cache_config_get();
+
+  apr_uint64_t value = parse_number(arg1);
+  if (value == (apr_uint64_t)(-1))
+      return "Invalid decimal number for the SVN cache size.";
+
   settings.cache_size = value * 0x400;
 
   svn_cache_config_set(&settings);
@@ -479,6 +489,22 @@ SVNInMemoryCacheSize_cmd(cmd_parms *cmd,
 }
 
 static const char *
+SVNMaxOpenFileHandles_cmd(cmd_parms *cmd, void *config, const char *arg1)
+{
+  svn_fs_cache_config_t settings = *svn_fs_get_cache_config();
+
+  apr_uint64_t value = parse_number(arg1);
+  if (value == (apr_uint64_t)(-1))
+    return "Invalid decimal number for the open file handle count.";
+
+  settings.file_handle_count = (apr_size_t)value;
+
+  svn_fs_set_cache_config(&settings);
+
+  return NULL;
+}
+
+static const char *
 SVNCompressionLevel_cmd(cmd_parms *cmd, void *config, const char *arg1)
 {
   int value = 0;
@@ -1008,6 +1034,11 @@ static const command_rec cmds[] =
                 "in-memory object cache (default value is 16384; 0 deactivates "
                 "the cache)."),
   /* per server */
+  AP_INIT_TAKE1("SVNMaxOpenFileHandles", SVNMaxOpenFileHandles_cmd, NULL,
+               RSRC_CONF,
+               "specify the maximum of unused file handles kept open per "
+               "process (default values is 16)."),
+  /* per server */
   AP_INIT_TAKE1("SVNCompressionLevel", SVNCompressionLevel_cmd, NULL,
                 RSRC_CONF,
                 "specifies the compression level used before sending file "

Modified: subversion/branches/file-handle-cache/subversion/svnadmin/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/svnadmin/main.c?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/svnadmin/main.c (original)
+++ subversion/branches/file-handle-cache/subversion/svnadmin/main.c Sat Oct 29 00:33:21 2011
@@ -280,6 +280,11 @@ static const apr_getopt_option_t options
         "                             minimize redundant operations. Default: 16.\n"
         "                             [used for FSFS repositories only]")},
 
+    {"open-file-count",       'F', 1,
+     N_("maximum number of files kept open after usage\n"
+        "                             to reduce OS and I/O overhead. Default: 64.\n"
+        "                             [used for FSFS repositories only]")},
+
     {NULL}
   };
 
@@ -310,7 +315,7 @@ static const svn_opt_subcommand_desc2_t 
     "essence compresses the repository by only storing the differences or\n"
     "delta from the preceding revision.  If no revisions are specified,\n"
     "this will simply deltify the HEAD revision.\n"),
-   {'r', 'q', 'M'} },
+   {'r', 'q', 'M', 'F'} },
 
   {"dump", subcommand_dump, {0}, N_
    ("usage: svnadmin dump REPOS_PATH [-r LOWER[:UPPER] [--incremental]]\n\n"
@@ -323,7 +328,7 @@ static const svn_opt_subcommand_desc2_t 
     "every path present in the repository as of that revision.  (In either\n"
     "case, the second and subsequent revisions, if any, describe only paths\n"
     "changed in those revisions.)\n"),
-  {'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M'} },
+   {'r', svnadmin__incremental, svnadmin__deltas, 'q', 'M', 'F'} },
 
   {"help", subcommand_help, {"?", "h"}, N_
    ("usage: svnadmin help [SUBCOMMAND...]\n\n"
@@ -357,7 +362,7 @@ static const svn_opt_subcommand_desc2_t 
     "in the dump stream whose revision numbers match the specified range.\n"),
    {'q', 'r', svnadmin__ignore_uuid, svnadmin__force_uuid,
     svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook,
-    svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M'} },
+    svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M', 'F'} },
 
   {"lslocks", subcommand_lslocks, {0}, N_
    ("usage: svnadmin lslocks REPOS_PATH [PATH-IN-REPOS]\n\n"
@@ -440,7 +445,7 @@ static const svn_opt_subcommand_desc2_t 
   {"verify", subcommand_verify, {0}, N_
    ("usage: svnadmin verify REPOS_PATH\n\n"
     "Verifies the data stored in the repository.\n"),
-  {'r', 'q', 'M'} },
+   {'r', 'q', 'M', 'F'} },
 
   { NULL, NULL, {0}, NULL, {0} }
 };
@@ -473,6 +478,7 @@ struct svnadmin_opt_state
   enum svn_repos_load_uuid uuid_action;             /* --ignore-uuid,
                                                        --force-uuid */
   apr_uint64_t memory_cache_size;                   /* --memory-cache-size M */
+  apr_size_t open_file_count;                       /* --open-file-count F */
   const char *parent_dir;
 
   const char *config_dir;    /* Overriding Configuration Directory */
@@ -1692,6 +1698,7 @@ main(int argc, const char *argv[])
   opt_state.start_revision.kind = svn_opt_revision_unspecified;
   opt_state.end_revision.kind = svn_opt_revision_unspecified;
   opt_state.memory_cache_size = svn_cache_config_get()->cache_size;
+  opt_state.open_file_count = 64;
 
   /* Parse options. */
   err = svn_cmdline__getopt_init(&os, argc, argv, pool);
@@ -1755,6 +1762,9 @@ main(int argc, const char *argv[])
         opt_state.memory_cache_size
             = 0x100000 * apr_strtoi64(opt_arg, NULL, 0);
         break;
+      case 'F':
+        opt_state.open_file_count = apr_strtoi64(opt_arg, NULL, 0);
+        break;
       case svnadmin__version:
         opt_state.version = TRUE;
         break;
@@ -1973,6 +1983,7 @@ main(int argc, const char *argv[])
     svn_cache_config_t settings = *svn_cache_config_get();
 
     settings.cache_size = opt_state.memory_cache_size;
+    settings.file_handle_count = opt_state.open_file_count;
     settings.single_threaded = TRUE;
 
     svn_cache_config_set(&settings);

Modified: subversion/branches/file-handle-cache/subversion/svnserve/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/svnserve/main.c?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/svnserve/main.c (original)
+++ subversion/branches/file-handle-cache/subversion/svnserve/main.c Sat Oct 29 00:33:21 2011
@@ -221,6 +221,14 @@ static const apr_getopt_option_t svnserv
         "Default is yes.\n"
         "                             "
         "[used for FSFS repositories only]")},
+    {"open-file-count", 'F', 1, 
+     N_("maximum number of files kept open after usage\n"
+        "                             "
+        "to reduce OS and I/O overhead.\n"
+        "                             "
+        "Default is 64 and 16 for non-threaded mode.\n"
+        "                             "
+        "[used for FSFS repositories only]")},
 #ifdef CONNECTION_HAVE_THREAD_OPTION
     /* ### Making the assumption here that WIN32 never has fork and so
      * ### this option never exists when --service exists. */
@@ -475,6 +483,7 @@ int main(int argc, const char *argv[])
   params.memory_cache_size = (apr_uint64_t)-1;
   params.cache_fulltexts = TRUE;
   params.cache_txdeltas = FALSE;
+  params.open_file_count = -1;
 
   while (1)
     {
@@ -609,6 +618,9 @@ int main(int argc, const char *argv[])
              = svn_tristate__from_word(arg) == svn_tristate_true;
           break;
 
+        case 'F':
+          params.open_file_count = apr_strtoi64(arg, NULL, 0);
+
 #ifdef WIN32
         case SVNSERVE_OPT_SERVICE:
           if (run_mode != run_mode_service)
@@ -878,7 +890,9 @@ int main(int argc, const char *argv[])
     if (params.memory_cache_size != -1)
       settings.cache_size = params.memory_cache_size;
 
-    settings.single_threaded = TRUE;
+    if (params.open_file_count != -1)
+      settings.file_handle_count = params.open_file_count;
+
     if (handling_mode == connection_mode_thread)
       {
 #ifdef APR_HAS_THREADS

Modified: subversion/branches/file-handle-cache/subversion/svnserve/server.h
URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/svnserve/server.h?rev=1190714&r1=1190713&r2=1190714&view=diff
==============================================================================
--- subversion/branches/file-handle-cache/subversion/svnserve/server.h (original)
+++ subversion/branches/file-handle-cache/subversion/svnserve/server.h Sat Oct 29 00:33:21 2011
@@ -125,6 +125,9 @@ typedef struct serve_params_t {
      Defaults to SVN_DELTA_COMPRESSION_LEVEL_DEFAULT. */
   int compression_level;
 
+  /* Number of handles kept open independently of there actual use
+     (used by FSFS only). */
+  apr_size_t open_file_count;
 } serve_params_t;
 
 /* Serve the connection CONN according to the parameters PARAMS. */