Author: rhuijben
Date: Fri Dec 20 01:58:27 2013
New Revision: 1552475

URL: http://svn.apache.org/r1552475
Log:
In ra_serf optimize the obtaining of inherited properties for servers that
don't have the new in 1.8 report by pipelining the property requests. This
considerably speeds up update and checkout against high latency 1.8 servers
on paths deep in the repository.

* subversion/libsvn_ra_serf/inherited_props.c
  (includes): Add svn_sorts.h
  (iprop_rq_info_t): New struct.
  (keep_only_regular_props): New function.
  (get_iprops_via_more_requests): New function.
  (svn_ra_serf__get_inherited_props): Call get_iprops_via_more_requests when
    the repository doesn't report support for iprops.

* subversion/libsvn_ra_serf/ra_serf.h
  (svn_ra_serf__reparent): Add prototype.

* subversion/libsvn_ra_serf/serf.c
  (svn_ra_serf__reparent): Make library public.

Modified:
    subversion/trunk/subversion/libsvn_ra_serf/inherited_props.c
    subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
    subversion/trunk/subversion/libsvn_ra_serf/serf.c

Modified: subversion/trunk/subversion/libsvn_ra_serf/inherited_props.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/inherited_props.c?rev=1552475&r1=1552474&r2=1552475&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/inherited_props.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/inherited_props.c Fri Dec 20 
01:58:27 2013
@@ -29,6 +29,7 @@
 #include "svn_hash.h"
 #include "svn_path.h"
 #include "svn_ra.h"
+#include "svn_sorts.h"
 #include "svn_string.h"
 #include "svn_xml.h"
 #include "svn_props.h"
@@ -220,6 +221,210 @@ create_iprops_body(serf_bucket_t **bkt,
   return SVN_NO_ERROR;
 }
 
+/* Per request information for get_iprops_via_more_requests */
+typedef struct iprop_rq_info_t
+{
+  const char *relpath;
+  const char *urlpath;
+  apr_hash_t *props;
+  svn_ra_serf__handler_t *handler;
+} iprop_rq_info_t;
+
+/* Removes all non regular properties from PROPS */
+static void
+keep_only_regular_props(apr_hash_t *props,
+                        apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+
+  for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi))
+    {
+      const char *propname = svn__apr_hash_index_key(hi);
+
+      if (svn_property_kind2(propname) != svn_prop_regular_kind)
+        svn_hash_sets(props, propname, NULL);
+    }
+}
+
+/* Assumes session reparented to the repository root. The old session
+   root is passed as session_url */
+static svn_error_t *
+get_iprops_via_more_requests(svn_ra_session_t *ra_session,
+                             apr_array_header_t **iprops,
+                             const char *session_url,
+                             const char *path,
+                             svn_revnum_t revision,
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool)
+{
+  svn_ra_serf__session_t *session = ra_session->priv;
+  const char *url;
+  const char *relpath;
+  apr_array_header_t *rq_info;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_interval_time_t waittime_left = session->timeout;
+  const svn_revnum_t rev_marker = SVN_INVALID_REVNUM;
+  int i;
+
+  rq_info = apr_array_make(scratch_pool, 16, sizeof(iprop_rq_info_t *));
+
+  if (!svn_path_is_empty(path))
+    url = svn_path_url_add_component2(session_url, path, scratch_pool);
+  else
+    url = session_url;
+
+  relpath = svn_uri_skip_ancestor(session->repos_root_str, url, scratch_pool);
+
+  /* Create all requests */
+  while (relpath[0] != '\0')
+    {
+      iprop_rq_info_t *rq = apr_pcalloc(scratch_pool, sizeof(*rq));
+
+      relpath = svn_relpath_dirname(relpath, scratch_pool);
+
+      rq->relpath = relpath;
+      rq->props = apr_hash_make(scratch_pool);
+
+      SVN_ERR(svn_ra_serf__get_stable_url(&rq->urlpath, NULL, session,
+                                          session->conns[0],
+                                          svn_path_url_add_component2(
+                                                session->repos_root.path,
+                                                relpath, scratch_pool),
+                                          revision,
+                                          scratch_pool, scratch_pool));
+
+      SVN_ERR(svn_ra_serf__deliver_props(&rq->handler, rq->props, session,
+                                         session->conns[0], rq->urlpath,
+                                         rev_marker, "0", all_props,
+                                         NULL, scratch_pool));
+
+      /* Allow ignoring authz problems */
+      rq->handler->no_fail_on_http_failure_status = TRUE;
+
+      svn_ra_serf__request_create(rq->handler);
+
+      APR_ARRAY_PUSH(rq_info, iprop_rq_info_t *) = rq;
+    }
+
+  while (TRUE)
+    {
+      apr_status_t status;
+      svn_error_t *err;
+
+      svn_pool_clear(iterpool);
+
+      if (session->cancel_func)
+        SVN_ERR(session->cancel_func(session->cancel_baton));
+
+      status = serf_context_run(session->context,
+                                SVN_RA_SERF__CONTEXT_RUN_DURATION,
+                                iterpool);
+
+      err = session->pending_error;
+      session->pending_error = SVN_NO_ERROR;
+
+      /* If the context duration timeout is up, we'll subtract that
+         duration from the total time alloted for such things.  If
+         there's no time left, we fail with a message indicating that
+         the connection timed out.  */
+      if (APR_STATUS_IS_TIMEUP(status))
+        {
+          status = 0;
+
+          if (session->timeout)
+            {
+              if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
+                {
+                  waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
+                }
+              else
+                {
+                  return
+                      svn_error_compose_create(
+                            err,
+                            svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+                                             _("Connection timed out")));
+                }
+            }
+        }
+      else
+        {
+          waittime_left = session->timeout;
+        }
+
+      SVN_ERR(err);
+      if (status)
+        {
+          /* ### This omits SVN_WARNING, and possibly relies on the fact that
+             ### MAX(SERF_ERROR_*) < SVN_ERR_BAD_CATEGORY_START? */
+          if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST)
+            {
+              /* apr can't translate subversion errors to text */
+              SVN_ERR_W(svn_error_create(status, NULL, NULL),
+                        _("Error running context"));
+            }
+
+          return svn_ra_serf__wrap_err(status, _("Error running context"));
+        }
+
+      for (i = 0; i < rq_info->nelts; i++)
+        {
+          iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *);
+
+          if (!rq->handler->done)
+            break;
+        }
+
+      if (i >= rq_info->nelts)
+        break; /* All requests done */
+    }
+
+  *iprops = apr_array_make(result_pool, rq_info->nelts,
+                           sizeof(svn_prop_inherited_item_t *));
+
+  /* And now create the result set */
+  for (i = 0; i < rq_info->nelts; i++)
+    {
+      iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *);
+      apr_hash_t *node_props;
+      svn_prop_inherited_item_t *new_iprop;
+
+      if (rq->handler->sline.code >= 400 && rq->handler->sline.code != 403)
+        {
+          return svn_error_trace(
+                        svn_ra_serf__error_on_status(rq->handler->sline,
+                                                     rq->handler->path,
+                                                     rq->handler->location));
+        }
+
+      /* Obtain the real properties from the double hash */
+      node_props = apr_hash_get(rq->props, &rev_marker, sizeof(rev_marker));
+
+      if (!node_props)
+        continue;
+
+      node_props = svn_hash_gets(node_props, rq->urlpath);
+
+      if (!node_props)
+        continue;
+
+      SVN_ERR(svn_ra_serf__flatten_props(&node_props, node_props,
+                                         scratch_pool, scratch_pool));
+
+      keep_only_regular_props(node_props, scratch_pool);
+
+      if (!apr_hash_count(node_props))
+        continue;
+
+      new_iprop = apr_palloc(result_pool, sizeof(*new_iprop));
+      new_iprop->path_or_url = apr_pstrdup(result_pool, rq->relpath);
+      new_iprop->prop_hash = svn_prop_hash_dup(node_props, result_pool);
+      svn_sort__array_insert(&new_iprop, *iprops, 0);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Request a inherited-props-report from the URL attached to RA_SESSION,
    and fill the IPROPS array hash with the results.  */
 svn_error_t *
