From 2906484345007c734c3ff75a6080fbab2d8f1a05 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Tue, 28 Jun 2011 00:18:53 +0900
Subject: [PATCH] Use ServerStat to find faster server.

---
 src/AbstractCommand.cc     | 49 ++++++++++++++++++++++++++---------
 src/AbstractCommand.h      |  3 ++-
 src/FeedbackURISelector.cc | 12 ---------
 src/FileEntry.cc           | 53 ++++++++++++++++++++++++++++++++++++++
 src/FileEntry.h            |  8 ++++++
 src/RequestGroupMan.h      |  5 ++++
 src/ServerStat.h           | 11 ++++++++
 7 files changed, 116 insertions(+), 25 deletions(-)

diff --git a/src/AbstractCommand.cc b/src/AbstractCommand.cc
index b7f246b5..d85d7ac1 100644
--- a/src/AbstractCommand.cc
+++ b/src/AbstractCommand.cc
@@ -92,7 +92,8 @@ AbstractCommand::AbstractCommand
     socketRecvBuffer_(socketRecvBuffer),
     checkSocketIsReadable_(false), checkSocketIsWritable_(false),
     nameResolverCheck_(false),
-    incNumConnection_(incNumConnection)
+    incNumConnection_(incNumConnection),
+    serverStatTimer_(global::wallclock)
 {
   if(socket_ && socket_->isOpen()) {
     setReadCheckSocket(socket_);
@@ -117,6 +118,22 @@ AbstractCommand::~AbstractCommand() {
   }
 }
 
+void AbstractCommand::useFasterRequest
+(const SharedHandle<Request>& fasterRequest)
+{
+  A2_LOG_INFO(fmt("CUID#%lld - Use faster Request hostname=%s, port=%u",
+                  getCuid(),
+                  fasterRequest->getHost().c_str(),
+                  fasterRequest->getPort()));
+  // Cancel current Request object and use faster one.
+  fileEntry_->removeRequest(req_);
+  Command* command =
+    InitiateConnectionCommandFactory::createInitiateConnectionCommand
+    (getCuid(), fasterRequest, fileEntry_, requestGroup_, e_);
+  e_->setNoWait(true);
+  e_->addCommand(command);
+}
+
 bool AbstractCommand::execute() {
   A2_LOG_DEBUG(fmt("CUID#%lld - socket: read:%d, write:%d, hup:%d, err:%d",
                    getCuid(),
@@ -158,17 +175,25 @@ bool AbstractCommand::execute() {
          !getPieceStorage()->hasMissingUnusedPiece()) {
         SharedHandle<Request> fasterRequest = fileEntry_->findFasterRequest(req_);
         if(fasterRequest) {
-          A2_LOG_INFO(fmt("CUID#%lld - Use faster Request hostname=%s, port=%u",
-                          getCuid(),
-                          fasterRequest->getHost().c_str(),
-                          fasterRequest->getPort()));
-          // Cancel current Request object and use faster one.
-          fileEntry_->removeRequest(req_);
-          Command* command =
-            InitiateConnectionCommandFactory::createInitiateConnectionCommand
-            (getCuid(), fasterRequest, fileEntry_, requestGroup_, e_);
-          e_->setNoWait(true);
-          e_->addCommand(command);
+          useFasterRequest(fasterRequest);
+          return true;
+        }
+      }
+      // Don't use this feature if PREF_MAX_{OVERALL_}DOWNLOAD_LIMIT
+      // is used
+      if(e_->getRequestGroupMan()->getMaxOverallDownloadSpeedLimit() == 0 &&
+         requestGroup_->getMaxDownloadSpeedLimit() == 0 &&
+         serverStatTimer_.difference(global::wallclock) >= 10) {
+        serverStatTimer_ = global::wallclock;
+        std::vector<std::pair<size_t, std::string> > usedHosts;
+        if(getOption()->getAsBool(PREF_SELECT_LEAST_USED_HOST)) {
+          getDownloadEngine()->getRequestGroupMan()->getUsedHosts(usedHosts);
+        }
+        SharedHandle<Request> fasterRequest =
+          fileEntry_->findFasterRequest
+          (req_, usedHosts, e_->getRequestGroupMan()->getServerStatMan());
+        if(fasterRequest) {
+          useFasterRequest(fasterRequest);
           return true;
         }
       }
diff --git a/src/AbstractCommand.h b/src/AbstractCommand.h
index d612fada..8d9fbf08 100644
--- a/src/AbstractCommand.h
+++ b/src/AbstractCommand.h
@@ -85,9 +85,10 @@ private:
   bool nameResolverCheck_;
 
   bool incNumConnection_;
+  Timer serverStatTimer_;
 
   size_t calculateMinSplitSize() const;
-
+  void useFasterRequest(const SharedHandle<Request>& fasterRequest);
 #ifdef ENABLE_ASYNC_DNS
   void setNameResolverCheck(const SharedHandle<AsyncNameResolver>& resolver);
 
diff --git a/src/FeedbackURISelector.cc b/src/FeedbackURISelector.cc
index cb9ad199..e93af40c 100644
--- a/src/FeedbackURISelector.cc
+++ b/src/FeedbackURISelector.cc
@@ -56,18 +56,6 @@ FeedbackURISelector::FeedbackURISelector
 
 FeedbackURISelector::~FeedbackURISelector() {}
 
-namespace {
-class ServerStatFaster {
-public:
-  bool operator()(const std::pair<SharedHandle<ServerStat>, std::string> lhs,
-                  const std::pair<SharedHandle<ServerStat>, std::string> rhs)
-    const
-  {
-    return lhs.first->getDownloadSpeed() > rhs.first->getDownloadSpeed();
-  }
-};
-} // namespace
-
 std::string FeedbackURISelector::select
 (FileEntry* fileEntry,
  const std::vector<std::pair<size_t, std::string> >& usedHosts)
diff --git a/src/FileEntry.cc b/src/FileEntry.cc
index 262ae890..f69466b8 100644
--- a/src/FileEntry.cc
+++ b/src/FileEntry.cc
@@ -46,6 +46,8 @@
 #include "uri.h"
 #include "PeerStat.h"
 #include "fmt.h"
+#include "ServerStatMan.h"
+#include "ServerStat.h"
 
 namespace aria2 {
 
@@ -221,6 +223,57 @@ FileEntry::findFasterRequest(const SharedHandle<Request>& base)
   return SharedHandle<Request>();
 }
 
+SharedHandle<Request>
+FileEntry::findFasterRequest
+(const SharedHandle<Request>& base,
+ const std::vector<std::pair<size_t, std::string> >& usedHosts,
+ const SharedHandle<ServerStatMan>& serverStatMan)
+{
+  const int startupIdleTime = 10;
+  const unsigned int SPEED_THRESHOLD = 20*1024;
+  if(lastFasterReplace_.difference(global::wallclock) < startupIdleTime) {
+    return SharedHandle<Request>();
+  }
+  const SharedHandle<PeerStat>& basestat = base->getPeerStat();
+  A2_LOG_DEBUG("Search faster server using ServerStat.");
+  // Use first 10 good URIs to introduce some randomness.
+  const size_t NUM_URI = 10;
+  std::vector<std::pair<SharedHandle<ServerStat>, std::string> > fastCands;
+  std::vector<std::string> normCands;
+  for(std::deque<std::string>::const_iterator i = uris_.begin(),
+        eoi = uris_.end(); i != eoi && fastCands.size() < NUM_URI; ++i) {
+    uri::UriStruct us;
+    if(!uri::parse(us, *i)) {
+      continue;
+    }
+    if(findSecond(usedHosts.begin(), usedHosts.end(), us.host) !=
+       usedHosts.end()) {
+      A2_LOG_DEBUG(fmt("%s is in usedHosts, not considered", (*i).c_str()));
+      continue;
+    }
+    SharedHandle<ServerStat> ss = serverStatMan->find(us.host, us.protocol);
+    if(ss && ss->isOK()) {
+      if((basestat &&
+          ss->getDownloadSpeed() > basestat->calculateDownloadSpeed()*1.5) ||
+         (!basestat && ss->getDownloadSpeed() > SPEED_THRESHOLD)) {
+        fastCands.push_back(std::make_pair(ss, *i));
+      }
+    }
+  }
+  if(!fastCands.empty()) {
+    A2_LOG_DEBUG("Selected from fastCands");
+    std::sort(fastCands.begin(), fastCands.end(), ServerStatFaster());
+    SharedHandle<Request> fastestRequest(new Request());
+    fastestRequest->setUri(fastCands.front().second);
+    fastestRequest->setReferer(base->getReferer());
+    inFlightRequests_.push_back(fastestRequest);
+    lastFasterReplace_ = global::wallclock;
+    return fastestRequest;
+  }
+  A2_LOG_DEBUG("No faster server found.");
+  return SharedHandle<Request>();
+}
+
 namespace {
 class RequestFaster {
 public:
diff --git a/src/FileEntry.h b/src/FileEntry.h
index 0a40ef49..1a321683 100644
--- a/src/FileEntry.h
+++ b/src/FileEntry.h
@@ -53,6 +53,7 @@
 namespace aria2 {
 
 class URISelector;
+class ServerStatMan;
 
 class FileEntry {
 private:
@@ -169,6 +170,13 @@ public:
   // removed from the pool and returned.
   SharedHandle<Request> findFasterRequest(const SharedHandle<Request>& base);
 
+  // Finds faster server using ServerStatMan.
+  SharedHandle<Request>
+  findFasterRequest
+  (const SharedHandle<Request>& base,
+   const std::vector<std::pair<size_t, std::string> >& usedHosts,
+   const SharedHandle<ServerStatMan>& serverStatMan);
+
   void poolRequest(const SharedHandle<Request>& request);
 
   bool removeRequest(const SharedHandle<Request>& request);
diff --git a/src/RequestGroupMan.h b/src/RequestGroupMan.h
index f4107d74..ab3da333 100644
--- a/src/RequestGroupMan.h
+++ b/src/RequestGroupMan.h
@@ -294,6 +294,11 @@ public:
 
   // Returns currently used hosts and its use count.
   void getUsedHosts(std::vector<std::pair<size_t, std::string> >& usedHosts);
+
+  const SharedHandle<ServerStatMan>& getServerStatMan() const
+  {
+    return serverStatMan_;
+  }
 };
 
 typedef SharedHandle<RequestGroupMan> RequestGroupManHandle;
diff --git a/src/ServerStat.h b/src/ServerStat.h
index f572997a..361d5e9e 100644
--- a/src/ServerStat.h
+++ b/src/ServerStat.h
@@ -39,6 +39,7 @@
 #include <string>
 #include <iosfwd>
 
+#include "SharedHandle.h"
 #include "TimeA2.h"
 
 namespace aria2 {
@@ -167,6 +168,16 @@ private:
 
 std::ostream& operator<<(std::ostream& o, const ServerStat& serverStat);
 
+class ServerStatFaster {
+public:
+  bool operator()
+  (const std::pair<SharedHandle<ServerStat>, std::string> lhs,
+   const std::pair<SharedHandle<ServerStat>, std::string> rhs) const
+  {
+    return lhs.first->getDownloadSpeed() > rhs.first->getDownloadSpeed();
+  }
+};
+
 } // namespace aria2
 
 #endif // D_SERVER_STAT_H