Restrict total parallel DNS resolutions

A/B tests suggest that large numbers of parallel
resolutions may cause the DNS failure rate
(re: ratio of "host not found" to "found") to
rise.  To stay safely away from that threshold,
this change lowers the maximum parallel resolutions
to 8, and restricts the speculative resolutions
to 3.

We are also running A/B tests which will look at the
impact of modulating either of these values.

BUG=3041
r=eroman
Review URL: http://codereview.chromium.org/4111004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64369 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/net/predictor.h b/chrome/browser/net/predictor.h
index e6363ad..f735cb4 100644
--- a/chrome/browser/net/predictor.h
+++ b/chrome/browser/net/predictor.h
@@ -263,7 +263,10 @@
   // When true, we don't make new lookup requests.
   bool shutdown_;
 
-  // The number of concurrent lookups currently allowed.
+  // The number of concurrent speculative lookups currently allowed to be sent
+  // to the resolver.  Any additional lookups will be queued to avoid exceeding
+  // this value.  The queue is a priority queue that will accelerate
+  // sub-resource speculation, and retard resolutions suggested by page scans.
   const size_t max_concurrent_dns_lookups_;
 
   // The maximum queueing delay that is acceptable before we enter congestion
diff --git a/chrome/browser/net/predictor_api.cc b/chrome/browser/net/predictor_api.cc
index 9149095..a0d885b 100644
--- a/chrome/browser/net/predictor_api.cc
+++ b/chrome/browser/net/predictor_api.cc
@@ -41,11 +41,35 @@
 static UrlList GetPredictedUrlListAtStartup(PrefService* user_prefs,
                                             PrefService* local_state);
 
+// Given that the underlying Chromium resolver defaults to a total maximum of
+// 8 paralell resolutions, we will avoid any chance of starving navigational
+// resolutions by limiting the number of paralell speculative resolutions.
+// TODO(jar): Move this limitation into the resolver.
 // static
-const size_t PredictorInit::kMaxPrefetchConcurrentLookups = 8;
+const size_t PredictorInit::kMaxSpeculativeParallelResolves = 3;
 
+// To control our congestion avoidance system, which discards a queue when
+// resolutions are "taking too long," we need an expected resolution time.
+// Common average is in the range of 300-500ms.
+static const int kExpectedResolutionTimeMs = 500;
+
+// To control the congestion avoidance system, we need an estimate of how many
+// speculative requests may arrive at once.  Since we currently only keep 8
+// subresource names for each frame, we'll use that as our basis.  Note that
+// when scanning search results lists, we might actually get 10 at a time, and
+// wikipedia can often supply (during a page scan) upwards of 50.  In those odd
+// cases, we may discard some of the later speculative requests mistakenly
+// assuming that the resolutions took too long.
+static const int kTypicalSpeculativeGroupSize = 8;
+
+// The next constant specifies an amount of queueing delay that is "too large,"
+// and indicative of problems with resolutions (perhaps due to an overloaded
+// router, or such).  When we exceed this delay, congestion avoidance will kick
+// in and all speculations in the queue will be discarded.
 // static
-const int PredictorInit::kMaxPrefetchQueueingDelayMs = 500;
+const int PredictorInit::kMaxSpeculativeResolveQueueDelayMs =
+    (kExpectedResolutionTimeMs * kTypicalSpeculativeGroupSize) /
+    kMaxSpeculativeParallelResolves;
 
 // A version number for prefs that are saved. This should be incremented when
 // we change the format so that we discard old data.
@@ -351,8 +375,10 @@
 //------------------------------------------------------------------------------
 
 static void InitNetworkPredictor(TimeDelta max_dns_queue_delay,
-    size_t max_concurrent, PrefService* user_prefs, PrefService* local_state,
-    bool preconnect_enabled) {
+                                 size_t max_parallel_resolves,
+                                 PrefService* user_prefs,
+                                 PrefService* local_state,
+                                 bool preconnect_enabled) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   bool prefetching_enabled =
@@ -367,7 +393,7 @@
           local_state->GetMutableList(prefs::kDnsHostReferralList)->DeepCopy());
 
   g_browser_process->io_thread()->InitNetworkPredictor(
-      prefetching_enabled, max_dns_queue_delay, max_concurrent, urls,
+      prefetching_enabled, max_dns_queue_delay, max_parallel_resolves, urls,
       referral_list, preconnect_enabled);
 }
 
