Commit 1a488b6c authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[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.
parent cfbf803b
......@@ -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.
......@@ -91,6 +91,26 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
SegmentObjectHolder<ZoneData, RRClass> holder(mem_sgmt, zone_class);
const AddResult result =
addZoneInternal(mem_sgmt, zone_name, holder.get());
return (result);
ZoneTable::addEmptyZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
return (addZoneInternal(mem_sgmt, zone_name, null_zone_data_.get()));
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 {
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,
} // end of namespace memory
......@@ -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);
......@@ -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
// Local Variables:
// mode: c++
// End:
......@@ -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_,
......@@ -145,6 +144,46 @@ TEST_F(ZoneTableTest, addZone) {
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_,
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_);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment