Commit 8a2ab2b9 authored by Evan Hunt's avatar Evan Hunt

3150. [func] Improved startup and reconfiguration time by

			enabling zones to load in multiple threads. [RT #25333]
parent 541dd4d8
3150. [func] Improved startup and reconfiguration time by
enabling zones to load in multiple threads. [RT #25333]
3149. [placeholder]
3148. [bug] Processing of normal queries could be stalled when
forwarding a UPDATE message. [RT #24711]
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.c,v 1.617 2011/08/30 05:16:10 marka Exp $ */
/* $Id: server.c,v 1.618 2011/09/02 21:15:30 each Exp $ */
/*! \file */
......@@ -39,6 +39,7 @@
#include <isc/parseint.h>
#include <isc/portset.h>
#include <isc/print.h>
#include <isc/refcount.h>
#include <isc/resource.h>
#include <isc/sha2.h>
#include <isc/socket.h>
......@@ -215,6 +216,16 @@ struct cfg_context {
cfg_aclconfctx_t * actx;
};
/*%
* Holds state information for the initial zone loading process.
* Uses the isc_refcount structure to count the number of views
* with pending zone loads, dereferencing as each view finishes.
*/
typedef struct {
ns_server_t *server;
isc_refcount_t refs;
} ns_zoneload_t;
/*
* These zones should not leak onto the Internet.
*/
......@@ -5216,34 +5227,87 @@ load_configuration(const char *filename, ns_server_t *server,
}
static isc_result_t
load_zones(ns_server_t *server, isc_boolean_t stop) {
view_loaded(void *arg) {
isc_result_t result;
ns_zoneload_t *zl = (ns_zoneload_t *) arg;
ns_server_t *server = zl->server;
unsigned int refs;
/*
* Force zone maintenance. Do this after loading
* so that we know when we need to force AXFR of
* slave zones whose master files are missing.
*
* We use the zoneload reference counter to let us
* know when all views are finished.
*/
isc_refcount_decrement(&zl->refs, &refs);
if (refs != 0)
return (ISC_R_SUCCESS);
isc_refcount_destroy(&zl->refs);
isc_mem_put(server->mctx, zl, sizeof (*zl));
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_NOTICE, "all zones loaded");
CHECKFATAL(dns_zonemgr_forcemaint(server->zonemgr),
"forcing zone maintenance");
ns_os_started();
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_NOTICE, "running");
return (ISC_R_SUCCESS);
}
static isc_result_t
load_zones(ns_server_t *server) {
isc_result_t result;
dns_view_t *view;
ns_zoneload_t *zl;
unsigned int refs = 0;
zl = isc_mem_get(server->mctx, sizeof (*zl));
if (zl == NULL)
return (ISC_R_NOMEMORY);
zl->server = server;
result = isc_task_beginexclusive(server->task);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
isc_refcount_init(&zl->refs, 1);
/*
* Load zone data from disk.
* Schedule zones to be loaded from disk.
*/
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link))
{
CHECK(dns_view_load(view, stop));
if (view->managed_keys != NULL)
CHECK(dns_zone_load(view->managed_keys));
if (view->redirect != NULL)
CHECK(dns_zone_load(view->redirect));
isc_refcount_increment(&zl->refs, NULL);
CHECK(dns_view_asyncload(view, view_loaded, zl));
}
/*
* Force zone maintenance. Do this after loading
* so that we know when we need to force AXFR of
* slave zones whose master files are missing.
*/
CHECK(dns_zonemgr_forcemaint(server->zonemgr));
cleanup:
isc_refcount_decrement(&zl->refs, &refs);
if (result != ISC_R_SUCCESS || refs == 0) {
isc_refcount_destroy(&zl->refs);
isc_mem_put(server->mctx, zl, sizeof (*zl));
} else {
/*
* Place the task manager into privileged mode. This
* ensures that after we leave task-exclusive mode, no
* other tasks will be able to run except for the ones
* that are loading zones.
*/
isc_taskmgr_setmode(ns_g_taskmgr, isc_taskmgrmode_privileged);
}
isc_task_endexclusive(server->task);
return (result);
}
......@@ -5331,11 +5395,7 @@ run_server(isc_task_t *task, isc_event_t *event) {
isc_hash_init();
CHECKFATAL(load_zones(server, ISC_FALSE), "loading zones");
ns_os_started();
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
ISC_LOG_NOTICE, "running");
CHECKFATAL(load_zones(server), "loading zones");
}
void
......@@ -5770,7 +5830,7 @@ reload(ns_server_t *server) {
isc_result_t result;
CHECK(loadconfig(server));
result = load_zones(server, ISC_FALSE);
result = load_zones(server);
if (result == ISC_R_SUCCESS)
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
......
These scripts generate a named.conf file with an arbitrary number of
small zones, for testing startup performance.
To generate a test server with 1000 zones, run:
To generate a test server with 1000 zones each of which contains 5 A
records, run:
$ sh setup.sh 1000 > named.conf
$ sh setup.sh 1000 5 > named.conf
Zones are generated with random names, and all of them load from the same
file: smallzone.db.
Zones are generated with random names, and the zone files are created
in the subdirectory "zones".
Or, to generate a test server with 100 zones which all load from the same
generic file (smallzone.db):
$ sh setup.sh -s 100 > named.conf
The "number of records" argument is ignored if -s is used.
#!/bin/sh
rm -rf zones
rm -f named.conf
......@@ -14,17 +14,19 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: makenames.pl,v 1.2 2011/07/06 05:05:51 each Exp $
# $Id: makenames.pl,v 1.3 2011/09/02 21:15:35 each Exp $
use strict;
die "Usage: makenames.pl <num>" if (@ARGV == 0);
die "Usage: makenames.pl <num> [<len>]" if (@ARGV == 0 || @ARGV > 2);
my $len = 10;
$len = @ARGV[1] if (@ARGV == 2);
my @chars = split("", "abcdefghijklmnopqrstuvwxyz123456789");
srand;
for (my $i = 0; $i < @ARGV[0]; $i++) {
my $name = "";
for (my $j = 0; $j < 10; $j++) {
for (my $j = 0; $j < $len; $j++) {
my $r = rand 35;
$name .= $chars[$r];
}
......
#!/usr/bin/perl
#
# Copyright (C) 2011 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: mkzonefile.pl,v 1.2 2011/09/02 21:15:35 each Exp $
use strict;
die "Usage: makenames.pl zonename num_records" if (@ARGV != 2);
my $zname = @ARGV[0];
my $nrecords = @ARGV[1];
my @chars = split("", "abcdefghijklmnopqrstuvwxyz");
print"\$TTL 300 ; 5 minutes
\$ORIGIN $zname.
@ IN SOA mname1. . (
2011080201 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
600 ; minimum (1 hour)
)
NS ns
ns A 10.53.0.3\n";
srand;
for (my $i = 0; $i < $nrecords; $i++) {
my $name = "";
for (my $j = 0; $j < 8; $j++) {
my $r = rand 25;
$name .= $chars[$r];
}
print "$name" . "\tIN\tA\t";
my $x = int rand 254;
my $y = int rand 254;
my $z = int rand 254;
print "10.$x.$y.$z\n";
}
......@@ -14,13 +14,30 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: setup.sh,v 1.3 2011/07/07 23:47:49 tbox Exp $
# $Id: setup.sh,v 1.4 2011/09/02 21:15:35 each Exp $
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <number of zones>"
usage () {
echo "Usage: $0 [-s] <number of zones> [<records per zone>]"
echo " -s: use the same zone file all zones"
exit 1
}
if [ "$#" -lt 1 -o "$#" -gt 3 ]; then
usage
fi
single_file=""
if [ $1 = "-s" ]; then
single_file=yes
shift
fi
nzones=$1
shift
nrecords=5
[ "$#" -eq 1 ] && nrecords=$1
. ../system/conf.sh
cat << EOF
......@@ -59,6 +76,12 @@ logging {
EOF
$PERL makenames.pl $1 | while read zonename; do
$PERL makenames.pl $nzones | while read zonename; do
if [ $single_file ]; then
echo "zone $zonename { type master; file \"smallzone.db\"; };"
else
[ -d zones ] || mkdir zones
$PERL mkzonefile.pl $zonename $nrecords > zones/$zonename.db
echo "zone $zonename { type master; file \"zones/$zonename.db\"; };"
fi
done
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: events.h,v 1.58 2011/08/30 23:46:53 tbox Exp $ */
/* $Id: events.h,v 1.59 2011/09/02 21:15:36 each Exp $ */
#ifndef DNS_EVENTS_H
#define DNS_EVENTS_H 1
......@@ -76,6 +76,7 @@
#define DNS_EVENT_ADBGROWNAMES (ISC_EVENTCLASS_DNS + 46)
#define DNS_EVENT_ZONESECURESERIAL (ISC_EVENTCLASS_DNS + 47)
#define DNS_EVENT_ZONESECUREDB (ISC_EVENTCLASS_DNS + 48)
#define DNS_EVENT_ZONELOAD (ISC_EVENTCLASS_DNS + 49)
#define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0)
#define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535)
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: view.h,v 1.134 2011/08/02 20:36:13 each Exp $ */
/* $Id: view.h,v 1.135 2011/09/02 21:15:36 each Exp $ */
#ifndef DNS_VIEW_H
#define DNS_VIEW_H 1
......@@ -76,6 +76,7 @@
#include <dns/rdatastruct.h>
#include <dns/rpz.h>
#include <dns/types.h>
#include <dns/zt.h>
ISC_LANG_BEGINDECLS
......@@ -728,14 +729,21 @@ dns_view_load(dns_view_t *view, isc_boolean_t stop);
isc_result_t
dns_view_loadnew(dns_view_t *view, isc_boolean_t stop);
isc_result_t
dns_view_asyncload(dns_view_t *view, dns_zt_allloaded_t callback, void *arg);
/*%<
* Load zones attached to this view. dns_view_load() loads
* all zones whose master file has changed since the last
* load; dns_view_loadnew() loads only zones that have never
* been loaded.
*
* dns_view_asyncload() loads zones asynchronously. When all zones
* in the view have finished loading, 'callback' is called with argument
* 'arg' to inform the caller.
*
* If 'stop' is ISC_TRUE, stop on the first error and return it.
* If 'stop' is ISC_FALSE, ignore errors.
* If 'stop' is ISC_FALSE (or we are loading asynchronously), ignore errors.
*
* Requires:
*
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: zone.h,v 1.192 2011/08/30 05:16:15 marka Exp $ */
/* $Id: zone.h,v 1.193 2011/09/02 21:15:36 each Exp $ */
#ifndef DNS_ZONE_H
#define DNS_ZONE_H 1
......@@ -35,6 +35,7 @@
#include <dns/masterdump.h>
#include <dns/rdatastruct.h>
#include <dns/types.h>
#include <dns/zt.h>
typedef enum {
dns_zone_none,
......@@ -287,6 +288,7 @@ dns_zone_loadnew(dns_zone_t *zone);
isc_result_t
dns_zone_loadandthaw(dns_zone_t *zone);
/*%<
* Cause the database to be loaded from its backing store.
* Confirm that the minimum requirements for the zone type are
......@@ -311,6 +313,25 @@ dns_zone_loadandthaw(dns_zone_t *zone);
*\li Any result value from dns_db_load().
*/
isc_result_t
dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg);
/*%<
* Cause the database to be loaded from its backing store asynchronously.
* Other zone maintenance functions are suspended until this is complete.
* When finished, 'done' is called to inform the caller, with 'arg' as
* its first argument and 'zone' as its second. (Normally, 'arg' is
* expected to point to the zone table but is left undefined for testing
* purposes.)
*/
isc_boolean_t
dns__zone_loadpending(dns_zone_t *zone);
/*%<
* Indicates whether the zone is waiting to be loaded asynchronously.
* (Not currently intended for use outside of this module and associated
* tests.)
*/
void
dns_zone_attach(dns_zone_t *source, dns_zone_t **target);
/*%<
......@@ -1427,6 +1448,14 @@ dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr);
* earliest convenience.
*/
void
dns__zonemgr_run(isc_task_t *task, isc_event_t *event);
/*%<
* Event handler to call dns_zonemgr_forcemaint(); used to start
* zone operations from a unit test. Not intended for use outside
* libdns or related tests.
*/
void
dns_zonemgr_resumexfrs(dns_zonemgr_t *zmgr);
/*%<
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: zt.h,v 1.38 2007/06/19 23:47:17 tbox Exp $ */
/* $Id: zt.h,v 1.39 2011/09/02 21:15:37 each Exp $ */
#ifndef DNS_ZT_H
#define DNS_ZT_H 1
......@@ -30,6 +30,21 @@
ISC_LANG_BEGINDECLS
typedef isc_result_t
(*dns_zt_allloaded_t)(void *arg);
/*%<
* Method prototype: when all pending zone loads are complete,
* the zone table can inform the caller via a callback function with
* this signature.
*/
typedef isc_result_t
(*dns_zt_zoneloaded_t)(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
/*%<
* Method prototype: when a zone finishes loading, the zt object
* can be informed via a callback function with this signature.
*/
isc_result_t
dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **zt);
/*%<
......@@ -134,6 +149,9 @@ dns_zt_load(dns_zt_t *zt, isc_boolean_t stop);
isc_result_t
dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop);
isc_result_t
dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg);
/*%<
* Load all zones in the table. If 'stop' is ISC_TRUE,
* stop on the first error and return it. If 'stop'
......@@ -142,6 +160,10 @@ dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop);
* dns_zt_loadnew() only loads zones that are not yet loaded.
* dns_zt_load() also loads zones that are already loaded and
* and whose master file has changed since the last load.
* dns_zt_asyncload() loads zones asynchronously; when all
* zones in the zone table have finished loaded (or failed due
* to errors), the caller is informed by calling 'alldone'
* with an argument of 'arg'.
*
* Requires:
* \li 'zt' to be valid
......@@ -178,6 +200,16 @@ dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub,
* any error code from 'action'.
*/
isc_boolean_t
dns_zt_loadspending(dns_zt_t *zt);
/*%<
* Returns ISC_TRUE if and only if there are zones still waiting to
* be loaded in zone table 'zt'.
*
* Requires:
* \li 'zt' to be valid.
*/
ISC_LANG_ENDDECLS
#endif /* DNS_ZT_H */
......@@ -12,7 +12,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: Makefile.in,v 1.7 2011/08/23 01:29:38 each Exp $
# $Id: Makefile.in,v 1.8 2011/09/02 21:15:37 each Exp $
srcdir = @srcdir@
VPATH = @srcdir@
......@@ -37,12 +37,12 @@ DNSDEPLIBS = ../libdns.@A@
LIBS = @LIBS@ @ATFLIBS@
OBJS = dnstest.@O@
SRCS = dnstest.c master_test.c time_test.c update_test.c \
zonemgr_test.c dbiterator_test.c
SRCS = dnstest.c master_test.c dbiterator_test.c time_test.c \
update_test.c zonemgr_test.c zt_test.c
SUBDIRS =
TARGETS = master_test@EXEEXT@ time_test@EXEEXT@ update_test@EXEEXT@ \
zonemgr_test@EXEEXT@ dbiterator_test@EXEEXT@
TARGETS = master_test@EXEEXT@ dbiterator_test@EXEEXT@ time_test@EXEEXT@ \
update_test@EXEEXT@ zonemgr_test@EXEEXT@ zt_test@EXEEXT@
@BIND9_MAKE_RULES@
......@@ -71,6 +71,11 @@ dbiterator_test@EXEEXT@: dbiterator_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPL
dbiterator_test.@O@ dnstest.@O@ ${DNSLIBS} \
${ISCLIBS} ${LIBS}
zt_test@EXEEXT@: zt_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
zt_test.@O@ dnstest.@O@ ${DNSLIBS} \
${ISCLIBS} ${LIBS}
unit::
sh ${top_srcdir}/unit/unittest.sh
......
......@@ -14,12 +14,14 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnstest.c,v 1.4 2011/07/06 01:36:32 each Exp $ */
/* $Id: dnstest.c,v 1.5 2011/09/02 21:15:37 each Exp $ */
/*! \file */
#include <config.h>
#include <unistd.h>
#include <isc/app.h>
#include <isc/buffer.h>
#include <isc/entropy.h>
......@@ -32,9 +34,12 @@
#include <isc/timer.h>
#include <isc/util.h>
#include <dns/fixedname.h>
#include <dns/log.h>
#include <dns/name.h>
#include <dns/result.h>
#include <dns/view.h>
#include <dns/zone.h>
#include <dst/dst.h>
......@@ -44,8 +49,11 @@ isc_mem_t *mctx = NULL;
isc_entropy_t *ectx = NULL;
isc_log_t *lctx = NULL;
isc_taskmgr_t *taskmgr = NULL;
isc_task_t *maintask = NULL;
isc_timermgr_t *timermgr = NULL;
isc_socketmgr_t *socketmgr = NULL;
dns_zonemgr_t *zonemgr = NULL;
isc_boolean_t app_running = ISC_FALSE;
int ncpus;
static isc_boolean_t hash_active = ISC_FALSE, dst_active = ISC_FALSE;
......@@ -67,8 +75,12 @@ static isc_logcategory_t categories[] = {
static void
cleanup_managers() {
if (app_running)
isc_app_finish();
if (socketmgr != NULL)
isc_socketmgr_destroy(&socketmgr);
if (maintask != NULL)
isc_task_destroy(&maintask);
if (taskmgr != NULL)
isc_taskmgr_destroy(&taskmgr);
if (timermgr != NULL)
......@@ -87,6 +99,8 @@ create_managers() {
CHECK(isc_taskmgr_create(mctx, ncpus, 0, &taskmgr));
CHECK(isc_timermgr_create(mctx, &timermgr));
CHECK(isc_socketmgr_create(mctx, &socketmgr));
CHECK(isc_task_create(taskmgr, 0, &maintask));
CHECK(isc_app_start());
return (ISC_R_SUCCESS);
cleanup:
......@@ -134,6 +148,14 @@ dns_test_begin(FILE *logfile, isc_boolean_t start_managers) {
if (start_managers)
CHECK(create_managers());
/*
* atf-run changes us to a /tmp directory, so tests
* that access test data files must first chdir to the proper
* location.
*/
if (chdir(TESTS) == -1)
CHECK(ISC_R_FAILURE);
return (ISC_R_SUCCESS);
cleanup:
......@@ -162,3 +184,114 @@ dns_test_end() {
isc_mem_destroy(&mctx);
}
/*
* Create a zone with origin 'name', return a pointer to the zone object in
* 'zonep'. If 'view' is set, add the zone to that view; otherwise, create
* a new view for the purpose.
*
* If the created view is going to be needed by the caller subsequently,
* then 'keepview' should be set to true; this will prevent the view
* from being detached. In this case, the caller is responsible for
* detaching the view.
*/
isc_result_t
dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view,
isc_boolean_t keepview)
{
isc_result_t result;
dns_zone_t *zone = NULL;
isc_buffer_t buffer;
dns_fixedname_t fixorigin;