]> git.saurik.com Git - apt.git/commitdiff
Add basic (non weight adjusted) shuffling for SrvRecords selection
authorMichael Vogt <mvo@debian.org>
Thu, 20 Aug 2015 08:40:45 +0000 (10:40 +0200)
committerMichael Vogt <mvo@debian.org>
Thu, 20 Aug 2015 09:41:51 +0000 (11:41 +0200)
Also add "Debug::Acquire::SrvRecs" debug option and the option
"Acquire::EnableSrvRecods" to allow disabling this lookup.

apt-pkg/contrib/srvrec.cc
apt-pkg/contrib/srvrec.h
methods/connect.cc
test/libapt/srvrecs_test.cc [new file with mode: 0644]

index 85241c1d791d600ee4b5cf77c4bc826cef433bc8..b4a3d97d25d9abe0a04781967a5807f4a02ea290 100644 (file)
 #include <netinet/in.h>
 #include <arpa/nameser.h>
 #include <resolv.h>
+#include <chrono>
 
 #include <algorithm>
 
-#include <apt-pkg/strutl.h>
+#include <apt-pkg/configuration.h>
 #include <apt-pkg/error.h>
+#include <apt-pkg/strutl.h>
+
+
 #include "srvrec.h"
 
+
 bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result)
 {
    std::string target;
@@ -112,6 +117,29 @@ bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result)
    // sort them by priority
    std::stable_sort(Result.begin(), Result.end());
 
+   for(std::vector<SrvRec>::iterator I = Result.begin();
+      I != Result.end(); ++I)
+   {
+      if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+      {
+         std::cerr << "SrvRecs: got " << I->target
+                   << " prio: " << I->priority
+                   << " weight: " << I->weight
+                   << std::endl;
+      }
+   }
+
+   return true;
+}
+
+SrvRec PopFromSrvRecs(std::vector<SrvRec> &Recs)
+{
+   // FIXME: instead of the simplistic shuffle below use the algorithm
+   //        described in rfc2782 (with weights)
+   //        and figure out how the weights need to be adjusted if
+   //        a host refuses connections
+
+#if 0  // all code below is only needed for the weight adjusted selection 
    // assign random number ranges
    int prev_weight = 0;
    int prev_priority = 0;
@@ -124,6 +152,12 @@ bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result)
       I->random_number_range_end = prev_weight + I->weight;
       prev_weight = I->random_number_range_end;
       prev_priority = I->priority;
+
+      if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+         std::cerr << "SrvRecs: got " << I->target
+                   << " prio: " << I->priority
+                   << " weight: " << I->weight
+                   << std::endl;
    }
 
    // go over the code in reverse order and note the max random range
@@ -136,8 +170,27 @@ bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result)
          max = I->random_number_range_end;
       I->random_number_range_max = max;
    }
+#endif
 
-   // FIXME: now shuffle 
+   // shuffle in a very simplistic way for now (equal weights)
+   std::vector<SrvRec>::iterator I, J;
+   I = J = Recs.begin();
+   for(;I != Recs.end(); ++I)
+   {
+      if(I->priority != J->priority)
+         break;
+   }
 
-   return true;
+   // FIXME: meeeeh, where to init this properly
+   unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
+   std::shuffle(J, I, std::default_random_engine(seed));
+
+   // meh, no pop_front() in std::vector?
+   SrvRec selected = *Recs.begin();
+   Recs.erase(Recs.begin());
+
+   if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+      std::cerr << "PopFromSrvRecs: selecting " << selected.target << std::endl;
+
+   return selected;
 }
index 9323623a50a102d9f7c035952a0fb884cdea1381..e07edc68322377fde5389d25c96021eb15105773 100644 (file)
@@ -39,4 +39,9 @@ bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result);
  */
 bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result);
 
+/** \brief Pop a single SRV record from the vector of SrvRec taking
+ *         priority and weight into account
+ */
+SrvRec PopFromSrvRecs(std::vector<SrvRec> &Recs);
+
 #endif
index 9e0f01ee3478926426f969df1e44df54d74bfc62..5612af6ec7f018ce61cde95824bf0c569c809598 100644 (file)
@@ -272,7 +272,8 @@ bool Connect(std::string Host,int Port,const char *Service,
    if(LastHost != Host || LastPort != Port)
    {
       SrvRecords.clear();
-      bool res = GetSrvRecords(Host, DefPort, SrvRecords);
+      if (_config->FindB("Acquire::EnableSrvRecods", true) == true)
+         GetSrvRecords(Host, DefPort, SrvRecords);
    }
    // we have no SrvRecords for this host, connect right away
    if(SrvRecords.size() == 0)
@@ -282,7 +283,7 @@ bool Connect(std::string Host,int Port,const char *Service,
    // try to connect in the priority order of the srv records
    while(SrvRecords.size() > 0)
    {
-      Host = SrvRecords[0].target;
+      Host = PopFromSrvRecs(SrvRecords).target;
       if(ConnectToHostname(Host, Port, Service, DefPort, Fd, TimeOut, Owner))
          return true;
 
diff --git a/test/libapt/srvrecs_test.cc b/test/libapt/srvrecs_test.cc
new file mode 100644 (file)
index 0000000..7e43cc7
--- /dev/null
@@ -0,0 +1,33 @@
+#include <config.h>
+
+#include <apt-pkg/srvrec.h>
+
+#include <string>
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+TEST(SrvRecTest, PopFromSrvRecs)
+{
+   // the PopFromSrvRecs() is using a random number so we must
+   // run it a bunch of times to ensure we are not fooled by randomness
+   std::set<std::string> selected;
+   for(int i=0;i<100;i++)
+   {
+      std::vector<SrvRec> Meep;
+      SrvRec foo = {target:"foo", priority: 20, weight: 0, port: 80};
+      Meep.push_back(foo);
+      
+      SrvRec bar = {target:"bar", priority: 20, weight: 0, port: 80};
+      Meep.push_back(bar);
+
+      EXPECT_EQ(Meep.size(), 2);
+      SrvRec result = PopFromSrvRecs(Meep);
+      selected.insert(result.target);
+      // ensure that pop removed one element
+      EXPECT_EQ(Meep.size(), 1);
+   }
+
+   // ensure that after enough runs we end up with both selected
+   EXPECT_EQ(selected.size(), 2);
+}