Browse Source

[2905] added ZoneTable::addEmptyZone() to allow adding an empty zone.

updated addZone() and findZone() behavior so they can handle the concept of
empty zones well.
JINMEI Tatuya 12 years ago
parent
commit
1a488b6c5e

+ 6 - 0
src/lib/datasrc/memory/memory_messages.mes

@@ -92,6 +92,12 @@ tried).
 Debug information.  A specific type RRset is requested at a zone origin
 of an in-memory zone and it is found.
 
+% DATASRC_MEMORY_MEM_ADD_EMPTY_ZONE adding an empty zone '%1/%2'
+Debug information. An "empty" zone is being added into the in-memory
+data source.  This is conceptual data indicating the state where the
+zone exists but its content isn't available.  That would be the case,
+for example, a broken zone specified in the configuration.
+
 % DATASRC_MEMORY_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
 Debug information. An RRset is being added to the in-memory data source.
 

+ 31 - 6
src/lib/datasrc/memory/zone_table.cc

@@ -91,6 +91,26 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
     }
     SegmentObjectHolder<ZoneData, RRClass> holder(mem_sgmt, zone_class);
     holder.set(content);
+
+    const AddResult result =
+        addZoneInternal(mem_sgmt, zone_name, holder.get());
+    holder.release();
+    return (result);
+}
+
+ZoneTable::AddResult
+ZoneTable::addEmptyZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_EMPTY_ZONE).
+        arg(zone_name).arg(rrclass_);
+
+    return (addZoneInternal(mem_sgmt, zone_name, null_zone_data_.get()));
+}
+
+ZoneTable::AddResult
+ZoneTable::addZoneInternal(util::MemorySegment& mem_sgmt,
+                           const dns::Name& zone_name,
+                           ZoneData* content)
+{
     // Get the node where we put the zone
     ZoneTableNode* node(NULL);
     switch (zones_->insert(mem_sgmt, zone_name, &node)) {
@@ -105,10 +125,9 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
     // Can Not Happen
     assert(node != NULL);
 
-    // We can release now, setData never throws
-    ZoneData* old = node->setData(holder.release());
+    ZoneData* old = node->setData(content);
     if (old != NULL) {
-        return (AddResult(result::EXIST, old));
+        return (AddResult(result::EXIST, old->isEmpty() ? NULL : old));
     } else {
         ++zone_count_;
         return (AddResult(result::SUCCESS, NULL));
@@ -138,10 +157,16 @@ ZoneTable::findZone(const Name& name) const {
         return (FindResult(result::NOTFOUND, NULL));
     }
 
-    // Can Not Happen (remember, NOTFOUND is handled)
-    assert(node != NULL);
+    // Can Not Happen (remember, NOTFOUND is handled).  node should also have
+    // data because the tree is constructed in the way empty nodes would
+    // be "invisible" for find().
+    assert(node != NULL && node->getData());
 
-    return (FindResult(my_result, node->getData()));
+    const ZoneData* zone_data = node->getData();
+    const result::ResultFlags flags =
+        zone_data->isEmpty() ? result::ZONE_EMPTY : result::FLAGS_DEFAULT;
+    return (FindResult(my_result, zone_data->isEmpty() ? NULL : zone_data,
+                       flags));
 }
 
 } // end of namespace memory

+ 44 - 7
src/lib/datasrc/memory/zone_table.h

@@ -86,10 +86,12 @@ public:
     /// \brief Result data of findZone() method.
     struct FindResult {
         FindResult(result::Result param_code,
-                   const ZoneData* param_zone_data) :
-            code(param_code), zone_data(param_zone_data)
+                   const ZoneData* param_zone_data,
+                   result::ResultFlags param_flags = result::FLAGS_DEFAULT) :
+            code(param_code), flags(param_flags), zone_data(param_zone_data)
         {}
         const result::Result code;
+        const result::ResultFlags flags;
         const ZoneData* const zone_data;
     };
 
@@ -154,7 +156,7 @@ public:
     /// \throw None.
     size_t getZoneCount() const { return (zone_count_); }
 
