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

Reply via email to