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/04/17 14:28:51 UTC

svn commit: r1094134 - in /subversion/trunk/subversion/libsvn_fs_fs: caching.c fs.h fs_fs.c fs_fs.h tree.c

Author: stefan2
Date: Sun Apr 17 12:28:50 2011
New Revision: 1094134

URL: http://svn.apache.org/viewvc?rev=1094134&view=rev
Log:
After all that preparation, finally introduce in-transaction directory
caching that makes file and folder operations O(1) instead of
O(#direntries).

The data lookup uses the same functions as non-txn directory data.
We simply need to update the txn-local cache whenever the respective
external (on disk) directory file gets updated or removed.

Please note that the location of the cache object is not ideal as it
resides in fs_fs_data instead of being part of fs_txn_root_data.
But since the latter is a wrapper around the first, it will not be
available in all the places that simply use svn_fs_t. Trying to fix
that requires large-scale rework of function signatures and their
callers. 

* subversion/libsvn_fs_fs/fs.h
  (fs_fs_data_t): add new cache instance
* subversion/libsvn_fs_fs/fs_fs.h
  (svn_fs_fs__initialize_caches): differentiate from next function in docstring
  (svn_fs_fs__initialize_txn_caches): declare new private API function
* subversion/libsvn_fs_fs/caching.c
  (remove_txn_cache): new utility function
  (svn_fs_fs__initialize_txn_caches): implement new private API function
* subversion/libsvn_fs_fs/tree.c
  (make_txn_root): initialize txn-local caches when creating a transaction

* subversion/libsvn_fs_fs/fs_fs.c
  (locate_dir_cache): new utility function
  (svn_fs_fs__rep_contents_dir, svn_fs_fs__rep_contents_dir_entry):
   call that new utility function
  (svn_fs_fs__set_entry, svn_fs_fs__delete_node_revision):
   update the txn-local cache whenever the respective txn-local
   files get modified

Modified:
    subversion/trunk/subversion/libsvn_fs_fs/caching.c
    subversion/trunk/subversion/libsvn_fs_fs/fs.h
    subversion/trunk/subversion/libsvn_fs_fs/fs_fs.c
    subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h
    subversion/trunk/subversion/libsvn_fs_fs/tree.c

Modified: subversion/trunk/subversion/libsvn_fs_fs/caching.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/caching.c?rev=1094134&r1=1094133&r2=1094134&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/caching.c (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/caching.c Sun Apr 17 12:28:50 2011
@@ -330,3 +330,66 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
 
   return SVN_NO_ERROR;
 }
+
+/* APR pool cleanup handler that will reset the cache pointer given in
+   BATON_VOID. */
+static apr_status_t
+remove_txn_cache(void *baton_void)
+{
+  svn_cache__t **cache_p = baton_void;
+  *cache_p = NULL;
+
+  return  APR_SUCCESS;
+}
+
+svn_error_t *
+svn_fs_fs__initialize_txn_caches(svn_fs_t *fs,
+                                 const char *txn_id,
+                                 apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  /* Transaction content needs to be carefully prefixed to virtually
+     eliminate any chance for conflicts. The (repo, txn_id) pair
+     should be unique but if a transaction fails, it might be possible
+     to start a new transaction later that receives the same id.
+     Therefore, throw in a uuid as well - just to be sure. */
+  const char *prefix = apr_pstrcat(pool,
+                                   "fsfs:", ffd->uuid,
+                                   "/", fs->path,
+                                   ":", txn_id,
+                                   ":", svn_uuid_generate(pool), ":",
+                                   (char *)NULL);
+
+  /* There must be no concurrent transactions in progress for the same
+     FSFS access / session object. Maybe, you forgot to clean POOL. */
+  SVN_ERR_ASSERT(ffd->txn_dir_cache == NULL);
+
+  /* create a txn-local directory cache */
+  if (svn_fs__get_global_membuffer_cache())
+    SVN_ERR(svn_cache__create_membuffer_cache(&(ffd->txn_dir_cache),
+                                              svn_fs__get_global_membuffer_cache(),
+                                              svn_fs_fs__serialize_dir_entries,
+                                              svn_fs_fs__deserialize_dir_entries,
+                                              APR_HASH_KEY_STRING,
+                                              apr_pstrcat(pool, prefix, "TXNDIR",
+                                                          (char *)NULL),
+                                              pool));
+  else
+    SVN_ERR(svn_cache__create_inprocess(&(ffd->txn_dir_cache),
+                                        svn_fs_fs__serialize_dir_entries,
+                                        svn_fs_fs__deserialize_dir_entries,
+                                        APR_HASH_KEY_STRING,
+                                        1024, 8, FALSE,
+                                        apr_pstrcat(pool, prefix, "TXNDIR",
+                                            (char *)NULL),
+                                        pool));
+
+  /* reset the transaction-specific cache if the pool gets cleaned up. */
+  apr_pool_cleanup_register(pool,
+                            &(ffd->txn_dir_cache),
+                            remove_txn_cache,
+                            apr_pool_cleanup_null);
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/trunk/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/fs.h?rev=1094134&r1=1094133&r2=1094134&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/fs.h Sun Apr 17 12:28:50 2011
@@ -248,6 +248,10 @@ typedef struct fs_fs_data_t
   /* Cache for node_revision_t objects; the key is (revision, id offset) */
   svn_cache__t *node_revision_cache;
 
+  /* Tempoary cache for changed directories yet to be committed; maps from
+     unparsed FS ID to ###x.  NULL outside transactions. */
+  svn_cache__t *txn_dir_cache;
+
   /* Data shared between all svn_fs_t objects for a given filesystem. */
   fs_fs_shared_data_t *shared;
 

Modified: subversion/trunk/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/fs_fs.c?rev=1094134&r1=1094133&r2=1094134&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/fs_fs.c Sun Apr 17 12:28:50 2011
@@ -4007,24 +4007,35 @@ parse_dir_entries(apr_hash_t **entries_p
   return SVN_NO_ERROR;
 }
 
+/* Return the cache object in FS responsible to storing the directory
+ * the NODEREV. If none exists, return NULL. */
+static svn_cache__t *
+locate_dir_cache(svn_fs_t *fs,
+                 node_revision_t *noderev)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  return svn_fs_fs__id_txn_id(noderev->id)
+      ? ffd->txn_dir_cache
+      : ffd->dir_cache;
+}
+
 svn_error_t *
 svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
                             svn_fs_t *fs,
                             node_revision_t *noderev,
                             apr_pool_t *pool)
 {
-  fs_fs_data_t *ffd = fs->fsap_data;
   const char *unparsed_id = NULL;
   apr_hash_t *unparsed_entries, *parsed_entries;
 
-  /* Are we looking for an immutable directory?  We could try the
-   * cache. */
-  if (! svn_fs_fs__id_txn_id(noderev->id))
+  /* find the cache we may use */
+  svn_cache__t *cache = locate_dir_cache(fs, noderev);
+  if (cache)
     {
       svn_boolean_t found;
 
       unparsed_id = svn_fs_fs__id_unparse(noderev->id, pool)->data;
-      SVN_ERR(svn_cache__get((void **) entries_p, &found, ffd->dir_cache,
+      SVN_ERR(svn_cache__get((void **) entries_p, &found, cache,
                              unparsed_id, pool));
       if (found)
         return SVN_NO_ERROR;
@@ -4035,9 +4046,9 @@ svn_fs_fs__rep_contents_dir(apr_hash_t *
   SVN_ERR(get_dir_contents(unparsed_entries, fs, noderev, pool));
   SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries, pool));
 
-  /* If this is an immutable directory, let's cache the contents. */
-  if (! svn_fs_fs__id_txn_id(noderev->id))
-    SVN_ERR(svn_cache__set(ffd->dir_cache, unparsed_id, parsed_entries, pool));
+  /* Update the cache, if we are to use one. */
+  if (cache)
+    SVN_ERR(svn_cache__set(cache, unparsed_id, parsed_entries, pool));
 
   *entries_p = parsed_entries;
   return SVN_NO_ERROR;
@@ -4050,26 +4061,26 @@ svn_fs_fs__rep_contents_dir_entry(svn_fs
                                   const char *name,
                                   apr_pool_t *pool)
 {
-  fs_fs_data_t *ffd = fs->fsap_data;
   svn_boolean_t found = FALSE;
 
-  /* Are we looking for an immutable directory?  We could try the
-   * cache. */
-  if (! svn_fs_fs__id_txn_id(noderev->id))
+  /* find the cache we may use */
+  svn_cache__t *cache = locate_dir_cache(fs, noderev);
+  if (cache)
     {
       const char *unparsed_id =
         svn_fs_fs__id_unparse(noderev->id, pool)->data;
 
-      /* Cache lookup. Return on the requested part of the dir info. */
+      /* Cache lookup. */
       SVN_ERR(svn_cache__get_partial((void **)dirent,
                                      &found,
-                                     ffd->dir_cache,
+                                     cache,
                                      unparsed_id,
                                      svn_fs_fs__extract_dir_entry,
                                      (void*)name,
                                      pool));
     }
 
+  /* fetch data from disk if we did not find it in the cache */
   if (! found)
     {
       apr_hash_t *entries;
@@ -5214,6 +5225,7 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
   const char *filename = path_txn_node_children(fs, parent_noderev->id, pool);
   apr_file_t *file;
   svn_stream_t *out;
+  fs_fs_data_t *ffd = fs->fsap_data;
 
   if (!rep || !rep->txn_id)
     {
@@ -5255,6 +5267,30 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
       out = svn_stream_from_aprfile2(file, TRUE, pool);
     }
 
+  /* if we have a directory cache for this transaction, update it */
+  if (ffd->txn_dir_cache)
+    {
+      apr_pool_t *subpool = svn_pool_create(pool);
+
+      /* build parameters: (name, new entry) pair */
+      const char *key =
+          svn_fs_fs__id_unparse(parent_noderev->id, subpool)->data;
+      replace_baton_t baton = {name, NULL};
+
+      if (id)
+        {
+          baton.new_entry = apr_pcalloc(subpool, sizeof(*baton.new_entry));
+          baton.new_entry->name = name;
+          baton.new_entry->kind = kind;
+          baton.new_entry->id = id;
+        }
+
+      /* actually update the cached directory (if cached) */
+      SVN_ERR(svn_cache__set_partial(ffd->txn_dir_cache, key, svn_fs_fs__replace_dir_entry, &baton, subpool));
+
+      svn_pool_destroy(subpool);
+    }
+
   /* Append an incremental hash entry for the entry change. */
   if (id)
     {
@@ -7368,9 +7404,19 @@ svn_fs_fs__delete_node_revision(svn_fs_t
   /* Delete any mutable data representation. */
   if (noderev->data_rep && noderev->data_rep->txn_id
       && noderev->kind == svn_node_dir)
+    {
+      fs_fs_data_t *ffd = fs->fsap_data;
       SVN_ERR(svn_io_remove_file2(path_txn_node_children(fs, id, pool), FALSE,
                                   pool));
 
+      /* remove the corresponding entry from the cache, if such exists */
+      if (ffd->txn_dir_cache)
+        {
+          const char *key = svn_fs_fs__id_unparse(id, pool)->data;
+          SVN_ERR(svn_cache__set(ffd->txn_dir_cache, key, NULL, pool));
+        }
+    }
+
   return svn_io_remove_file2(path_txn_node_rev(fs, id, pool), FALSE, pool);
 }
 

Modified: subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h?rev=1094134&r1=1094133&r2=1094134&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/fs_fs.h Sun Apr 17 12:28:50 2011
@@ -505,11 +505,17 @@ svn_fs_fs__get_node_origin(const svn_fs_
                            apr_pool_t *pool);
 
 
-/* Sets up the svn_cache__t structures in FS.  POOL is used for
-   temporary allocations. */
+/* Sets up the non-transaction-local svn_cache__t structures in FS.
+   POOL is used for temporary allocations. */
 svn_error_t *
 svn_fs_fs__initialize_caches(svn_fs_t *fs, apr_pool_t *pool);
 
+/* Sets up the svn_cache__t structures local to transaction TXN_ID in FS.
+   POOL is used for temporary allocations. */
+svn_error_t *
+svn_fs_fs__initialize_txn_caches(svn_fs_t *fs,
+                                 const char *txn_id,
+                                 apr_pool_t *pool);
 
 /* Possibly pack the repository at PATH.  This just take full shards, and
    combines all the revision files into a single one, with a manifest header.

Modified: subversion/trunk/subversion/libsvn_fs_fs/tree.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/tree.c?rev=1094134&r1=1094133&r2=1094134&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/tree.c (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/tree.c Sun Apr 17 12:28:50 2011
@@ -3901,6 +3901,12 @@ make_txn_root(svn_fs_root_t **root_p,
                                       apr_pstrcat(pool, txn, ":TXN", (char *)NULL),
                                       root->pool));
 
+  /* Initialize transaction-local caches in FS.
+
+     Note that we cannot put those caches in frd because that content
+     fs root object is not available where we would need it. */
+  svn_fs_fs__initialize_txn_caches(fs, txn, pool);
+
   root->fsap_data = frd;
 
   *root_p = root;