-    /// Add a new zone to the \c ZoneTable.
+    /// \brief Add a new zone to the \c ZoneTable.
     ///
     /// This method adds a given zone data to the internal table.
     ///
@@ -183,13 +185,36 @@ public:
     ///     added to the zone table.
     /// \return \c result::EXIST The zone table already contained
     ///     zone of the same origin. The old data is replaced and returned
-    ///     inside the result.
+    ///     inside the result unless it's empty; if the zone was previously
+    ///     added by \c addEmptyZone(), the data returned is NULL.
     AddResult addZone(util::MemorySegment& mem_sgmt,
                       dns::RRClass zone_class,
                       const dns::Name& zone_name,
                       ZoneData* content);
 
-    /// Find a zone that best matches the given name in the \c ZoneTable.
+    /// \brief Add an empty zone to the \c ZoneTable.
+    ///
+    /// This method is similar to \c addZone(), but adds a conceptual "empty"
+    /// zone of the given zone name to the table.  The added empty zone
+    /// affects subsequent calls to \c addZone() (and \c addEmptyZone() itself)
+    /// and \c findZone() as described for these methods.
+    ///
+    /// The intended meaning of an empty zone in the table is that the zone
+    /// is somehow broken, such as configured to be loaded but loading failed.
+    /// But this class is not aware of such interpretation; it's up to the
+    /// user of the class how to use the concept of empty zones.
+    ///
+    /// It returns an \c AddResult object as described for \c addZone().
+    ///
+    /// The same notes on exception safety as that for \c addZone() applies.
+    ///
+    /// \param mem_sgmt Same as addZone().
+    /// \param zone_name Same as addZone().
+    AddResult addEmptyZone(util::MemorySegment& mem_sgmt,
+                           const dns::Name& zone_name);
+
+    /// \brief Find a zone that best matches the given name in the
+    /// \c ZoneTable.
     ///
     /// It searches the internal storage for a zone that gives the
     /// longest match against \c name, and returns the result in the
@@ -200,8 +225,11 @@ public:
     ///   - \c result::PARTIALMATCH: A zone whose origin is a
     ///    super domain of \c name is found (but there is no exact match)
     ///   - \c result::NOTFOUND: For all other cases.
-    /// - \c zone_data: corresponding zone data of the found zone; NULL if
-    ///   no matching zone is found.
+    /// - \c flags If the zone is empty (added by \c addEmptyZone()),
+    ///   result::ZONE_EMPTY is set.
+    /// - \c zone_data: corresponding zone data of the found zone if found and
+    ///   non empty; NULL if no matching zone is found or the found zone is
+    ///   empty.
     ///
     /// \throw none
     ///
@@ -216,6 +244,15 @@ private:
 
     // this is a shared placeholder for broken zones
     boost::interprocess::offset_ptr<ZoneData> null_zone_data_;
+
+    // Common routine for addZone and addEmptyZone.  This method can throw
+    // util::MemorySegmentGrown, in which case addresses from mem_sgmt
+    // can be relocated.  The caller is responsible for destroying content
+    // on exception, if it needs to be destroyed.  On successful return it
+    // ensures there's been no address relocation.
+    AddResult addZoneInternal(util::MemorySegment& mem_sgmt,
+                              const dns::Name& zone_name,
+                              ZoneData* content);
 };
 }
 }

+ 20 - 5
src/lib/datasrc/result.h

