Throughput: Change the min number of requests in flight required

In network quality estimator (NQE), change the minimum number of
requests in flight required before a throughput observation can be
taken. The parameter is controlled using field trial params, with
default value set to 1.

BUG=725146

CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.android:android_cronet_tester

Review-Url: https://codereview.chromium.org/2893933004
Cr-Commit-Position: refs/heads/master@{#473755}
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 1728675..6b9be03 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -319,7 +319,7 @@
   AddDefaultEstimates();
 
   throughput_analyzer_.reset(new nqe::internal::ThroughputAnalyzer(
-      base::ThreadTaskRunnerHandle::Get(),
+      &params_, base::ThreadTaskRunnerHandle::Get(),
       base::Bind(&NetworkQualityEstimator::OnNewThroughputObservationAvailable,
                  base::Unretained(this)),
       use_localhost_requests_, use_smaller_responses_for_tests));
diff --git a/net/nqe/network_quality_estimator_params.cc b/net/nqe/network_quality_estimator_params.cc
index 7372ba5b..ab4dc9c2 100644
--- a/net/nqe/network_quality_estimator_params.cc
+++ b/net/nqe/network_quality_estimator_params.cc
@@ -368,6 +368,10 @@
 NetworkQualityEstimatorParams::NetworkQualityEstimatorParams(
     const std::map<std::string, std::string>& params)
     : params_(params),
+      throughput_min_requests_in_flight_(
+          GetValueForVariationParam(params_,
+                                    "throughput_min_requests_in_flight",
+                                    1)),
       weight_multiplier_per_second_(GetWeightMultiplierPerSecond(params_)),
       weight_multiplier_per_dbm_(
           GetDoubleValueForVariationParamWithDefaultValue(params_,
diff --git a/net/nqe/network_quality_estimator_params.h b/net/nqe/network_quality_estimator_params.h
index 5c279e1..f543a54 100644
--- a/net/nqe/network_quality_estimator_params.h
+++ b/net/nqe/network_quality_estimator_params.h
@@ -60,6 +60,13 @@
   // Returns the threshold for effective connection type |type|.
   const NetworkQuality& ConnectionThreshold(EffectiveConnectionType type) const;
 
+  // Returns the minimum number of requests in-flight to consider the network
+  // fully utilized. A throughput observation is taken only when the network is
+  // considered as fully utilized.
+  size_t throughput_min_requests_in_flight() const {
+    return throughput_min_requests_in_flight_;
+  }
+
   // Returns the weight multiplier per second, which represents the factor by
   // which the weight of an observation reduces every second.
   double weight_multiplier_per_second() const {
@@ -103,6 +110,7 @@
   // NetworkQualityEstimator field trial.
   const std::map<std::string, std::string> params_;
 
+  const size_t throughput_min_requests_in_flight_;
   const double weight_multiplier_per_second_;
   const double weight_multiplier_per_dbm_;
   const double correlation_uma_logging_probability_;
diff --git a/net/nqe/throughput_analyzer.cc b/net/nqe/throughput_analyzer.cc
index 6785d14..de1b0ef 100644
--- a/net/nqe/throughput_analyzer.cc
+++ b/net/nqe/throughput_analyzer.cc
@@ -10,6 +10,7 @@
 #include "base/single_thread_task_runner.h"
 #include "net/base/network_activity_monitor.h"
 #include "net/base/url_util.h"
+#include "net/nqe/network_quality_estimator_params.h"
 #include "net/url_request/url_request.h"
 
 #if defined(OS_ANDROID)
@@ -35,11 +36,13 @@
 namespace internal {
 
 ThroughputAnalyzer::ThroughputAnalyzer(
+    const NetworkQualityEstimatorParams* params,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     ThroughputObservationCallback throughput_observation_callback,
     bool use_local_host_requests_for_tests,
     bool use_smaller_responses_for_tests)
-    : task_runner_(task_runner),
+    : params_(params),
+      task_runner_(task_runner),
       throughput_observation_callback_(throughput_observation_callback),
       last_connection_change_(base::TimeTicks::Now()),
       window_start_time_(base::TimeTicks()),
@@ -47,6 +50,7 @@
       disable_throughput_measurements_(false),
       use_localhost_requests_for_tests_(use_local_host_requests_for_tests),
       use_small_responses_for_tests_(use_smaller_responses_for_tests) {
+  DCHECK(params_);
   DCHECK(task_runner_);
   DCHECK(!IsCurrentlyTrackingThroughput());
 }
@@ -66,7 +70,8 @@
   // started, and there is at least one active request that does not degrade
   // throughput computation accuracy.
   if (accuracy_degrading_requests_.size() > 0 ||
-      IsCurrentlyTrackingThroughput() || requests_.size() <= 0) {
+      IsCurrentlyTrackingThroughput() ||
+      requests_.size() < params_->throughput_min_requests_in_flight()) {
     return;
   }
   window_start_time_ = base::TimeTicks::Now();
@@ -96,6 +101,8 @@
   // requests should be currently active.
   DCHECK_EQ(0U, accuracy_degrading_requests_.size());
 
+  DCHECK_LE(params_->throughput_min_requests_in_flight(), requests_.size());
+
   return true;
 }
 
@@ -141,7 +148,7 @@
   }
 
   int32_t downstream_kbps;
-  if (MayBeGetThroughputObservation(&downstream_kbps)) {
+  if (MaybeGetThroughputObservation(&downstream_kbps)) {
     // Notify the provided callback.
     task_runner_->PostTask(
         FROM_HERE,
@@ -164,7 +171,7 @@
   if (requests_.erase(&request) == 1u) {
     // If there is no network activity, stop tracking throughput to prevent
     // recording of any observations.
-    if (requests_.size() == 0)
+    if (requests_.size() < params_->throughput_min_requests_in_flight())
       EndThroughputObservationWindow();
     return;
   }
@@ -172,7 +179,7 @@
   NOTREACHED();
 }
 
-bool ThroughputAnalyzer::MayBeGetThroughputObservation(
+bool ThroughputAnalyzer::MaybeGetThroughputObservation(
     int32_t* downstream_kbps) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(downstream_kbps);
diff --git a/net/nqe/throughput_analyzer.h b/net/nqe/throughput_analyzer.h
index e94102b..d83e9db 100644
--- a/net/nqe/throughput_analyzer.h
+++ b/net/nqe/throughput_analyzer.h
@@ -31,6 +31,8 @@
 
 namespace internal {
 
+class NetworkQualityEstimatorParams;
+
 // Makes throughput observations. Polls NetworkActivityMonitor
 // (TrafficStats on Android) to count number of bits received over throughput
 // observation windows in accordance with the following rules:
@@ -55,6 +57,7 @@
   // estimation.
   // Virtualized for testing.
   ThroughputAnalyzer(
+      const NetworkQualityEstimatorParams* params,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       ThroughputObservationCallback throughput_observation_callback,
       bool use_local_host_requests_for_tests,
@@ -106,7 +109,7 @@
   // tracking throughput. A throughput observation can be taken only if the
   // time-window is currently active, and enough bytes have accumulated in
   // that window. |downstream_kbps| should not be null.
-  bool MayBeGetThroughputObservation(int32_t* downstream_kbps);
+  bool MaybeGetThroughputObservation(int32_t* downstream_kbps);
 
   // Starts the throughput observation window that keeps track of network
   // bytes if the following conditions are true:
@@ -133,6 +136,7 @@
   // do not exceed their capacities.
   void BoundRequestsSize();
 
+  const NetworkQualityEstimatorParams* params_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   // Called every time a new throughput observation is available.
diff --git a/net/nqe/throughput_analyzer_unittest.cc b/net/nqe/throughput_analyzer_unittest.cc
index 3792700..4ed98ee 100644
--- a/net/nqe/throughput_analyzer_unittest.cc
+++ b/net/nqe/throughput_analyzer_unittest.cc
@@ -7,7 +7,9 @@
 #include <stdint.h>
 
 #include <deque>
+#include <map>
 #include <memory>
+#include <string>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -15,8 +17,10 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/url_util.h"
+#include "net/nqe/network_quality_estimator_params.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
@@ -30,8 +34,10 @@
 
 class TestThroughputAnalyzer : public internal::ThroughputAnalyzer {
  public:
-  TestThroughputAnalyzer()
+  explicit TestThroughputAnalyzer(
+      internal::NetworkQualityEstimatorParams* params)
       : internal::ThroughputAnalyzer(
+            params,
             base::ThreadTaskRunnerHandle::Get(),
             base::Bind(
                 &TestThroughputAnalyzer::OnNewThroughputObservationAvailable,
@@ -78,7 +84,9 @@
                }};
 
   for (const auto& test : tests) {
-    TestThroughputAnalyzer throughput_analyzer;
+    std::map<std::string, std::string> variation_params;
+    internal::NetworkQualityEstimatorParams params(variation_params);
+    TestThroughputAnalyzer throughput_analyzer(&params);
 
     TestDelegate test_delegate;
     TestURLRequestContext context;
@@ -128,7 +136,9 @@
 
   for (const auto& test : tests) {
     // Localhost requests are not allowed for estimation purposes.
-    TestThroughputAnalyzer throughput_analyzer;
+    std::map<std::string, std::string> variation_params;
+    internal::NetworkQualityEstimatorParams params(variation_params);
+    TestThroughputAnalyzer throughput_analyzer(&params);
 
     TestDelegate test_delegate;
     TestURLRequestContext context;
@@ -186,47 +196,71 @@
 // requests overlap.
 TEST(ThroughputAnalyzerTest, TestThroughputWithNetworkRequestsOverlap) {
   static const struct {
+    size_t throughput_min_requests_in_flight;
+    size_t number_requests_in_flight;
     int64_t increment_bits;
     bool expect_throughput_observation;
   } tests[] = {
       {
-          100 * 1000 * 8, true,
+          1, 2, 100 * 1000 * 8, true,
       },
       {
-          1, false,
+          3, 1, 100 * 1000 * 8, false,
+      },
+      {
+          3, 2, 100 * 1000 * 8, false,
+      },
+      {
+          3, 3, 100 * 1000 * 8, true,
+      },
+      {
+          3, 4, 100 * 1000 * 8, true,
+      },
+      {
+          1, 2, 1, false,
       },
   };
 
   for (const auto& test : tests) {
     // Localhost requests are not allowed for estimation purposes.
-    TestThroughputAnalyzer throughput_analyzer;
+    std::map<std::string, std::string> variation_params;
+    variation_params["throughput_min_requests_in_flight"] =
+        base::IntToString(test.throughput_min_requests_in_flight);
+    internal::NetworkQualityEstimatorParams params(variation_params);
+    TestThroughputAnalyzer throughput_analyzer(&params);
     TestDelegate test_delegate;
     TestURLRequestContext context;
 
     EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
 
-    std::unique_ptr<URLRequest> request_network_1 = context.CreateRequest(
-        GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
-        TRAFFIC_ANNOTATION_FOR_TESTS);
-    std::unique_ptr<URLRequest> request_network_2 = context.CreateRequest(
-        GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
-        TRAFFIC_ANNOTATION_FOR_TESTS);
-    request_network_1->Start();
-    request_network_2->Start();
+    std::vector<std::unique_ptr<URLRequest>> requests_in_flight;
+
+    for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
+      std::unique_ptr<URLRequest> request_network_1 = context.CreateRequest(
+          GURL("http://example.com/echo.html"), DEFAULT_PRIORITY,
+          &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+      requests_in_flight.push_back(std::move(request_network_1));
+      requests_in_flight.back()->Start();
+    }
 
     base::RunLoop().Run();
 
-    EXPECT_LE(0, throughput_analyzer.throughput_observations_received());
+    EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
 
-    throughput_analyzer.NotifyStartTransaction(*request_network_1);
-    throughput_analyzer.NotifyStartTransaction(*request_network_2);
+    for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
+      URLRequest* request = requests_in_flight.at(i).get();
+      throughput_analyzer.NotifyStartTransaction(*request);
+    }
 
     // Increment the bytes received count to emulate the bytes received for
     // |request_network_1| and |request_network_2|.
     throughput_analyzer.IncrementBitsReceived(test.increment_bits);
 
-    throughput_analyzer.NotifyRequestCompleted(*request_network_1);
-    throughput_analyzer.NotifyRequestCompleted(*request_network_2);
+    for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
+      URLRequest* request = requests_in_flight.at(i).get();
+      throughput_analyzer.NotifyRequestCompleted(*request);
+    }
+
     base::RunLoop().RunUntilIdle();
 
     // Only one observation should be taken since two requests overlap.
@@ -238,6 +272,73 @@
   }
 }
 
+// Tests if the throughput observation is taken correctly when the start and end
+// of network requests overlap, and the minimum number of in flight requests
+// when taking an observation is more than 1.
+TEST(ThroughputAnalyzerTest, TestThroughputWithMultipleNetworkRequests) {
+  std::map<std::string, std::string> variation_params;
+  variation_params["throughput_min_requests_in_flight"] = "3";
+  internal::NetworkQualityEstimatorParams params(variation_params);
+  TestThroughputAnalyzer throughput_analyzer(&params);
+  TestDelegate test_delegate;
+  TestURLRequestContext context;
+
+  EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
+
+  std::unique_ptr<URLRequest> request_1 = context.CreateRequest(
+      GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+  std::unique_ptr<URLRequest> request_2 = context.CreateRequest(
+      GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+  std::unique_ptr<URLRequest> request_3 = context.CreateRequest(
+      GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+  std::unique_ptr<URLRequest> request_4 = context.CreateRequest(
+      GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  request_1->Start();
+  request_2->Start();
+  request_3->Start();
+  request_4->Start();
+
+  base::RunLoop().Run();
+
+  EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
+
+  throughput_analyzer.NotifyStartTransaction(*(request_1.get()));
+  throughput_analyzer.NotifyStartTransaction(*(request_2.get()));
+
+  const size_t increment_bits = 100 * 1000 * 8;
+
+  // Increment the bytes received count to emulate the bytes received for
+  // |request_1| and |request_2|.
+  throughput_analyzer.IncrementBitsReceived(increment_bits);
+
+  throughput_analyzer.NotifyRequestCompleted(*(request_1.get()));
+  base::RunLoop().RunUntilIdle();
+  // No observation should be taken since only 1 request is in flight.
+  EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
+
+  throughput_analyzer.NotifyStartTransaction(*(request_3.get()));
+  throughput_analyzer.NotifyStartTransaction(*(request_4.get()));
+  EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
+
+  // 3 requests are in flight which is at least as many as the minimum number of
+  // in flight requests required. An observation should be taken.
+  throughput_analyzer.IncrementBitsReceived(increment_bits);
+
+  // Only one observation should be taken since two requests overlap.
+  throughput_analyzer.NotifyRequestCompleted(*(request_2.get()));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, throughput_analyzer.throughput_observations_received());
+  throughput_analyzer.NotifyRequestCompleted(*(request_3.get()));
+  throughput_analyzer.NotifyRequestCompleted(*(request_4.get()));
+  EXPECT_EQ(1, throughput_analyzer.throughput_observations_received());
+}
+
 }  // namespace
 
 }  // namespace nqe