Commit 8e7af7e1 authored by Michal Vaner's avatar Michal Vaner
Browse files

Sync with trunk

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac458@4146 e5f2f494-b856-4b98-b285-d166d9295462
parents 0cb5cf23 3f51d6ed
143. [build] jinmei
Fixed build problems with clang++ in unit tests due to recent
changes. No behavior change. (Trac #448, svn r4133)
142. [func] jinmei
b10-auth: updated query benchmark so that it can test in memory
data source. Also fixed a bug that the output buffer isn't
cleared after query processing, resulting in misleading results
or program crash. This is a regression due to change #135.
(Trac #465, svn r4103)
141. [bug] jinmei
b10-auth: Fixed a bug that the authoritative server includes
trailing garbage data in responses. This is a regression due to
change #135. (Trac #462, svn r4081)
140. [func] y-aharen
src/bin/auth: Added a feature to count queries and send counter
values to statistics periodically. To support it, added wrapping
......
......@@ -597,6 +597,8 @@ AC_CONFIG_FILES([Makefile
src/lib/Makefile
src/lib/asiolink/Makefile
src/lib/asiolink/tests/Makefile
src/lib/asiolink/internal/Makefile
src/lib/asiolink/internal/tests/Makefile
src/lib/bench/Makefile
src/lib/bench/example/Makefile
src/lib/bench/tests/Makefile
......
......@@ -162,33 +162,20 @@ private:
AuthSrv* server_;
};
// This is a derived class of \c DNSAnswer, to serve as a
// callback in the asiolink module. It takes a completed
// set of answer data from the DNS lookup and assembles it
// into a wire-format response.
// This is a derived class of \c DNSAnswer, to serve as a callback in the
// asiolink module. We actually shouldn't do anything in this class because
// we build complete response messages in the process methods; otherwise
// the response message will contain trailing garbage. In future, we should
// probably even drop the reliance on DNSAnswer. We don't need the coroutine
// tricks provided in that framework, and its overhead would be significant
// in terms of performance consideration for the authoritative server
// implementation.
class MessageAnswer : public DNSAnswer {
public:
MessageAnswer(AuthSrv* srv) : server_(srv) {}
virtual void operator()(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer) const
{
MessageRenderer renderer(*buffer);
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
ConstEDNSPtr edns(message->getEDNS());
renderer.setLengthLimit(edns ? edns->getUDPSize() :
Message::DEFAULT_MAX_UDPSIZE);
} else {
renderer.setLengthLimit(65535);
}
message->toWire(renderer);
if (server_->getVerbose()) {
cerr << "[b10-auth] sending a response (" << renderer.getLength()
<< " bytes):\n" << message->toText() << endl;
}
}
private:
AuthSrv* server_;
MessageAnswer(AuthSrv*) {}
virtual void operator()(const IOMessage&, MessagePtr,
OutputBufferPtr) const
{}
};
// This is a derived class of \c SimpleCallback, to serve
......
......@@ -33,6 +33,7 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/query.h>
#include <asiolink/asiolink.h>
......@@ -53,24 +54,25 @@ XfroutClient xfrout_client("dummy_path"); // path doesn't matter
// Just something to pass as the server to resume
class DummyServer : public DNSServer {
public:
virtual void operator()(asio::error_code, size_t) { }
virtual void resume(const bool) { }
virtual void operator()(asio::error_code, size_t) {}
virtual void resume(const bool) {}
virtual DNSServer* clone() {
return new DummyServer(*this);
return (new DummyServer(*this));
}
};
class QueryBenchMark {
private:
protected:
// Maintain dynamically generated objects via shared pointers because
// QueryBenchMark objects will be copied.
typedef boost::shared_ptr<AuthSrv> AuthSrvPtr;
private:
typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
public:
QueryBenchMark(const int cache_slots, const char* const datasrc_file,
protected:
QueryBenchMark(const bool enable_cache,
const BenchQueries& queries, MessagePtr query_message,
OutputBufferPtr buffer) :
server_(new AuthSrv(cache_slots >= 0 ? true : false, xfrout_client)),
server_(new AuthSrv(enable_cache, xfrout_client)),
queries_(queries),
query_message_(query_message),
buffer_(buffer),
......@@ -78,13 +80,8 @@ public:
dummy_endpoint(IOEndpointPtr(IOEndpoint::create(IPPROTO_UDP,
IOAddress("192.0.2.1"),
5300)))
{
if (cache_slots >= 0) {
server_->setCacheSlots(cache_slots);
}
server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
string(datasrc_file) + "\"}"));
}
{}
public:
unsigned int run() {
BenchQueries::const_iterator query;
const BenchQueries::const_iterator query_end = queries_.end();
......@@ -93,14 +90,16 @@ public:
IOMessage io_message(&(*query)[0], (*query).size(), dummy_socket,
*dummy_endpoint);
query_message_->clear(Message::PARSE);
buffer_->clear();
server_->processMessage(io_message, query_message_, buffer_,
&server);
&server);
}
return (queries_.size());
}
private:
protected:
AuthSrvPtr server_;
private:
const BenchQueries& queries_;
MessagePtr query_message_;
OutputBufferPtr buffer_;
......@@ -108,26 +107,92 @@ private:
IOEndpointPtr dummy_endpoint;
};
class Sqlite3QueryBenchMark : public QueryBenchMark {
public:
Sqlite3QueryBenchMark(const int cache_slots,
const char* const datasrc_file,
const BenchQueries& queries,
MessagePtr query_message,
OutputBufferPtr buffer) :
QueryBenchMark(cache_slots >= 0 ? true : false, queries,
query_message, buffer)
{
if (cache_slots >= 0) {
server_->setCacheSlots(cache_slots);
}
server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
string(datasrc_file) + "\"}"));
}
};
class MemoryQueryBenchMark : public QueryBenchMark {
public:
MemoryQueryBenchMark(const char* const zone_file,
const char* const zone_origin,
const BenchQueries& queries,
MessagePtr query_message,
OutputBufferPtr buffer) :
QueryBenchMark(false, queries, query_message, buffer)
{
configureAuthServer(*server_,
Element::fromJSON(
"{\"datasources\": "
" [{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"" +
string(zone_origin) + "\","
" \"file\": \"" +
string(zone_file) + "\"}]}]}"));
}
};
void
printQPSResult(unsigned int iteration, double duration,
double iteration_per_second)
{
cout.precision(6);
cout << "Processed " << iteration << " queries in "
<< fixed << duration << "s";
cout.precision(2);
cout << " (" << fixed << iteration_per_second << "qps)" << endl;
}
}
namespace isc {
namespace bench {
template<>
void
BenchMark<QueryBenchMark>::printResult() const {
cout.precision(6);
cout << "Processed " << getIteration() << " queries in "
<< fixed << getDuration() << "s";
cout.precision(2);
cout << " (" << fixed << getIterationPerSecond() << "qps)" << endl;
BenchMark<Sqlite3QueryBenchMark>::printResult() const {
printQPSResult(getIteration(), getDuration(), getIterationPerSecond());
}
template<>
void
BenchMark<MemoryQueryBenchMark>::printResult() const {
printQPSResult(getIteration(), getDuration(), getIterationPerSecond());
}
}
}
namespace {
const int ITERATION_DEFAULT = 1;
enum DataSrcType {
SQLITE3,
MEMORY
};
void
usage() {
cerr << "Usage: query_bench [-n iterations] datasrc_file query_datafile"
cerr <<
"Usage: query_bench [-n iterations] [-t datasrc_type] [-o origin] "
"datasrc_file query_datafile\n"
" -n Number of iterations per test case (default: "
<< ITERATION_DEFAULT << ")\n"
" -t Type of data source: sqlite3|memory (default: sqlite3)\n"
" -o Origin name of datasrc_file necessary for \"memory\", "
"ignored for others\n"
" datasrc_file: sqlite3 DB file for \"sqlite3\", "
"textual master file for \"memory\" datasrc\n"
" query_datafile: queryperf style input data"
<< endl;
exit (1);
}
......@@ -136,12 +201,20 @@ usage() {
int
main(int argc, char* argv[]) {
int ch;
int iteration = 1;
while ((ch = getopt(argc, argv, "n:")) != -1) {
int iteration = ITERATION_DEFAULT;
const char* opt_datasrc_type = "sqlite3";
const char* origin = NULL;
while ((ch = getopt(argc, argv, "n:t:o:")) != -1) {
switch (ch) {
case 'n':
iteration = atoi(optarg);
break;
case 't':
opt_datasrc_type = optarg;
break;
case 'o':
origin = optarg;
break;
case '?':
default:
usage();
......@@ -155,6 +228,21 @@ main(int argc, char* argv[]) {
const char* const datasrc_file = argv[0];
const char* const query_data_file = argv[1];
DataSrcType datasrc_type = SQLITE3;
if (strcmp(opt_datasrc_type, "sqlite3") == 0) {
; // no need to override
} else if (strcmp(opt_datasrc_type, "memory") == 0) {
datasrc_type = MEMORY;
} else {
cerr << "Unknown data source type: " << datasrc_type << endl;
return (1);
}
if (datasrc_type == MEMORY && origin == NULL) {
cerr << "'-o Origin' is missing for memory data source " << endl;
return (1);
}
BenchQueries queries;
loadQueryData(query_data_file, queries, RRClass::IN());
OutputBufferPtr buffer(new OutputBuffer(4096));
......@@ -162,32 +250,46 @@ main(int argc, char* argv[]) {
cout << "Parameters:" << endl;
cout << " Iterations: " << iteration << endl;
cout << " Data Source: " << datasrc_file << endl;
cout << " Data Source: type=" << opt_datasrc_type << ", file=" <<
datasrc_file << endl;
if (origin != NULL) {
cout << " Origin: " << origin << endl;
}
cout << " Query data: file=" << query_data_file << " (" << queries.size()
<< " queries)" << endl << endl;
cout << "Benchmark enabling Hot Spot Cache with unlimited slots "
<< endl;
BenchMark<QueryBenchMark>(iteration,
QueryBenchMark(0, datasrc_file, queries, message,
buffer));
switch (datasrc_type) {
case SQLITE3:
cout << "Benchmark enabling Hot Spot Cache with unlimited slots "
<< endl;
BenchMark<Sqlite3QueryBenchMark>(
iteration, Sqlite3QueryBenchMark(0, datasrc_file, queries,
message, buffer));
cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
<< endl;
BenchMark<QueryBenchMark>(iteration,
QueryBenchMark(10 * queries.size(), datasrc_file,
cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
<< endl;
BenchMark<Sqlite3QueryBenchMark>(
iteration, Sqlite3QueryBenchMark(10 * queries.size(), datasrc_file,
queries, message, buffer));
cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
<< endl;
BenchMark<QueryBenchMark>(iteration,
QueryBenchMark(queries.size() / 2, datasrc_file,
cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
<< endl;
BenchMark<Sqlite3QueryBenchMark>(
iteration, Sqlite3QueryBenchMark(queries.size() / 2, datasrc_file,
queries, message, buffer));
cout << "Benchmark disabling Hot Spot Cache" << endl;
BenchMark<QueryBenchMark>(iteration,
QueryBenchMark(-1, datasrc_file, queries,
message, buffer));
cout << "Benchmark disabling Hot Spot Cache" << endl;
BenchMark<Sqlite3QueryBenchMark>(
iteration, Sqlite3QueryBenchMark(-1, datasrc_file, queries,
message, buffer));
break;
case MEMORY:
cout << "Benchmark with In Memory Data Source" << endl;
BenchMark<MemoryQueryBenchMark>(
iteration, MemoryQueryBenchMark(datasrc_file, origin, queries,
message, buffer));
break;
}
return (0);
}
......@@ -151,16 +151,21 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) {
isc_throw(AuthConfigError, "Missing zone file for zone: "
<< origin->str());
}
const result::Result result = memory_datasrc_->addZone(
ZonePtr(new MemoryZone(rrclass_, Name(origin->stringValue()))));
shared_ptr<MemoryZone> new_zone(new MemoryZone(rrclass_,
Name(origin->stringValue())));
const result::Result result = memory_datasrc_->addZone(new_zone);
if (result == result::EXIST) {
isc_throw(AuthConfigError, "zone "<< origin->str()
<< " already exists");
}
// TODO
// then load the zone from 'file', which is currently not implemented.
//
/*
* TODO: Once we have better reloading of configuration (something
* else than throwing everything away and loading it again), we will
* need the load method to be split into some kind of build and
* commit/abort parts.
*/
new_zone->load(file->stringValue());
}
}
......
......@@ -25,11 +25,70 @@
using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::dns::rdata;
using namespace std;
namespace isc {
namespace auth {
void
Query::getAdditional(const isc::datasrc::Zone& zone,
const isc::dns::RRset& rrset) const
{
if (rrset.getType() == RRType::NS()) {
// Need to perform the search in the "GLUE OK" mode.
RdataIteratorPtr rdata_iterator = rrset.getRdataIterator();
for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
const Rdata& rdata(rdata_iterator->getCurrent());
const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
findAddrs(zone, ns.getNSName(), Zone::FIND_GLUE_OK);
}
}
}
void
Query::findAddrs(const isc::datasrc::Zone& zone,
const isc::dns::Name& qname,
const isc::datasrc::Zone::FindOptions options) const
{
// Out of zone name
NameComparisonResult result = zone.getOrigin().compare(qname);
if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
(result.getRelation() != NameComparisonResult::EQUAL))
return;
// Find A rrset
Zone::FindResult a_result = zone.find(qname, RRType::A(), options);
if (a_result.code == Zone::SUCCESS) {
response_.addRRset(Message::SECTION_ADDITIONAL,
boost::const_pointer_cast<RRset>(a_result.rrset));
}
// Find AAAA rrset
Zone::FindResult aaaa_result = zone.find(qname, RRType::AAAA(), options);
if (aaaa_result.code == Zone::SUCCESS) {
response_.addRRset(Message::SECTION_ADDITIONAL,
boost::const_pointer_cast<RRset>(aaaa_result.rrset));
}
}
void
Query::putSOA(const Zone& zone) const {
Zone::FindResult soa_result(zone.find(zone.getOrigin(),
RRType::SOA()));
if (soa_result.code != Zone::SUCCESS) {
isc_throw(NoSOA, "There's no SOA record in zone " <<
zone.getOrigin().toText());
} else {
/*
* FIXME:
* The const-cast is wrong, but the Message interface seems
* to insist.
*/
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<RRset>(soa_result.rrset));
}
}
void
Query::process() const {
bool keep_doing = true;
......@@ -100,15 +159,21 @@ Query::process() const {
// TODO : fill in authority and addtional sections.
break;
case Zone::DELEGATION:
// TODO : add NS to authority section, fill in additional section.
response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
response_.setRcode(Rcode::NOERROR());
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<RRset>(db_result.rrset));
getAdditional(*result.zone, *db_result.rrset);
break;
case Zone::NXDOMAIN:
// Just empty answer with SOA in authority section
response_.setRcode(Rcode::NXDOMAIN());
// TODO : add SOA to authority section
putSOA(*result.zone);
break;
case Zone::NXRRSET:
// Just empty answer with SOA in authority section
response_.setRcode(Rcode::NOERROR());
// TODO : add SOA to authority section
putSOA(*result.zone);
break;
case Zone::CNAME:
case Zone::DNAME:
......@@ -117,5 +182,6 @@ Query::process() const {
}
}
}
}
}
......@@ -14,11 +14,15 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <exceptions/exceptions.h>
#include <datasrc/zone.h>
namespace isc {
namespace dns {
class Message;
class Name;
class RRType;
class RRset;
}
namespace datasrc {
......@@ -60,6 +64,48 @@ namespace auth {
/// accidentally, and since it's considered a temporary development state,
/// we keep this name at the moment.
class Query {
private:
/// \short Adds a SOA.
///
/// Adds a SOA of the zone into the authority zone of response_.
/// Can throw NoSOA.
///
void putSOA(const isc::datasrc::Zone& zone) const;
/// Look up additional data (i.e., address records for the names included
/// in NS or MX records).
///
/// This method may throw a exception because its underlying methods may
/// throw exceptions.
///
/// \param zone The Zone wherein the additional data to the query is bo be
/// found.
/// \param rrset The RRset (i.e., NS or MX rrset) which require additional
/// processing.
void getAdditional(const isc::datasrc::Zone& zone,
const isc::dns::RRset& rrset) const;
/// Find address records for a specified name.
///
/// Search the specified zone for AAAA/A RRs of each of the NS/MX RDATA
/// (domain name), and insert the found ones into the additional section
/// if address records are available. By default the search will stop
/// once it encounters a zone cut.
///
/// Note: we need to perform the search in the "GLUE OK" mode for NS RDATA,
/// which means that we should include A/AAAA RRs under a zone cut.
/// The glue records must exactly match the name in the NS RDATA, without
/// CNAME or wildcard processing.
///
/// \param zone The Zone wherein the address records is to be found.
/// \param qname The name in rrset RDATA.
/// \param options The search options.
void findAddrs(const isc::datasrc::Zone& zone,
const isc::dns::Name& qname,
const isc::datasrc::Zone::FindOptions options
= isc::datasrc::Zone::FIND_DEFAULT) const;
public:
/// Constructor from query parameters.
///
......@@ -100,10 +146,33 @@ public:
/// providing compatible behavior may have its own benefit, so this point
/// should be revisited later.
///
/// Right now this method never throws an exception, but it may in a
/// future version.
/// This might throw BadZone or any of its specific subclasses, but that
/// shouldn't happen in real-life (as BadZone means wrong data, it should
/// have been rejected upon loading).
void process() const;
/// \short Bad zone data encountered.
///
/// This is thrown when process encounteres misconfigured zone in a way
/// it can't continue. This throws, not sets the Rcode, because such
/// misconfigured zone should not be present in the data source and
/// should have been rejected sooner.
struct BadZone : public isc::Exception {
BadZone(const char* file, size_t line, const char* what) :
Exception(file, line, what)
{}
};
/// \short Zone is missing its SOA record.
///
/// We tried to add a SOA into the authoritative section, but the zone
/// does not contain one.
struct NoSOA : public BadZone {
NoSOA(const char* file, size_t line, const char* what) :
BadZone(file, line, what)
{}
};
private:
const isc::datasrc::MemoryDataSrc& memory_datasrc_;
const isc::dns::Name& qname_;
......
......@@ -33,6 +33,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
......
......@@ -15,16 +15,34 @@
// $Id$
#include <config.h>
#include <vector>
#include <gtest/gtest.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>