Browse Source

[2204] simplify configureDataSource by always creating a new lists and swap.

so we don't have to worry about what are in the current lists or rollback
operations.
swapDataSrcClientLists() is newly introduced for AuthSrv.  No direc tests
yet (technically bad in terms TDD but the definition is very simple), which
will be provided in the next step.
the lock is now moved inside swapDataSrcClientLists().

note: even though this version builds everything, the amount of work
should be mostly the same because the only save is to create the empty
ClientList when the new and old have the same class of client.  The expensive
part is ClientList::configure().  This version doesn't need any more call
to configure() than the old version.
JINMEI Tatuya 12 years ago
parent
commit
919fe74ccb

+ 11 - 2
src/bin/auth/auth_srv.cc

@@ -269,8 +269,7 @@ public:
     const shared_ptr<TSIGKeyRing>* keyring_;
 
     /// The data source client list
-    shared_ptr<std::map<RRClass, shared_ptr<ConfigurableClientList> > >
-        datasrc_client_lists_;
+    AuthSrv::DataSrcClientListsPtr datasrc_client_lists_;
 
     shared_ptr<ConfigurableClientList> getClientList(const RRClass& rrclass) {
         // TODO: Debug-build only check
@@ -948,6 +947,16 @@ AuthSrv::setClientList(const RRClass& rrclass,
     }
 }
 