@@ -554,33 +580,35 @@
 
   if (trial_->group() != disabled_prefetch) {
     // Initialize the DNS prefetch system.
-    size_t max_concurrent = kMaxPrefetchConcurrentLookups;
-    int max_queueing_delay_ms = kMaxPrefetchQueueingDelayMs;
+    size_t max_parallel_resolves = kMaxSpeculativeParallelResolves;
+    int max_queueing_delay_ms = kMaxSpeculativeResolveQueueDelayMs;
 
-    if (trial_->group() == max_250ms_prefetch)
-      max_queueing_delay_ms = 250;
-    else if (trial_->group() == max_500ms_prefetch)
-      max_queueing_delay_ms = 500;
-    else if (trial_->group() == max_750ms_prefetch)
-      max_queueing_delay_ms = 750;
-    else if (trial_->group() == max_2s_prefetch)
-      max_queueing_delay_ms = 2000;
     if (trial_->group() == max_2_concurrent_prefetch)
-      max_concurrent = 2;
+      max_parallel_resolves = 2;
     else if (trial_->group() == max_4_concurrent_prefetch)
-      max_concurrent = 4;
+      max_parallel_resolves = 4;
     else if (trial_->group() == max_6_concurrent_prefetch)
-      max_concurrent = 6;
-    // Scale acceptable delay so we don't cause congestion limits to fire as
-    // we modulate max_concurrent (*if* we are modulating it at all).
-    max_queueing_delay_ms = (kMaxPrefetchQueueingDelayMs *
-                             kMaxPrefetchConcurrentLookups) / max_concurrent;
+      max_parallel_resolves = 6;
+
+    if (trial_->group() == max_250ms_prefetch) {
+      max_queueing_delay_ms =
+         (250 * kTypicalSpeculativeGroupSize) /  max_parallel_resolves;
+    } else if (trial_->group() == max_500ms_prefetch) {
+      max_queueing_delay_ms =
+          (500 * kTypicalSpeculativeGroupSize) /  max_parallel_resolves;
+    } else if (trial_->group() == max_750ms_prefetch) {
+      max_queueing_delay_ms =
+          (750 * kTypicalSpeculativeGroupSize) /  max_parallel_resolves;
+    } else if (trial_->group() == max_2s_prefetch) {
+      max_queueing_delay_ms =
+          (2000 * kTypicalSpeculativeGroupSize) /  max_parallel_resolves;
+    }
 
     TimeDelta max_queueing_delay(
         TimeDelta::FromMilliseconds(max_queueing_delay_ms));
 
     DCHECK(!g_predictor);
-    InitNetworkPredictor(max_queueing_delay, max_concurrent, user_prefs,
+    InitNetworkPredictor(max_queueing_delay, max_parallel_resolves, user_prefs,
                          local_state, preconnect_enabled);
   }
 }
diff --git a/chrome/browser/net/predictor_api.h b/chrome/browser/net/predictor_api.h
index 3e04e11..a03f7c91 100644
--- a/chrome/browser/net/predictor_api.h
+++ b/chrome/browser/net/predictor_api.h
@@ -81,15 +81,14 @@
 // Helper class to handle global init and shutdown.
 class PredictorInit {
  public:
-  // Too many concurrent lookups negate benefits of prefetching by trashing
-  // the OS cache before all resource loading is complete.
-  // This is the default.
-  static const size_t kMaxPrefetchConcurrentLookups;
+  // Too many concurrent lookups performed in parallel may overload a resolver,
+  // or may cause problems for a local router.  The following limits that count.
+  static const size_t kMaxSpeculativeParallelResolves;
 
   // When prefetch requests are queued beyond some period of time, then the
   // system is congested, and we need to clear all queued requests to get out
   // of that state.  The following is the suggested default time limit.
-  static const int kMaxPrefetchQueueingDelayMs;
+  static const int kMaxSpeculativeResolveQueueDelayMs;
 
   PredictorInit(PrefService* user_prefs, PrefService* local_state,
                 bool preconnect_enabled);
diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc
index a0a0113..38f0281 100644
--- a/chrome/browser/net/predictor_unittest.cc
+++ b/chrome/browser/net/predictor_unittest.cc
@@ -65,7 +65,7 @@
       : io_thread_(BrowserThread::IO, &loop_),
         host_resolver_(new net::MockCachingHostResolver()),
         default_max_queueing_delay_(TimeDelta::FromMilliseconds(
-            PredictorInit::kMaxPrefetchQueueingDelayMs)) {
+            PredictorInit::kMaxSpeculativeResolveQueueDelayMs)) {
   }
 
  protected:
