Commit 34159d69 authored by Evan Hunt's avatar Evan Hunt

70. [func] each

  	Added a hot-spot cache to libdatasrc to speed up access to
	repeatedly-queried data and reduce the number of queries to
	the underlying database; this should substantially improve
	performance.  Also added a "-n" ("no cache") option to
	bind10 and b10-auth to disable the cache if needed.
	(Trac #192, svn r2383)


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@2383 e5f2f494-b856-4b98-b285-d166d9295462
parent 222a6e82
70. [func] each
Added a hot-spot cache to libdatasrc to speed up access to
repeatedly-queried data and reduce the number of queries to
the underlying database; this should substantially improve
performance. Also added a "-n" ("no cache") option to
bind10 and b10-auth to disable the cache if needed.
(Trac #192, svn r2383)
bind10-devel-20100701 released on July 1, 2010
69. [func]* jelte
......
......@@ -568,7 +568,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
......
......@@ -60,7 +60,7 @@ private:
AuthSrvImpl(const AuthSrvImpl& source);
AuthSrvImpl& operator=(const AuthSrvImpl& source);
public:
AuthSrvImpl();
AuthSrvImpl(const bool use_cache);
isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config);
......@@ -76,9 +76,13 @@ public:
/// Currently non-configurable, but will be.
static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
/// Hot spot cache
isc::datasrc::HotCache cache_;
};
AuthSrvImpl::AuthSrvImpl() : cs_(NULL), verbose_mode_(false)
AuthSrvImpl::AuthSrvImpl(const bool use_cache) :
cs_(NULL), verbose_mode_(false)
{
// cur_datasrc_ is automatically initialized by the default constructor,
// effectively being an empty (sqlite) data source. once ccsession is up
......@@ -86,9 +90,12 @@ AuthSrvImpl::AuthSrvImpl() : cs_(NULL), verbose_mode_(false)
// add static data source
data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
// enable or disable the cache
cache_.setEnabled(use_cache);
}
AuthSrv::AuthSrv() : impl_(new AuthSrvImpl) {
AuthSrv::AuthSrv(const bool use_cache) : impl_(new AuthSrvImpl(use_cache)) {
}
AuthSrv::~AuthSrv() {
......@@ -239,7 +246,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message,
message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
try {
Query query(message, dnssec_ok);
Query query(message, impl_->cache_, dnssec_ok);
impl_->data_sources_.doQuery(query);
} catch (const Exception& ex) {
if (impl_->verbose_mode_) {
......
......@@ -43,7 +43,7 @@ private:
AuthSrv(const AuthSrv& source);
AuthSrv& operator=(const AuthSrv& source);
public:
explicit AuthSrv();
explicit AuthSrv(const bool use_cache);
~AuthSrv();
//@}
/// \return \c true if the \message contains a response to be returned;
......
......@@ -86,7 +86,7 @@ my_command_handler(const string& command, const ElementPtr args) {
void
usage() {
cerr << "Usage: b10-auth [-p port] [-4|-6]" << endl;
cerr << "Usage: b10-auth [-p port] [-4|-6] [-nv]" << endl;
exit(1);
}
} // end of anonymous namespace
......@@ -95,9 +95,9 @@ int
main(int argc, char* argv[]) {
int ch;
const char* port = DNSPORT;
bool use_ipv4 = true, use_ipv6 = true;
bool use_ipv4 = true, use_ipv6 = true, cache = true;
while ((ch = getopt(argc, argv, "46p:v")) != -1) {
while ((ch = getopt(argc, argv, "46np:v")) != -1) {
switch (ch) {
case '4':
// Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
......@@ -110,6 +110,9 @@ main(int argc, char* argv[]) {
// The same note as -4 applies.
use_ipv4 = false;
break;
case 'n':
cache = false;
break;
case 'p':
port = optarg;
break;
......@@ -142,7 +145,7 @@ main(int argc, char* argv[]) {
specfile = string(AUTH_SPECFILE_LOCATION);
}
auth_server = new AuthSrv;
auth_server = new AuthSrv(cache);
auth_server->setVerbose(verbose_mode);
io_service = new asio_link::IOService(auth_server, port, use_ipv4,
......
......@@ -44,7 +44,7 @@ const char* BADCONFIG_TESTDB =
class AuthSrvTest : public ::testing::Test {
protected:
AuthSrvTest() : request_message(Message::RENDER),
AuthSrvTest() : server(true), request_message(Message::RENDER),
parse_message(Message::PARSE), default_qid(0x1035),
opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
qclass(RRClass::IN()), qtype(RRType::A()), ibuffer(NULL),
......
......@@ -176,8 +176,8 @@ class ProcessInfo:
class BoB:
"""Boss of BIND class."""
def __init__(self, msgq_socket_file=None, auth_port=5300, verbose=False,
setuid=None, username=None):
def __init__(self, msgq_socket_file=None, auth_port=5300, nocache=False,
verbose=False, setuid=None, username=None):
"""Initialize the Boss of BIND. This is a singleton (only one
can run).
......@@ -195,6 +195,7 @@ class BoB:
self.runnable = False
self.uid = setuid
self.username = username
self.nocache = nocache
def config_handler(self, new_config):
if self.verbose:
......@@ -302,6 +303,8 @@ class BoB:
# start b10-auth
# XXX: this must be read from the configuration manager in the future
authargs = ['b10-auth', '-p', str(self.auth_port)]
if self.nocache:
authargs += ['-n']
if self.verbose:
sys.stdout.write("[bind10] Starting b10-auth using port %d\n" %
self.auth_port)
......@@ -557,6 +560,8 @@ def main():
parser = OptionParser(version=__version__)
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
default=False, help="disable hot-spot cache in b10-auth")
parser.add_option("-p", "--port", dest="auth_port", type="string",
action="callback", callback=check_port, default="5300",
help="port the b10-auth daemon will use (default 5300)")
......@@ -621,7 +626,7 @@ def main():
# Go bob!
boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port),
options.verbose, setuid, username)
options.nocache, options.verbose, setuid, username)
startup_result = boss_of_bind.startup()
if startup_result:
sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
......
......@@ -13,3 +13,4 @@ libdatasrc_la_SOURCES = data_source.h data_source.cc
libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
libdatasrc_la_SOURCES += query.h query.cc
libdatasrc_la_SOURCES += cache.h cache.cc
This diff is collapsed.
......@@ -40,7 +40,7 @@ class RRsetList;
namespace datasrc {
class NameMatch;
class DataSrcMatch;
class Query;
class DataSrc;
......@@ -89,12 +89,15 @@ public:
// NAME_NOT_FOUND: The node does not exist in the data source.
// TYPE_NOT_FOUND: The node does not contain the requested RRType
// NO_SUCH_ZONE: The zone does not exist in this data source.
//
// DATA_NOT_FOUND: A combination of the last three, for coding convenience
enum QueryResponseFlags {
REFERRAL = 0x01,
CNAME_FOUND = 0x02,
NAME_NOT_FOUND = 0x04,
TYPE_NOT_FOUND = 0x08,
NO_SUCH_ZONE = 0x10
NO_SUCH_ZONE = 0x10,
DATA_NOT_FOUND = (NAME_NOT_FOUND|TYPE_NOT_FOUND|NO_SUCH_ZONE)
};
// 'High-level' methods. These will be implemented by the
......@@ -107,9 +110,7 @@ public:
// 'Medium-level' methods. This will be implemented by the general
// DataSrc class but MAY be overwritten by subclasses.
virtual void findClosestEnclosure(NameMatch& match,
const isc::dns::RRClass& qclasss)
const = 0;
virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
// Optional 'low-level' methods. These will have stub implementations
// in the general DataSrc class but MAY be overwritten by subclasses
......@@ -179,9 +180,7 @@ public:
virtual void doQuery(Query& q);
virtual void findClosestEnclosure(NameMatch& match,
const isc::dns::RRClass& qclass)
const = 0;
virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
const isc::dns::RRClass& getClass() const { return rrclass; }
void setClass(isc::dns::RRClass& c) { rrclass = c; }
......@@ -250,8 +249,7 @@ public:
void removeDataSrc(ConstDataSrcPtr data_src);
size_t dataSrcCount() { return data_sources.size(); };
void findClosestEnclosure(NameMatch& match,
const isc::dns::RRClass& qclass) const;
void findClosestEnclosure(DataSrcMatch& match) const;
// Actual queries for data should not be sent to a MetaDataSrc object,
// so we return NOT_IMPLEMENTED if we receive any.
......@@ -298,31 +296,107 @@ private:
std::vector<ConstDataSrcPtr> data_sources;
};
class NameMatch {
/// \brief Information about the zone along with the %data source that best
/// matches a give name and RR class.
///
/// A \c DataSrcMatch object is created with a domain name and RR class to
/// hold the search state of looking for the zone and the %data source that
/// stores the zone that best match the given name and RR class.
/// The application of this class passes an object of \c DataSrcMatch to
/// one or more ^data sources via their \c findClosestEnclosure() method.
/// The %data source searches its content for the given key, and update
/// the state if it finds a better zone than the currently recorded one.
///
/// The state of a \c DataSrcMatch object should be updated if and only if:
/// - The specified RR class and the RR class of the %data source are the
// same, or the specified RR class is ANY; and
/// - There is no matching %data source and name found (which is probably
/// wrong, see below), or the given enclosing name gives a longer match
/// than the currently stored enclosing name against the specified name.
class DataSrcMatch {
///
/// \name Constructors, Assignment Operator and Destructor.
///
/// Note: The copy constructor and the assignment operator are intentionally
/// defined as private.
/// Note: The copy constructor and the assignment operator are
/// intentionally defined as private.
//@{
private:
NameMatch(const NameMatch& source);
NameMatch& operator=(const NameMatch& source);
DataSrcMatch(const DataSrcMatch& source);
DataSrcMatch& operator=(const DataSrcMatch& source);
public:
NameMatch(const isc::dns::Name& qname) :
closest_name_(NULL), best_source_(NULL), qname_(qname) {}
~NameMatch();
/// \brief The constructor.
///
/// This constructor normally doesn't throw an exception. However,
/// it creates a copy of the given name object, which may require memory
/// allocation, and if it fails the corresponding standard exception will
/// be thrown.
///
/// \param name The domain name to be matched.
/// \param rrclass The RR class to be matched
DataSrcMatch(const isc::dns::Name& name,
const isc::dns::RRClass& rrclass) :
closest_name_(NULL), best_source_(NULL),
name_(name), rrclass_(rrclass)
{}
~DataSrcMatch();
//@}
void update(const DataSrc& new_source, const isc::dns::Name& container);
/// \name Getter and Setter Methods
//@{
/// \brief Returns the name to be matched.
const isc::dns::Name& getName() const { return (name_); }
const isc::dns::Name& qname() const { return (qname_); }
const isc::dns::Name* closestName() const { return (closest_name_); }
const DataSrc* bestDataSrc() const { return (best_source_); }
/// \brief Returns the RR class to be matched.
///
/// This method never throws an exception.
const isc::dns::RRClass& getClass() const { return (rrclass_); }
/// \brief Returns the best enclosing zone name found for the given
// name and RR class so far.
///
/// \return A pointer to the zone apex \c Name, NULL if none found yet.
///
/// This method never throws an exception.
const isc::dns::Name* getEnclosingZone() const { return (closest_name_); }
/// \brief Returns the best %data source found for the given name and
/// RR class so far.
///
/// This method never throws an exception.
///
/// \return A pointer to a concrete %data source, NULL if none found yet.
const DataSrc* getDataSource() const { return (best_source_); }
//@}
/// \brief Update the object state with better information if possible.
///
/// This method is intended to be called by a concrete %data source's
/// \c findClosestEnclosure() method to store the best match for
/// the given name and class that has been found so far.
///
/// It compares the best name (if found) and \c container, and if the
/// latter gives a longer match, it will install the given %data source
/// and the enclosing name as the best match;
/// if there is no known pair of %data source and enclosing name,
/// this method will install the given pair unconditionally.
/// (which is probably BAD);
/// otherwise this method does nothing.
///
/// In any case, if a new pair of %data source and enclosing name are
/// installed, a new name object will be internally allocated.
/// And, if memory allocation fails the corresponding standard exception
/// will be thrown.
///
/// \param new_source A candidate %data source that gives a better match.
/// \param container The enclosing name of the matching zone in
/// \c new_source.
void update(const DataSrc& new_source, const isc::dns::Name& container);
private:
const isc::dns::Name* closest_name_;
isc::dns::Name* closest_name_;
const DataSrc* best_source_;
const isc::dns::Name qname_;
const isc::dns::Name name_;
const isc::dns::RRClass& rrclass_;
};
class Nsec3Param {
......
......@@ -28,36 +28,37 @@ using namespace isc::dns;
namespace isc {
namespace datasrc {
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect) :
qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY),
state(GETANSWER), flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
op(AUTH_QUERY), state(GETANSWER), flags(0)
{}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect,
const Op o) :
qname(n), qclass(c), qtype(t), section(sect), op(o), state(GETANSWER),
flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
state(GETANSWER), flags(0)
{}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect,
const State st) :
qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY), state(st),
flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
op(AUTH_QUERY), state(st), flags(0)
{}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect,
const Op o, const State st) :
qname(n), qclass(c), qtype(t), section(sect), op(o), state(st), flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
state(st), flags(0)
{}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const Op o) :
qname(n), qclass(c), qtype(t), section(Section::ANSWER()), op(o),
state(GETANSWER), flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t),
section(Section::ANSWER()), op(o), state(GETANSWER), flags(0)
{
if (op != SIMPLE_QUERY) {
isc_throw(Unexpected, "invalid constructor for this task operation");
......@@ -65,21 +66,20 @@ QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
}
// A referral query doesn't need to specify section, state, or type.
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
const Op o) :
qname(n), qclass(c), qtype(RRType::ANY()), section(Section::ANSWER()),
op(o), state(GETANSWER), flags(0)
QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, const Op o) :
q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
section(Section::ANSWER()), op(o), state(GETANSWER), flags(0)
{
if (op != REF_QUERY) {
isc_throw(Unexpected, "invalid constructor for this task operation");
}
}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::Section& sect, const Op o,
const State st) :
qname(n), qclass(c), qtype(RRType::ANY()), section(sect), op(o),
state(st), flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
section(sect), op(o), state(st), flags(0)
{
if (op != GLUE_QUERY && op != NOGLUE_QUERY) {
isc_throw(Unexpected, "invalid constructor for this task operation");
......@@ -88,9 +88,9 @@ QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask::~QueryTask() {}
Query::Query(Message& m, bool dnssec) :
Query::Query(Message& m, HotCache& c, bool dnssec) :
status_(PENDING), qname_(NULL), qclass_(NULL), qtype_(NULL),
message_(&m), want_additional_(true), want_dnssec_(dnssec)
cache_(&c), message_(&m), want_additional_(true), want_dnssec_(dnssec)
{
// Check message formatting
if (message_->getRRCount(Section::QUESTION()) != 1) {
......@@ -104,7 +104,7 @@ Query::Query(Message& m, bool dnssec) :
qtype_ = &question->getType();
restarts_ = 0;
querytasks_.push(QueryTaskPtr(new QueryTask(*qname_, *qclass_, *qtype_,
querytasks_.push(QueryTaskPtr(new QueryTask(*this, *qname_, *qtype_,
Section::ANSWER())));
}
......
......@@ -19,6 +19,9 @@
#include <boost/shared_ptr.hpp>
#include <datasrc/cache.h>
#include <datasrc/data_source.h>
#include <dns/name.h>
#include <dns/message.h>
#include <dns/rrtype.h>
......@@ -27,9 +30,11 @@
#include <queue>
namespace isc {
namespace datasrc {
class Query;
typedef boost::shared_ptr<Query> QueryPtr;
// An individual task to be carried out by the query logic
class QueryTask {
private:
......@@ -41,6 +46,9 @@ public:
// XXX: Members are currently public, but should probably be
// moved to private and wrapped in get() functions later.
// The \c Query that this \c QueryTask was created to service.
const Query& q;
// The standard query tuple: qname/qclass/qtype.
// Note that qtype is ignored in the GLUE_QUERY/NOGLUE_QUERY case.
const isc::dns::Name qname;
......@@ -118,15 +126,14 @@ public:
uint32_t flags;
// Constructors
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect);
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
const isc::dns::RRType& t, const isc::dns::Section& sect,
Op o);
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect, Op o);
QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect,
const State st);
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect,
Op o, State st);
......@@ -134,12 +141,12 @@ public:
// to simplify the code.
//
// A simple query doesn't need to specify section or state.
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, Op o);
// A referral query doesn't need to specify section, state, or type.
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, Op o);
QueryTask(const Query& q, const isc::dns::Name& n, Op o);
// A glue (or noglue) query doesn't need to specify type.
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::Section& sect, Op o, State st);
~QueryTask();
......@@ -148,9 +155,6 @@ public:
typedef boost::shared_ptr<QueryTask> QueryTaskPtr;
typedef std::queue<QueryTaskPtr> QueryTaskQueue;
class Query;
typedef boost::shared_ptr<Query> QueryPtr;
// Data Source query
class Query {
public:
......@@ -171,7 +175,7 @@ private:
Query& operator=(const Query& source);
public:
// Query constructor
Query(isc::dns::Message& m, bool dnssec);
Query(isc::dns::Message& m, HotCache& c, bool dnssec);
/// \brief The destructor.
virtual ~Query();
//@}
......@@ -179,17 +183,17 @@ public:
// wantAdditional() == true indicates that additional-section data
// should be looked up while processing this query. false indicates
// that we're only interested in answer-section data
bool wantAdditional() { return want_additional_; }
bool wantAdditional() { return (want_additional_); }
void setWantAdditional(bool d) { want_additional_ = d; }
// wantDnssec() == true indicates that DNSSEC data should be retrieved
// from the data source when this query is being processed
bool wantDnssec() const { return want_dnssec_; }
bool wantDnssec() const { return (want_dnssec_); }
void setWantDnssec(bool d) { want_dnssec_ = d; }
const isc::dns::Name& qname() const { return *qname_; }
const isc::dns::RRClass& qclass() const { return *qclass_; }
const isc::dns::RRType& qtype() const { return *qtype_; }
const isc::dns::Name& qname() const { return (*qname_); }
const isc::dns::RRClass& qclass() const { return (*qclass_); }
const isc::dns::RRType& qtype() const { return (*qtype_); }
// Note: these can't be constant member functions because they expose
// writable 'handles' of internal member variables. It's questionable
......@@ -197,10 +201,10 @@ public:
// corresponding members are public (which itself is not a good practice
// but it's a different topic), but at the moment we keep them.
// We should definitely revisit the design later.
isc::dns::Message& message() { return *message_; }
QueryTaskQueue& tasks() { return querytasks_; }
isc::dns::Message& message() { return (*message_); }
QueryTaskQueue& tasks() { return (querytasks_); }
Status status() const { return status_; }
Status status() const { return (status_); }
void setStatus(Status s) { status_ = s; }
// Limit CNAME chains to 16 per query, to avoid loops
......@@ -211,6 +215,13 @@ public:
return (false);
}
void setDatasrc(DataSrc* ds) { datasrc_ = ds; }
DataSrc* datasrc() const { return (datasrc_); }
// \brief The query cache. This is a static member of class \c Query;
// the same cache will be used by all instances.
HotCache& getCache() const { return (*cache_); }
private:
Status status_;
......@@ -218,6 +229,9 @@ private:
const isc::dns::RRClass* qclass_;
const isc::dns::RRType* qtype_;
HotCache* cache_;
DataSrc* datasrc_;
isc::dns::Message* message_;
QueryTaskQueue querytasks_;
......
......@@ -344,19 +344,17 @@ Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
}
void
Sqlite3DataSrc::findClosestEnclosure(NameMatch& match,
const RRClass& qclass) const
{
if (qclass != getClass() && qclass != RRClass::ANY()) {
Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
return;
}
unsigned int position;
if (findClosest(match.qname(), &position) == -1) {