Commit d264ba48 authored by Evan Hunt's avatar Evan Hunt
Browse files

Oops: Forgot to explicitly add some files in the recent merge.


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@2385 e5f2f494-b856-4b98-b285-d166d9295462
parent 34159d69
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id$
#include <stdint.h>
#include <map>
#include <dns/question.h>
#include <dns/rrclass.h>
#include <dns/rrset.h>
#include <dns/rrtype.h>
#include <list>
#include <datasrc/cache.h>
using namespace std;
using namespace isc::dns;
namespace isc {
namespace datasrc {
/// \brief A \c CacheEntry object contains the data stored with
/// each \c CacheNode: a pointer to the cached RRset (empty in
/// the case of a negative cache entry), and a copy of the
/// query-response flags that were returned when the RRset
/// was originally looked up in the low-level data source.
class CacheEntry {
private:
/// The copy constructor and the assignment operator are intentionally
/// defined as private.
CacheEntry(const CacheEntry& source);
CacheEntry& operator=(const CacheEntry& source);
public:
CacheEntry(RRsetPtr r, uint32_t f) : rrset(r), flags(f) {};
RRsetPtr rrset;
uint32_t flags;
};
typedef boost::shared_ptr<CacheEntry> CacheEntryPtr;
/// \brief A \c CacheNode is a node in the \c HotCache LRU queue. It
/// contains a pointer to a \c CacheEntry, a reference to the \c Question
/// that we are answering, a lifespan during which this entry remains
/// valid, and pointers to the next and previous entries in the list.
class CacheNode {
private:
/// \name Constructors and Assignment Operator
///
/// Note: The copy constructor and the assignment operator are intentionally
/// defined as private.
//@{
CacheNode(const CacheNode& source);
CacheNode& operator=(const CacheNode& source);
public:
/// \brief Constructor for positive cache entry.
///
/// \param rrset The \c RRset to cache.
/// \param flags The query response flags returned from the low-level
/// data source when this \c RRset was looked up.
/// \param lifespan How long the cache node is to be considered valid.
CacheNode(const RRsetPtr rrset, uint32_t flags, time_t lifespan);
/// \brief Constructor for negative cache entry.
///
/// \param name Query name
/// \param rrclass Query class
/// \param rrtype Query type
/// \param flags Query response flags returned from the low-level
/// data source, indicating why this lookup failed (name not found,
/// type not found, etc).
/// \param lifespan How long the cache node is to be considered valid.
CacheNode(const Name& name,
const RRClass& rrclass,
const RRType& rrtype,
uint32_t flags,
time_t lifespan);
//@}
/// \name Getter and Setter Methods
//@{
/// \brief Returns a pointer to the cached RRset (or an empty
/// RRsetPtr for negative cache entries).
/// \return \c RRsetPtr
RRsetPtr getRRset() const { return (entry->rrset); }
/// \brief Returns the query response flags associated with the data.
///
/// \return \c uint32_t
uint32_t getFlags() const { return (entry->flags); }
/// \brief Is this record still valid?
///
/// \return True if the expiration time has not yet passed,
/// or false if it has.
bool isValid() const;
//@}
// An iterator referencing this entry in the LRU list. This
// permits unit-time removal using list::erase().
list<CacheNodePtr>::iterator lru_entry_;
/// The \c Question (name/rrclass/rrtype) answered by this cache node
const isc::dns::Question question;
private:
// The cached RRset data
CacheEntryPtr entry;
// When this record should be discarded
time_t expiry;
};
// CacheNode constructor for a positive cache entry
CacheNode::CacheNode(const RRsetPtr rrset, const uint32_t flags,
const time_t lifespan) :
question(Question(rrset->getName(), rrset->getClass(), rrset->getType()))
{
const time_t now = time(NULL);
expiry = now + lifespan;
entry = CacheEntryPtr(new CacheEntry(rrset, flags));
}
// CacheNode constructor for a negative cache entry
CacheNode::CacheNode(const Name& name,
const RRClass& rrclass,
const RRType& rrtype,
const uint32_t flags,
const time_t lifespan) :
question(Question(name, rrclass, rrtype))
{
const time_t now = time(NULL);
expiry = now + lifespan;
entry = CacheEntryPtr(new CacheEntry(RRsetPtr(), flags));
}
// Returns true if the node has not yet expired.
bool
CacheNode::isValid() const {
const time_t now = time(NULL);
return (now < expiry);
}
/// This class abstracts the implementation details for \c HotCache.
///
/// Each node inserted into the cache is placed at the head of a
/// doubly-linked list. Whenever that node is retrieved from the cache,
/// it is again moved to the head of the list. When the configured
/// number of slots in the cache has been exceeded, the least recently
/// used nodes will be removed from the tail of the list.
///
/// A pointer to each cache node is also placed in a \c std::map object,
/// keyed by \c isc::dns::Question. This allows retrieval of data in
/// (usually) logarithmic time. (Possible TODO item: replace this with a
/// hash table instead.)
class HotCacheImpl {
public:
HotCacheImpl(int slots, bool enabled);
// The LRU list
list<CacheNodePtr> lru_;
// Flag to indicate whether the cache is enabled
bool enabled_;
// The number of available slots in the LRU list. (If zero,
// then the list is unbounded; otherwise, items are removed
// from the tail of the list whenever it grows past slots_.)
int slots_;
// The number of items currently in the list.
int count_;
// Map from query tuple to cache node pointer, allowing fast retrieval
// of data without a linear search of the LRU list
std::map<Question, CacheNodePtr> map_;
// Move a node to the front of the LRU list.
void promote(CacheNodePtr node);
// Remove a node from the cache.
void remove(ConstCacheNodePtr node);
// Insert a node into the cache (called by both cache() and ncache())
void insert(CacheNodePtr node);
};
// HotCacheImpl constructor
HotCacheImpl::HotCacheImpl(int slots, bool enabled) :
enabled_(enabled), slots_(slots), count_(0)
{}
// Insert a cache node into the cache
inline void
HotCacheImpl::insert(const CacheNodePtr node) {
std::map<Question, CacheNodePtr>::const_iterator iter;
iter = map_.find(node->question);
if (iter != map_.end()) {
CacheNodePtr old = iter->second;
if (old && old->isValid()) {
remove(old);
}
}
lru_.push_front(node);
node->lru_entry_ = lru_.begin();
map_[node->question] = node;
++count_;
if (slots_ != 0 && count_ > slots_) {
remove(lru_.back());
}
}
// Promote a node to the head of the LRU list
void
HotCacheImpl::promote(CacheNodePtr node) {
if (!node) {
return;
}
if (node->lru_entry_ == lru_.begin()) {
return;
}
lru_.erase(node->lru_entry_);
lru_.push_front(node);
node->lru_entry_ = lru_.begin();
}
// Remove a node from the LRU list and the map
void
HotCacheImpl::remove(ConstCacheNodePtr node) {
lru_.erase(node->lru_entry_);
map_.erase(node->question);
--count_;
}
// HotCache constructor
HotCache::HotCache(const int slots) {
impl_ = new HotCacheImpl(slots, true);
}
// HotCache destructor
HotCache::~HotCache() {
delete impl_;
}
// Add a positive entry to the cache
void
HotCache::addPositive(RRsetPtr rrset, const uint32_t flags,
const time_t lifespan)
{
if (!impl_->enabled_) {
return;
}
impl_->insert(CacheNodePtr(new CacheNode(rrset, flags, lifespan)));
}
// Add a negative entry to the cache
void
HotCache::addNegative(const Name& name, const RRClass &rrclass,
const RRType& rrtype, const uint32_t flags,
const time_t lifespan)
{
if (!impl_->enabled_) {
return;
}
if (rrtype == RRType::ANY() || rrclass == RRClass::ANY()) {
return;
}
impl_->insert(CacheNodePtr(new CacheNode(name, rrclass, rrtype,
flags, lifespan)));
}
// Try to retrieve an entry from the cache, returning true if
// it was found and valid.
bool
HotCache::retrieve(const Name& n, const RRClass& c, const RRType& t,
RRsetPtr& rrset, uint32_t& flags)
{
if (!impl_->enabled_) {
return (false);
}
std::map<Question, CacheNodePtr>::const_iterator iter;
iter = impl_->map_.find(Question(n, c, t));
if (iter == impl_->map_.end()) {
return (false);
}
CacheNodePtr node = iter->second;
if (node->isValid()) {
impl_->promote(node);
rrset = node->getRRset();
flags = node->getFlags();
return (true);
}
impl_->remove(node);
return (false);
}
// Set the number of slots in the cache.
void
HotCache::setSlots(const int slots) {
impl_->slots_ = slots;
if (!impl_->enabled_) {
return;
}
while (impl_->slots_ != 0 && impl_->count_ > impl_->slots_) {
impl_->remove(impl_->lru_.back());
}
}
// Return the number of slots in the cache
int
HotCache::getSlots() const {
return (impl_->slots_);
}
/// Enable or disable the cache
void
HotCache::setEnabled(const bool e) {
impl_->enabled_ = e;
}
/// Indicate whether the cache is enabled
bool
HotCache::getEnabled() const {
return (impl_->enabled_);
}
// Return the number of entries in the cache
int
HotCache::getCount() const {
return (impl_->count_);
}
}
}
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id$
#ifndef __CACHE_H
#define __CACHE_H
#include <time.h>
#include <boost/shared_ptr.hpp>
#include <dns/rrset.h>
namespace isc {
namespace dns {
class Name;
class RRClass;
class RRType;
}
namespace datasrc {
class CacheNode;
typedef boost::shared_ptr<CacheNode> CacheNodePtr;
typedef boost::shared_ptr<const CacheNode> ConstCacheNodePtr;
class HotCacheImpl;
/// \brief A \c HotCache is a hot-spot cache for one or more data sources.
///
/// A \c HotCache must be instantiated prior to creating a \c Query.
/// The same instance should be passed to the constructor for all queries,
/// so that all of them will be using the same cache.
///
/// The cache may contain positive or negative entries, indicating
/// whether the data does or does not exist in the underlying data
/// source. Entries have a fixed and limited lifespan (currently
/// set to 30 seconds, see LIFESPAN_ below). If a cache entry is
/// found which has exceeded its lifespan, it will not be returned
/// to the caller--exactly as if it had not been found.
///
/// The current 30 second cache entry lifespan is experimental. A longer
/// lifespan would improve performance somewhat; however, longer-lived
/// cache entries are more likely to be incorrect in the event that
/// the underlying data source had been updated. Depending on the
/// frequency of queries and the frequency of updates, longer or
/// shorter lifespans may be desirable -- it's even possible
/// we may want the lifespan to be set differently depending on
/// the zone or the data source (i.e., with an infinite lifespan
/// for cached data from a static data source). Additional benchmarking
/// and analysis will be needed for this.
///
/// The cache may be configured with a number of available slots for
/// for entries. When set to a nonzero value, no more than that number
/// of entries can exist in the cache. If more entries are inserted,
/// old entries will be dropped in "least recently used" order. If
/// set to zero, the cache size is unlimited. The current default is
/// based on one thousand queries per second, times the number of seconds
/// in the cache lifespan: 30,000 slots.
///
/// Notes to developers: The current implementation of HotCache uses
/// a std::map (keyed by isc::dns::Question) to locate nodes, so access
/// will generally be in O(log n) time. (XXX: This might be faster if a
/// hash table were used instead.)
///
/// A linked list is also maintained to keep track of recent accesses
/// to cache entries; each time an entry is accessed, it is moved to the
/// head of the list; when entries need to be removed, they are taken
/// from the tail of the list. This operation is not locked. BIND 10
/// does not currently use threads, but if it ever does (or if libdatasrc
/// is ever used by a threaded application), this will need to be
//revisited.
class HotCache {
private:
/// \name Static definitions
//@{
/// \brief Default validity period for cache entries
static const int LIFESPAN_ = 30;
/// \brief Default number of slots in cache
static const int SLOTS_ = 1000 * LIFESPAN_;
//@}
/// \name Constructors, Assignment Operator and Destructor.
///
/// Note: The copy constructor and the assignment operator are intentionally
/// defined as private.
//@{
HotCache(const HotCache& source);
HotCache& operator=(const HotCache& source);
public:
/// \brief Constructor for HotCache
///
/// \param slots The number of slots available in the cache.
HotCache(const int slots = SLOTS_);
/// \brief Destructor for HotCache
~HotCache();
//@}
/// \name Cache Manipulation Methods
//@{
/// \brief Enter a positive cache entry.
///
/// If an entry already exists in the cache which matches the
/// name/class/type of the RRset being cached, then the old entry
/// is removed before the the new one is inserted. (XXX: This is
/// currently slightly inefficient; it would be quicker to keep the
/// existing node and simply update the rrset, flags, and lifespan.)
///
/// \param rrset The \c RRset to cache.
/// \param flags The query response flags returned from the low-level
/// data source when this \c RRset was looked up.
/// \param lifespan How long the cache node is to be considered valid;
/// defaulting to 30 seconds.
void addPositive(isc::dns::RRsetPtr rrset,
uint32_t flags,
time_t lifespan = LIFESPAN_);
/// \brief Enter a negative cache entry.
///
/// In the case of a negative cache entry there is no \c RRset to
/// cache, so instead a null \c RRsetPtr will be stored. Since the
/// name, class, and type cannot be retrieved from an \c RRset, they
/// must be specified in the parameters.
///
/// If an entry already exists in the cache which matches the
/// specified name/class/type, then the old entry is removed
/// before the the new one is inserted. (XXX: As noted in the comments
/// for addPositive(), this is currently slightly inefficient.)
///
/// \param name Query name
/// \param rrclass Query class
/// \param rrtype Query type
/// \param flags Query response flags returned from the low-level
/// data source, indicating why this lookup failed (name not found,
/// type not found, etc).
/// \param lifespan How long the cache node is to be considered valid;
/// defaulting to 30 seconds.
///
/// Note: 'rrclass' and 'rrtype' must refer to a specific class and
/// type; it is not meaningful to cache type or class ANY. Currently,
/// this condition is silently ignored.
void addNegative(const isc::dns::Name& name,
const isc::dns::RRClass& rrclass,
const isc::dns::RRType& rrtype,
uint32_t flags,
time_t lifespan = LIFESPAN_);
/// \brief Retrieve (and promote) a record from the cache
///
/// Retrieves a record from the cache matching the given
/// query-tuple. Returns true if one is found. If it is a
/// posiitve cache entry, then 'rrset' is set to the cached
/// RRset. For both positive and negative cache entries, 'flags'
/// is set to the query response flags. The cache entry is
/// then promoted to the head of the LRU queue. (NOTE: Because
/// of this, "retrieve" cannot be implemented as a const method.)
///
/// \param name The query name
/// \param rrclass The query class
/// \param rrtype The query type
/// \param rrset Returns the RRset found, if any, to the caller
/// \param flags Returns the flags, if any, to the caller
///
/// \return \c bool, true if data was found in the cache, false if not.
bool retrieve(const isc::dns::Name& qname,
const isc::dns::RRClass& qclass,
const isc::dns::RRType& qtype,
isc::dns::RRsetPtr& rrset,
uint32_t& flags);
//@}
/// \name Getter and Setter Methods
//@{
/// \brief Sets the number of slots in the cache.
///
/// If slots is set to zero, the cache can grow without any imposed
/// limit. If slots is to set a lower number than the cache currently
/// contains, then the least recently used records will be purged from
/// the cache until the total number of items in the cache equals slots.
void setSlots(int slots);
/// \brief Returns the number of slots in the cache.
int getSlots() const;
/// \brief Enable or disable the cache
void setEnabled(bool e);
/// \brief Indicate whether the cache is enabled
bool getEnabled() const;
/// \brief Returns the number of nodes currently stored in the cache.
///
/// Note that this doesn't indicate how many nodes are still valid;
/// some may have expired.
int getCount() const;
//@}
private:
/// \brief Hidden implementation details
HotCacheImpl* impl_;
};
}
}
#endif
// Local Variables:
// mode: c++
// End:
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id$
#include <stdexcept>
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rrset.h>
#include <datasrc/cache.h>
#include <datasrc/data_source.h>
#include <gtest/gtest.h>
using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::datasrc;
namespace {
class CacheTest : public ::testing::Test {
protected:
CacheTest() : test_name("test.example.com"),
test_nsname("ns.example.com"),
test_ch("example.com")
{