On 2012-05-28, Imre Oolberg <[email protected]> wrote:
> Hi!
>
> I am having trouble on OpenBSD v. 5.1 using NSD nameserver.
>
> When slave NSD name server receives zone update and reloads it into its
> database high and sustained user load (about 1-2) is generated on cpu
> depending on hardware from 3 minutes to 10 minutes. Also this kind on
> load is observed when doing nsdc patch. It seems to happen only when
> zone has many RRs, say 100k NS lines; using NSD with OpenBSD v. 4.8 from
> packages does not have this issue, also not 5.1-current, but 5.1 does; i
> have tried and got similar results on amd64 and i386, happens on
> both; Normally this kind of reload and patch takes several seconds only.
> I would be very thankful if somebody could have a look at it and confirm
> this behaviour. And if really nsd on 5.1 is to blame may i add that
> patch would be very much welcomed! :)
This is due to a bug introduced with NSD 3.2.9 and fixed shortly afterwards.
The _untested_ diff against -stable below may fix it.
Index: difffile.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/difffile.c,v
retrieving revision 1.1.1.5
diff -u -p -r1.1.1.5 difffile.c
--- difffile.c 29 Jan 2012 11:15:31 -0000 1.1.1.5
+++ difffile.c 28 May 2012 20:10:31 -0000
@@ -261,14 +261,43 @@ has_data_below(domain_type* top)
/* in the canonical ordering subdomains are after this name */
d = domain_next(d);
while(d != NULL && dname_is_subdomain(domain_dname(d),
domain_dname(top))) {
- if(d->is_existing)
+ if(d->is_existing) {
return 1;
+ }
d = domain_next(d);
}
return 0;
}
-static void
+
+/* this routine makes empty terminals non-existent.
+ * @domain the lowest empty terminal
+ * @ce the closest encloser
+ */
+static domain_type*
+rrset_delete_empty_terminals(domain_type* domain, domain_type* ce)
+{
+ assert(domain);
+ if (domain->rrsets == 0) {
+ /* if there is no data below it, it becomes non existing.
+ also empty nonterminals above it become nonexisting */
+ /* check for data below this node. */
+ if(!has_data_below(domain)) {
+ /* nonexist this domain and all parent empty
nonterminals */
+ domain_type* p = domain;
+ while(p != NULL && p->rrsets == 0) {
+ if(p == ce || has_data_below(p))
+ return p;
+ p->is_existing = 0;
+ p = p->parent;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static domain_type*
rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset)
{
int i;
@@ -279,7 +308,7 @@ rrset_delete(namedb_type* db, domain_typ
}
if(!*pp) {
/* rrset does not exist for domain */
- return;
+ return NULL;
}
*pp = rrset->next;
@@ -320,23 +349,13 @@ rrset_delete(namedb_type* db, domain_typ
sizeof(rr_type) * rrset->rr_count);
region_recycle(db->region, rrset, sizeof(rrset_type));
+ rrset->rr_count = 0;
+
/* is the node now an empty node (completely deleted) */
- if(domain->rrsets == 0) {
- /* if there is no data below it, it becomes non existing.
- also empty nonterminals above it become nonexisting */
- /* check for data below this node. */
- if(!has_data_below(domain)) {
- /* nonexist this domain and all parent empty
nonterminals */
- domain_type* p = domain;
- while(p != NULL && p->rrsets == 0) {
- if(has_data_below(p))
- break;
- p->is_existing = 0;
- p = p->parent;
- }
- }
+ if (domain->rrsets == 0) {
+ return domain;
}
- rrset->rr_count = 0;
+ return NULL;
}
static int
@@ -384,6 +403,7 @@ find_rr_num(rrset_type* rrset,
static int
delete_RR(namedb_type* db, const dname_type* dname,
uint16_t type, uint16_t klass,
+ domain_type* prevdomain,
buffer_type* packet, size_t rdatalen, zone_type *zone,
region_type* temp_region, int is_axfr)
{
@@ -442,7 +462,11 @@ delete_RR(namedb_type* db, const dname_t
if(rrset->rr_count == 1) {
/* delete entire rrset */
- rrset_delete(db, domain, rrset);
+ domain = rrset_delete(db, domain, rrset);
+ if (domain && !domain->nextdiff) {
+ /* this domain is not yet in the diff chain */
+ prevdomain->nextdiff = domain;
+ }
} else {
/* swap out the bad RR and decrease the count */
rr_type* rrs_orig = rrset->rrs;
@@ -683,6 +707,8 @@ delete_zone_rrs(namedb_type* db, zone_ty
{
rrset_type *rrset;
domain_type *domain = zone->apex;
+ domain_type *ce = NULL;
+ domain_type *next = NULL;
zone->updated = 1;
#ifdef NSEC3
#ifndef FULL_PREHASH
@@ -698,10 +724,13 @@ delete_zone_rrs(namedb_type* db, zone_ty
dname_to_string(domain_dname(domain),0)));
/* delete all rrsets of the zone */
while((rrset = domain_find_any_rrset(domain, zone))) {
- rrset_delete(db, domain, rrset);
+ (void)rrset_delete(db, domain, rrset);
}
- domain = domain_next(domain);
+ next = domain_next(domain);
+ domain->nextdiff = next;
+ domain = next;
}
+
#ifdef NSEC3
#ifndef FULL_PREHASH
if (0 != zone_nsec3_domains_create(db, zone)) {
@@ -743,6 +772,7 @@ apply_ixfr(namedb_type* db, FILE *in, co
uint16_t rrlen;
const dname_type *dname_zone, *dname;
zone_type* zone_db;
+ domain_type* domain, *ce = NULL, *next = NULL;
char file_zone_name[3072];
uint32_t file_serial, file_seq_nr;
uint16_t file_id;
@@ -893,6 +923,7 @@ apply_ixfr(namedb_type* db, FILE *in, co
}
else counter = 0;
+ domain = zone_db->apex;
for(; counter < ancount; ++counter,++(*rr_count))
{
uint16_t type, klass;
@@ -985,11 +1016,14 @@ apply_ixfr(namedb_type* db, FILE *in, co
&& seq_nr == seq_total-1) {
continue; /* do not delete final SOA RR for
IXFR */
}
- if(!delete_RR(db, dname, type, klass, packet,
+ if(!delete_RR(db, dname, type, klass, domain, packet,
rrlen, zone_db, region, *is_axfr)) {
region_destroy(region);
return 0;
}
+ if (!*is_axfr && domain->nextdiff) {
+ domain = domain->nextdiff;
+ }
}
else
{
@@ -1001,6 +1035,17 @@ apply_ixfr(namedb_type* db, FILE *in, co
}
}
}
+ /* fix empty terminals */
+ domain = zone_db->apex;
+ while(domain && dname_is_subdomain(
+ domain_dname(domain), domain_dname(zone_db->apex)))
+ {
+ ce = rrset_delete_empty_terminals(domain, ce);
+ next = domain->nextdiff;
+ domain->nextdiff = NULL;
+ domain = next;
+ }
+
region_destroy(region);
return 1;
}
Index: namedb.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/namedb.c,v
retrieving revision 1.1.1.3
diff -u -p -r1.1.1.3 namedb.c
--- namedb.c 29 Jan 2012 11:15:37 -0000 1.1.1.3
+++ namedb.c 28 May 2012 20:10:31 -0000
@@ -37,6 +37,7 @@ allocate_domain_info(domain_table_type *
result->node.key = dname_partial_copy(
table->region, dname, domain_dname(parent)->label_count + 1);
result->parent = parent;
+ result->nextdiff = NULL;
result->wildcard_child_closest_match = result;
result->rrsets = NULL;
result->number = 0;
@@ -71,6 +72,7 @@ domain_table_create(region_type *region)
root = (domain_type *) region_alloc(region, sizeof(domain_type));
root->node.key = origin;
root->parent = NULL;
+ root->nextdiff = NULL;
root->wildcard_child_closest_match = root;
root->rrsets = NULL;
root->number = 1; /* 0 is used for after header */
Index: namedb.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/namedb.h,v
retrieving revision 1.1.1.4
diff -u -p -r1.1.1.4 namedb.h
--- namedb.h 29 Jan 2012 11:15:40 -0000 1.1.1.4
+++ namedb.h 28 May 2012 20:10:31 -0000
@@ -43,6 +43,7 @@ struct domain
{
rbnode_t node;
domain_type *parent;
+ domain_type *nextdiff;
domain_type *wildcard_child_closest_match;
rrset_type *rrsets;
#ifdef NSEC3