@@ -243,11 +448,33 @@ svn_ra_serf__get_inherited_props(svn_ra_
 
   if (!iprop_capable)
     {
-      /* ### TODO: Use pipelined propfind requests to obtain properties,
-                   without waiting for every intermediate response */
+      svn_error_t *err;
+      const char *reparent_uri = NULL;
+      const char *session_uri;
+      const char *repos_root_url;
+
+      SVN_ERR(svn_ra_serf__get_repos_root(ra_session, &repos_root_url,
+                                          scratch_pool));
+
+      session_uri = apr_pstrdup(scratch_pool, session->session_url_str);
+      if (strcmp(repos_root_url, session->session_url_str) != 0)
+        {
+          reparent_uri  = session_uri;
+          SVN_ERR(svn_ra_serf__reparent(ra_session, repos_root_url,
+                                        scratch_pool));
+        }
 
       /* For now, use implementation in libsvn_ra */
-      return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
+      err = get_iprops_via_more_requests(ra_session, iprops, session_uri, path,
+                                         revision, result_pool, scratch_pool);
+
+      if (reparent_uri)
+        err = svn_error_compose_create(err,
+                                       svn_ra_serf__reparent(ra_session,
+                                                             reparent_uri ,
+                                                             scratch_pool));
+
+      return svn_error_trace(err);
     }
 
   SVN_ERR(svn_ra_serf__get_stable_url(&req_url,

Modified: subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h?rev=1552475&r1=1552474&r2=1552475&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h Fri Dec 20 01:58:27 
2013
@@ -1495,6 +1495,12 @@ svn_ra_serf__get_stable_url(const char *
 
 /** RA functions **/
 
+/* Implements svn_ra__vtable_t.reparent(). */
+svn_error_t *
+svn_ra_serf__reparent(svn_ra_session_t *ra_session,
+                      const char *url,
+                      apr_pool_t *pool);
+
 /* Implements svn_ra__vtable_t.get_log(). */
 svn_error_t *
 svn_ra_serf__get_log(svn_ra_session_t *session,

Modified: subversion/trunk/subversion/libsvn_ra_serf/serf.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/serf.c?rev=1552475&r1=1552474&r2=1552475&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/serf.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/serf.c Fri Dec 20 01:58:27 2013
@@ -767,7 +767,7 @@ ra_serf_dup_session(svn_ra_session_t *ne
 }
 
 /* Implements svn_ra__vtable_t.reparent(). */
-static svn_error_t *
+svn_error_t *
 svn_ra_serf__reparent(svn_ra_session_t *ra_session,
                       const char *url,
                       apr_pool_t *pool)


Reply via email to