@@ -112,7 +112,7 @@
   scoped_refptr<Predictor> testing_master =
       new Predictor(host_resolver_.get(),
                     default_max_queueing_delay_,
-                    PredictorInit::kMaxPrefetchConcurrentLookups,
+                    PredictorInit::kMaxSpeculativeParallelResolves,
                     false);
   testing_master->Shutdown();
 }
@@ -126,7 +126,7 @@
   scoped_refptr<Predictor> testing_master =
       new Predictor(host_resolver_.get(),
                     default_max_queueing_delay_,
-                    PredictorInit::kMaxPrefetchConcurrentLookups,
+                    PredictorInit::kMaxSpeculativeParallelResolves,
                     false);
 
   GURL localhost("http://localhost:80");
@@ -152,7 +152,7 @@
   scoped_refptr<Predictor> testing_master =
       new Predictor(host_resolver_.get(),
                     default_max_queueing_delay_,
-                    PredictorInit::kMaxPrefetchConcurrentLookups,
+                    PredictorInit::kMaxSpeculativeParallelResolves,
                     false);
 
   GURL goog("http://www.google.com:80");
@@ -184,7 +184,7 @@
   scoped_refptr<Predictor> testing_master =
       new Predictor(host_resolver_.get(),
                     default_max_queueing_delay_,
-                    PredictorInit::kMaxPrefetchConcurrentLookups,
+                    PredictorInit::kMaxSpeculativeParallelResolves,
                     false);
 
   GURL goog("http://www.google.com:80"),
@@ -221,7 +221,6 @@
   EXPECT_FALSE(testing_master->WasFound(bad1));
   EXPECT_FALSE(testing_master->WasFound(bad2));
 
-  EXPECT_GT(testing_master->peak_pending_lookups(), names.size() / 2);
   EXPECT_LE(testing_master->peak_pending_lookups(), names.size());
   EXPECT_LE(testing_master->peak_pending_lookups(),
             testing_master->max_concurrent_dns_lookups());
@@ -235,7 +234,7 @@
   scoped_refptr<Predictor> testing_master =
       new Predictor(host_resolver_.get(),
                     default_max_queueing_delay_,
-                    PredictorInit::kMaxPrefetchConcurrentLookups,
+                    PredictorInit::kMaxSpeculativeParallelResolves,
                     false);
 
   UrlList names;
@@ -355,7 +354,7 @@
   scoped_refptr<Predictor> predictor =
       new Predictor(host_resolver_.get(),
                     default_max_queueing_delay_,
-                    PredictorInit::kMaxPrefetchConcurrentLookups,
+                    PredictorInit::kMaxSpeculativeParallelResolves,
                     false);
   scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
   predictor->SerializeReferrers(referral_list.get());
@@ -374,7 +373,7 @@
   scoped_refptr<Predictor> predictor =
       new Predictor(host_resolver_.get(),
                     default_max_queueing_delay_,
-                    PredictorInit::kMaxPrefetchConcurrentLookups,
+                    PredictorInit::kMaxSpeculativeParallelResolves,
                     false);
   const GURL motivation_url("http://www.google.com:91");
   const GURL subresource_url("http://icons.google.com:90");
@@ -402,7 +401,7 @@
   scoped_refptr<Predictor> predictor =
       new Predictor(host_resolver_.get(),
                     default_max_queueing_delay_,
-                    PredictorInit::kMaxPrefetchConcurrentLookups,
+                    PredictorInit::kMaxSpeculativeParallelResolves,
                     false);
   GURL motivation_url("http://www.google.com:110");