@@ -18,13 +18,10 @@
 namespace isc {
 namespace datasrc {
 namespace result {
-/// Result codes of various public methods of in memory data source
+/// \brief Result codes of various public methods of DataSourceClient.
 ///
 /// The detailed semantics may differ in different methods.
 /// See the description of specific methods for more details.
-///
-/// Note: this is intended to be used from other data sources eventually,
-/// but for now it's specific to in memory data source and its backend.
 enum Result {
     SUCCESS,  ///< The operation is successful.
     EXIST,    ///< The search key is already stored.
@@ -32,8 +29,26 @@ enum Result {
     PARTIALMATCH ///< Only a partial match is found.
 };
 
+/// \brief Flags for supplemental information along with the \c Result
+///
+/// Initially there's only one flag defined, but several flags will be added
+/// later.  One likely case is to indicate a flag that is listed in in-memory
+/// but its content is served in the underlying data source.  This will help
+/// when only a subset of zones are cached in-memory so the lookup code can
+/// efficiently detect whether it doesn't exist or is not just cached.
+/// When more flags are added, the logical-or operation should be allowed
+/// (by defining \c operator|) on these flags.
+enum ResultFlags {
+    FLAGS_DEFAULT = 0,          // no flags
+    ZONE_EMPTY = 1 ///< The zone found is empty, normally meaning it's broken
+};
+
 }
 }
 }
 
-#endif
+#endif  // DATASRC_RESULT_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 42 - 3
src/lib/datasrc/tests/memory/zone_table_unittest.cc

@@ -92,9 +92,8 @@ TEST_F(ZoneTableTest, addZone) {
     EXPECT_EQ(static_cast<const ZoneData*>(NULL), holder1.get());
     EXPECT_EQ(1, zone_table->getZoneCount()); // count is now incremented
 
-    // Duplicate add doesn't replace the existing data.
-    SegmentObjectHolder<ZoneData, RRClass> holder2(
-        mem_sgmt_, zclass_);
+    // Duplicate add replaces the existing data wit the newly added one.
+    SegmentObjectHolder<ZoneData, RRClass> holder2(mem_sgmt_, zclass_);
     holder2.set(ZoneData::create(mem_sgmt_, zname1));
     const ZoneTable::AddResult result2(zone_table->addZone(mem_sgmt_, zclass_,
                                                            zname1,
@@ -145,6 +144,46 @@ TEST_F(ZoneTableTest, addZone) {
                  std::bad_alloc);
 }
 
+TEST_F(ZoneTableTest, addEmptyZone) {
+    // By default there's no zone contained.
+    EXPECT_EQ(0, zone_table->getZoneCount());
+
+    // Adding an empty zone.  It should succeed.
+    const ZoneTable::AddResult result1 =
+        zone_table->addEmptyZone(mem_sgmt_, zname1);
+    EXPECT_EQ(result::SUCCESS, result1.code);
+    EXPECT_EQ(static_cast<const ZoneData*>(NULL), result1.zone_data);
+    EXPECT_EQ(1, zone_table->getZoneCount());
+
+    // The empty zone can be "found", with the ZONE_EMPTY flag on, and the
+    // returned ZoneData being NULL.
+    const ZoneTable::FindResult fresult1 = zone_table->findZone(zname1);
+    EXPECT_EQ(result::SUCCESS, fresult1.code);
+    EXPECT_EQ(result::ZONE_EMPTY, fresult1.flags);
+    EXPECT_EQ(static_cast<const ZoneData*>(NULL), fresult1.zone_data);
+
+    // Replacing an empty zone with non-empty one.  Should be no problem, but
+    // the empty zone data are not returned in the result structure; it's
+    // internal to the ZoneTable implementation.
+    SegmentObjectHolder<ZoneData, RRClass> holder2(mem_sgmt_, zclass_);
+    holder2.set(ZoneData::create(mem_sgmt_, zname1));
+    const ZoneTable::AddResult result2(zone_table->addZone(mem_sgmt_, zclass_,
+                                                           zname1,
+                                                           holder2.release()));
+    EXPECT_EQ(result::EXIST, result2.code);
+    EXPECT_EQ(static_cast<const ZoneData*>(NULL), result2.zone_data);
+    EXPECT_EQ(1, zone_table->getZoneCount());
+
+    // Replacing a non-empty zone with an empty one is also okay.  It's not
+    // different from replacing with another non-empty one.
+    const ZoneTable::AddResult result3 =
+        zone_table->addEmptyZone(mem_sgmt_, zname1);
+    EXPECT_EQ(result::EXIST, result3.code);
+    EXPECT_NE(static_cast<const ZoneData*>(NULL), result3.zone_data);
+    ZoneData::destroy(mem_sgmt_, result3.zone_data, zclass_);
+    EXPECT_EQ(1, zone_table->getZoneCount());
+}
+
 TEST_F(ZoneTableTest, findZone) {
     SegmentObjectHolder<ZoneData, RRClass> holder1(
         mem_sgmt_, zclass_);