Browse Source

[2811] Add a DomainTreeTest.checkDistance testcase

This is a test that verifies that the tree is balanced, and doesn't go
over the red-black theoretical limit. It constructs a random large
million+ name zone and checks that the distance from every node to its
subtree root is within limit. This is a check that was not implemented
and we should have this as performance correctness proof, as the
DomainTree is so central to our memory datasrc performance.
Mukund Sivaraman 12 years ago
parent
commit
363e37a380

+ 15 - 0
src/lib/datasrc/memory/domaintree.h

@@ -435,6 +435,21 @@ public:
     /// This method never throws an exception.
     const DomainTreeNode<T>* predecessor() const;
 
+    /// \brief returns the node distance from the root of its subtree
+    size_t getDistance() const {
+        size_t nodes = 1;
+        for (const DomainTreeNode<T>* node = this;
+             node != NULL;
+             node = node->getParent()) {
+            if (node->isSubTreeRoot()) {
+                break;
+            }
+            ++nodes;
+        }
+
+        return (nodes);
+    }
+
 private:
     /// \brief private shared implementation of successor and predecessor
     ///

+ 75 - 1
src/lib/datasrc/tests/memory/domaintree_unittest.cc

@@ -17,6 +17,7 @@
 #include <exceptions/exceptions.h>
 
 #include <util/memory_segment_local.h>
+#include <util/random/random_number_generator.h>
 
 #include <dns/name.h>
 #include <dns/rrclass.h>
@@ -94,6 +95,10 @@ protected:
         const char* const domain_names[] = {
             "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
             "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
+        const int node_distances[] = {
+            3, 1, 2, 2, 2, 3, 1, 2, 1, 1, 2, 2
+        };
+
         int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
         for (int i = 0; i < name_count; ++i) {
             dtree.insert(mem_sgmt_, Name(domain_names[i]), &dtnode);
@@ -103,7 +108,8 @@ protected:
 
             dtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
                                             &dtnode);
-            EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
+            EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(
+                          new int(node_distances[i])));
         }
     }
 
@@ -126,6 +132,74 @@ TEST_F(DomainTreeTest, nodeCount) {
     EXPECT_EQ(0, dtree.getNodeCount());
 }
 
+TEST_F(DomainTreeTest, getDistance) {
+    TestDomainTreeNodeChain node_path;
+    const TestDomainTreeNode* node = NULL;
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name("a"),
+                                           &node,
+                                           node_path));
+    while (node != NULL) {
+        const int* distance = node->getData();
+        if (distance != NULL) {
+            EXPECT_EQ(*distance, node->getDistance());
+        }
+        node = dtree_expose_empty_node.nextNode(node_path);
+    }
+}
+
+TEST_F(DomainTreeTest, checkDistance) {
+    // This test checks an important performance-related property of the
+    // DomainTree (a red-black tree), which is important for us: the
+    // longest path from a sub-tree's root to a node is no more than
+    // 2log(n). This tests that the tree is balanced.
+
+    TreeHolder mytree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+    TestDomainTree& mytree = *mytree_holder.get();
+    isc::util::random::UniformRandomIntegerGenerator gen('a', 'z');
+    const size_t log_num_nodes = 20;
+
+    // Make a large million+ node top-level domain tree, i.e., the
+    // following code inserts names such as:
+    //
+    //   savoucnsrkrqzpkqypbygwoiliawpbmz.
+    //   wkadamcbbpjtundbxcmuayuycposvngx.
+    //   wzbpznemtooxdpjecdxynsfztvnuyfao.
+    //   yueojmhyffslpvfmgyfwioxegfhepnqq.
+    //
+    for (int i = 0; i < (1 << log_num_nodes); i++) {
+        string namestr;
+        while (true) {
+            for (int j = 0; j < 32; j++) {
+                namestr += gen();
+            }
+            namestr += '.';
+
+            if (mytree.insert(mem_sgmt_, Name(namestr), &dtnode) ==
+                TestDomainTree::SUCCESS) {
+                break;
+            }
+
+            namestr.clear();
+        }
+
+        EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
+    }
+
+    TestDomainTreeNodeChain node_path;
+    const TestDomainTreeNode* node = NULL;
+
+    // Try to find a node left of the left-most node, and start from its
+    // next node (which is the left-most node in its subtree).
+    EXPECT_EQ(TestDomainTree::NOTFOUND,
+              mytree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
+    while ((node = mytree.nextNode(node_path)) != NULL) {
+        // The distance from each node to its sub-tree root must be less
+        // than 2 * log(n).
+        EXPECT_GE(2 * log_num_nodes, node->getDistance());
+    }
+}
+
 TEST_F(DomainTreeTest, setGetData) {
     // set new data to an existing node.  It should have some data.
     int* newdata = new int(11);