Commit f63a2aa7 authored by Jerry's avatar Jerry
Browse files

sync with trunk


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac453@4110 e5f2f494-b856-4b98-b285-d166d9295462
parents 78a33024 c708d579
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
......
......@@ -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);
}
......@@ -67,6 +67,24 @@ Query::findAddrs(const isc::datasrc::Zone& zone,
}
}
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;
......@@ -105,12 +123,14 @@ Query::process() const {
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:
......
......@@ -14,6 +14,8 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <exceptions/exceptions.h>
namespace isc {
namespace dns {
class Message;
......@@ -132,15 +134,46 @@ 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_;
const isc::dns::RRType& qtype_;
isc::dns::Message& response_;
/**
* \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;
};
}
......
......@@ -15,6 +15,19 @@
// $Id$
#include <config.h>
#include <vector>
#include <gtest/gtest.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rdataclass.h>
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
#include <testutils/srv_unittest.h>
......@@ -22,6 +35,7 @@
using namespace isc::cc;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::xfr;
using namespace asiolink;
......@@ -45,8 +59,104 @@ protected:
MockXfroutClient xfrout;
AuthSrv server;
const RRClass rrclass;
vector<uint8_t> response_data;
};
// A helper function that builds a response to version.bind/TXT/CH that
// should be identical to the response from our builtin (static) data source
// by default. The resulting wire-format data will be stored in 'data'.
void
createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
const Name version_name("version.bind");
Message message(Message::RENDER);
UnitTestUtil::createRequestMessage(message, Opcode::QUERY(),
qid, version_name,
RRClass::CH(), RRType::TXT());
message.setHeaderFlag(Message::HEADERFLAG_QR);
message.setHeaderFlag(Message::HEADERFLAG_AA);
RRsetPtr rrset_version = RRsetPtr(new RRset(version_name, RRClass::CH(),
RRType::TXT(), RRTTL(0)));
rrset_version->addRdata(generic::TXT(PACKAGE_STRING));
message.addRRset(Message::SECTION_ANSWER, rrset_version);
RRsetPtr rrset_version_ns = RRsetPtr(new RRset(version_name, RRClass::CH(),
RRType::NS(), RRTTL(0)));
rrset_version_ns->addRdata(generic::NS(version_name));
message.addRRset(Message::SECTION_AUTHORITY, rrset_version_ns);
OutputBuffer obuffer(0);
MessageRenderer renderer(obuffer);
message.toWire(renderer);
data.clear();
data.assign(static_cast<const uint8_t*>(renderer.getData()),
static_cast<const uint8_t*>(renderer.getData()) +
renderer.getLength());
}
// In the following tests we confirm the response data is rendered in
// wire format in the expected way.
// The most primitive check: checking the result of the processMessage()
// method
TEST_F(AuthSrvTest, builtInQuery) {
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("version.bind"),
RRClass::CH(), RRType::TXT());
createRequestPacket(request_message, IPPROTO_UDP);
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
createBuiltinVersionResponse(default_qid, response_data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
response_obuffer->getData(),
response_obuffer->getLength(),
&response_data[0], response_data.size());
}
// Same test emulating the UDPServer class behavior (defined in libasiolink).
// This is not a good test in that it assumes internal implementation details
// of UDPServer, but we've encountered a regression due to the introduction
// of that class, so we add a test for that case to prevent such a regression
// in future.
// Besides, the generalization of UDPServer is probably too much for the
// authoritative only server in terms of performance, and it's quite likely
// we need to drop it for the authoritative server implementation.
// At that point we can drop this test, too.
TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("version.bind"),
RRClass::CH(), RRType::TXT());
createRequestPacket(request_message, IPPROTO_UDP);
(*server.getDNSLookupProvider())(*io_message, parse_message,
response_obuffer, &dnsserv);
(*server.getDNSAnswerProvider())(*io_message, parse_message,
response_obuffer);
createBuiltinVersionResponse(default_qid, response_data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
response_obuffer->getData(),
response_obuffer->getLength(),
&response_data[0], response_data.size());
}
// Same type of test as builtInQueryViaDNSServer but for an error response.
TEST_F(AuthSrvTest, iqueryViaDNSServer) {
createDataFromFile("iquery_fromWire.wire");
(*server.getDNSLookupProvider())(*io_message, parse_message,
response_obuffer, &dnsserv);
(*server.getDNSAnswerProvider())(*io_message, parse_message,
response_obuffer);
UnitTestUtil::readWireData("iquery_response_fromWire.wire",
response_data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
response_obuffer->getData(),
response_obuffer->getLength(),
&response_data[0], response_data.size());
}
// Unsupported requests. Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
UNSUPPORTED_REQUEST_TEST;
......
......@@ -29,6 +29,8 @@ using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::auth;
namespace {
RRsetPtr a_rrset = RRsetPtr(new RRset(Name("www.example.com"),
RRClass::IN(), RRType::A(),
RRTTL(3600)));
......@@ -44,7 +46,9 @@ RRsetPtr glue_aaaa_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
RRsetPtr noglue_a_rrset(RRsetPtr(new RRset(Name("noglue.example.com"),
RRClass::IN(), RRType::A(),
RRTTL(3600))));
namespace {
RRsetPtr soa_rrset = RRsetPtr(new RRset(Name("example.com"),
RRClass::IN(), RRType::SOA(),
RRTTL(3600)));
// This is a mock Zone class for testing.
// It is a derived class of Zone, and simply hardcode the results of find()
// return SUCCESS for "www.example.com",
......@@ -54,14 +58,15 @@ namespace {
// otherwise return DNAME
class MockZone : public Zone {
public:
MockZone() :
MockZone(bool has_SOA = true) :
origin_(Name("example.com")),
has_SOA_(has_SOA),
delegation_rrset(RRsetPtr(new RRset(Name("delegation.example.com"),
RRClass::IN(), RRType