+AuthSrv::DataSrcClientListsPtr
+AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) {
+    {
+        thread::Mutex::Locker locker(impl_->mutex_);
+        std::swap(new_lists, impl_->datasrc_client_lists_);
+    }
+
+    return (new_lists);
+}
+
 shared_ptr<ConfigurableClientList>
 AuthSrv::getClientList(const RRClass& rrclass) {
     return (impl_->getClientList(rrclass));

+ 16 - 2
src/bin/auth/auth_srv.h

@@ -15,10 +15,11 @@
 #ifndef __AUTH_SRV_H
 #define __AUTH_SRV_H 1
 
-#include <string>
-
 #include <config/ccsession.h>
+
 #include <datasrc/factory.h>
+#include <datasrc/client_list.h>
+
 #include <dns/message.h>
 #include <dns/opcode.h>
 #include <util/buffer.h>
@@ -35,6 +36,11 @@
 #include <server_common/portconfig.h>
 #include <auth/statistics.h>
 
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+#include <string>
+
 namespace isc {
 namespace util {
 namespace io {
@@ -309,6 +315,14 @@ public:
                        boost::shared_ptr<isc::datasrc::ConfigurableClientList>&
                        list);
 
+    typedef boost::shared_ptr<std::map<
+        isc::dns::RRClass, boost::shared_ptr<
+                               isc::datasrc::ConfigurableClientList> > >
+    DataSrcClientListsPtr;
+
+    DataSrcClientListsPtr swapDataSrcClientLists(DataSrcClientListsPtr
+                                                 new_lists);
+
     /// \brief Returns the currently used client list for the class.
     ///
     /// \param rrclass The class for which to get the list.

+ 17 - 59
src/bin/auth/datasrc_config.h

@@ -19,10 +19,10 @@
 
 #include <cc/data.h>
 #include <datasrc/client_list.h>
-#include <util/threads/lock.h>
 
 #include <boost/shared_ptr.hpp>
 
+#include <utility>
 #include <set>
 
 /// \brief Configure the authoritative server's data source lists
@@ -45,67 +45,25 @@ configureDataSourceGeneric(Server& server,
 {
     typedef boost::shared_ptr<List> ListPtr;
     typedef std::map<std::string, isc::data::ConstElementPtr> Map;
-    typedef std::pair<isc::dns::RRClass, ListPtr> RollbackPair;
-    typedef std::pair<isc::dns::RRClass, isc::data::ConstElementPtr>
-        RollbackConfiguration;
+    typedef std::map<isc::dns::RRClass, ListPtr> ListMap;
 
-    // Lock the client lists, we're going to manipulate them.
-    isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
+    boost::shared_ptr<ListMap> new_lists(new ListMap);
 
-    // Some structures to be able to perform a rollback
-    std::vector<RollbackPair> rollback_sets;
-    std::vector<RollbackConfiguration> rollback_configurations;
-    try {
-        // Get the configuration and current state.
-        const Map& map(config->mapValue());
-        const std::vector<isc::dns::RRClass>
-            activeVector(server.getClientListClasses());
-        std::set<isc::dns::RRClass> active(activeVector.begin(),
-                                           activeVector.end());
-        // Go through the configuration and change everything.
-        for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
-            const isc::dns::RRClass rrclass(it->first);
-            active.erase(rrclass);
-            ListPtr list(server.getClientList(rrclass));
-            bool need_set(false);
-            if (list) {
-                rollback_configurations.
-                    push_back(RollbackConfiguration(rrclass,
-                                                    list->getConfiguration()));
-            } else {
-                list.reset(new List(rrclass));
-                need_set = true;
-                rollback_sets.push_back(RollbackPair(rrclass, ListPtr()));
-            }
-            list->configure(it->second, true);
-            if (need_set) {
-                server.setClientList(rrclass, list);
-            }
-        }
-        // Remove the ones that are not in the configuration.
-        for (std::set<isc::dns::RRClass>::iterator it(active.begin());
-             it != active.end(); ++it) {
-            // There seems to be no way the setClientList could throw.
-            // But this is just to make sure in case it did to restore
-            // the original.
-            rollback_sets.push_back(
-                RollbackPair(*it, server.getClientList(*it)));
-            server.setClientList(*it, ListPtr());
-        }
-    } catch (...) {
-        // Perform a rollback of the changes. The old configuration should
-        // work.
-        for (typename std::vector<RollbackPair>::const_iterator
-                 it(rollback_sets.begin()); it != rollback_sets.end(); ++it) {
-            server.setClientList(it->first, it->second);
-        }
-        for (typename std::vector<RollbackConfiguration>::const_iterator
-                 it(rollback_configurations.begin());
-             it != rollback_configurations.end(); ++it) {
-            server.getClientList(it->first)->configure(it->second, true);
-        }
-        throw;
+    // Get the configuration and current state.
+    const Map& map(config->mapValue());
+
+    // Go through the configuration and create corresponding list.
+    for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
+        const isc::dns::RRClass rrclass(it->first);
+        ListPtr list(new List(rrclass));
+        list->configure(it->second, true);
+        new_lists->insert(std::pair<isc::dns::RRClass, ListPtr>(rrclass,
+                                                                list));
     }
+
+    // Replace the server's lists.  By ignoring the return value we let the
+    // old lists be destroyed.
+    server.swapDataSrcClientLists(new_lists);
 }
 
 /// \brief Concrete version of configureDataSource() for the

+ 2 - 0
src/bin/auth/tests/command_unittest.cc

@@ -16,6 +16,8 @@
 
 #include "datasrc_util.h"
 
+#include <util/threads/lock.h>
+
 #include <auth/auth_srv.h>
 #include <auth/auth_config.h>
 #include <auth/command.h>

+ 32 - 36
src/bin/auth/tests/datasrc_config_unittest.cc

@@ -81,27 +81,27 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&,
 
 class DatasrcConfigTest : public ::testing::Test {
 public:
-    // These pretend to be the server
-    ListPtr getClientList(const RRClass& rrclass) {
-        log_ += "get " + rrclass.toText() + "\n";
-        return (lists_[rrclass]);
-    }
-    void setClientList(const RRClass& rrclass, const ListPtr& list) {
-        log_ += "set " + rrclass.toText() + " " +
-            (list ? list->getConf() : "") + "\n";
-        lists_[rrclass] = list;
-    }
-    vector<RRClass> getClientListClasses() const {
-        vector<RRClass> result;
-        for (std::map<RRClass, ListPtr>::const_iterator it(lists_.begin());
-             it != lists_.end(); ++it) {
-            result.push_back(it->first);
+    // To pretend to be the server:
+    void swapDataSrcClientLists(shared_ptr<std::map<dns::RRClass, ListPtr> >
+                                new_lists)
+    {
+        lists_.clear();         // first empty it
+
+        // Record the operation and results.  Note that map elements are
+        // sorted by RRClass, so the ordering should be predictable.
+        for (std::map<dns::RRClass, ListPtr>::const_iterator it =
+                 new_lists->begin();
+             it != new_lists->end();
+             ++it)
+        {
+            const RRClass rrclass = it->first;
+            ListPtr list = it->second;
+            log_ += "set " + rrclass.toText() + " " +
+                (list ? list->getConf() : "") + "\n";
+            lists_[rrclass] = list;
         }
-        return (result);
-    }
-    isc::util::thread::Mutex& getClientListMutex() const {
-        return (mutex_);
     }
+
 protected:
     DatasrcConfigTest() :
         session(ElementPtr(new ListElement), ElementPtr(new ListElement),
@@ -147,9 +147,8 @@ protected:
         session.addMessage(createCommand("config_update", config),
                            "data_sources", "*");
         mccs->checkCommand();
-        // Check it called the correct things (check that there's no IN yet and
-        // set a new one.
-        EXPECT_EQ("get IN\nset IN xxx\n", log_);
+        // Check that the passed config is stored.
+        EXPECT_EQ("set IN xxx\n", log_);
         EXPECT_EQ(1, lists_.size());
     }
     FakeSession session;
@@ -166,8 +165,10 @@ TEST_F(DatasrcConfigTest, createList) {
 }
 
 TEST_F(DatasrcConfigTest, modifyList) {
-    // First, initialize the list
+    // First, initialize the list, and confirm the current config
     initializeINList();
+    EXPECT_EQ("xxx", lists_[RRClass::IN()]->getConf());
+
     // And now change the configuration of the list
     const ElementPtr
         config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}]}"));
@@ -175,9 +176,7 @@ TEST_F(DatasrcConfigTest, modifyList) {
                        "*");
     log_ = "";
     mccs->checkCommand();
-    // This one does not set
-    EXPECT_EQ("get IN\n", log_);
-    // But this should contain the yyy configuration
+    // Now the new one should be installed.
     EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
     EXPECT_EQ(1, lists_.size());
 }
@@ -191,7 +190,7 @@ TEST_F(DatasrcConfigTest, multiple) {
                        "*");
     mccs->checkCommand();
     // We have set commands for both classes.
-    EXPECT_EQ("get CH\nset CH xxx\nget IN\nset IN yyy\n", log_);
+    EXPECT_EQ("set IN yyy\nset CH xxx\n", log_);
     // We should have both there
     EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
     EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
@@ -212,9 +211,7 @@ TEST_F(DatasrcConfigTest, updateAdd) {
                        "*");
     log_ = "";
     mccs->checkCommand();
-    // The CH is set, IN not
-    EXPECT_EQ("get CH\nset CH xxx\nget IN\n", log_);
-    // But this should contain the yyy configuration
+    EXPECT_EQ("set IN yyy\nset CH xxx\n", log_);
     EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
     EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
     EXPECT_EQ(2, lists_.size());
@@ -229,12 +226,11 @@ TEST_F(DatasrcConfigTest, updateDelete) {
                        "*");
     log_ = "";
     mccs->checkCommand();
-    EXPECT_EQ("get IN\nset IN \n", log_);
-    EXPECT_FALSE(lists_[RRClass::IN()]);
-    // In real auth server, the NULL one would be removed. However, we just
-    // store it, so the IN bucket is still in there. This checks there's nothing
-    // else.
-    EXPECT_EQ(1, lists_.size());
+
+    // No operation takes place in the configuration, and the old one is
+    // just dropped
+    EXPECT_EQ("", log_);
+    EXPECT_TRUE(lists_.empty());
 }
 
 // Check that we can rollback an addition if something else fails