]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
work on prefetch: store the updated results in the cache.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 8 Jan 2010 15:59:36 +0000 (15:59 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 8 Jan 2010 15:59:36 +0000 (15:59 +0000)
git-svn-id: file:///svn/unbound/trunk@1954 be551aaa-1e26-0410-a405-d3ace91eadb9

14 files changed:
daemon/cachedump.c
daemon/worker.c
doc/Changelog
iterator/iter_utils.c
iterator/iter_utils.h
iterator/iterator.c
pythonmod/pythonmod_utils.c
services/cache/dns.c
services/cache/dns.h
services/mesh.c
services/mesh.h
testdata/iter_prefetch.rpl [new file with mode: 0644]
util/module.h
validator/validator.c

index 32ae5236cc24b4ad192267c062f0911cddc1d5f6..3e8b124ab3c319efe741abb35f5b2e095f35cef5 100644 (file)
@@ -758,7 +758,7 @@ load_msg(SSL* ssl, ldns_buffer* buf, struct worker* worker)
        if(!go_on) 
                return 1; /* skip this one, not all references satisfied */
 
-       if(!dns_cache_store(&worker->env, &qinf, &rep, 0)) {
+       if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0)) {
                log_warn("error out of memory");
                return 0;
        }
index 7b2d6216332eaa46daf8a85a966b30b43d6b8603..053c6e73ea32a6c3b86cab070822af78b04c4a67 100644 (file)
 /** Size of an UDP datagram */
 #define NORMAL_UDP_SIZE        512 /* bytes */
 
+/** 
+ * seconds to add to prefetch leeway.  This is a TTL that expires old rrsets
+ * earlier than they should in order to put the new update into the cache.
+ * This additional value is to make sure that if not all TTLs are equal in
+ * the message to be updated(and replaced), that rrsets with up to this much
+ * extra TTL are also replaced.  This means that the resulting new message
+ * will have (most likely) this TTL at least, avoiding very small 'split
+ * second' TTLs due to operators choosing relative primes for TTLs (or so).
+ * Also has to be at least one to break ties (and overwrite cached entry).
+ */
+#define PREFETCH_EXPIRY_ADD 60
+
 #ifdef UNBOUND_ALLOC_STATS
 /** measure memory leakage */
 static void
@@ -592,7 +604,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
 /** Reply to client and perform prefetch to keep cache up to date */
 static void
 reply_and_prefetch(struct worker* worker, struct query_info* qinfo, 
-       uint16_t flags, struct comm_reply* repinfo)
+       uint16_t flags, struct comm_reply* repinfo, uint32_t leeway)
 {
        /* first send answer to client to keep its latency 
         * as small as a cachereply */
@@ -603,7 +615,8 @@ reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
         * client addrs waiting, which has the cache blacklisted (to bypass
         * the cache and go to the network for the data). */
        /* this (potentially) runs the mesh for the new query */
-       mesh_new_prefetch(worker->env.mesh, qinfo, flags);
+       mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + 
+               PREFETCH_EXPIRY_ADD);
 }
 
 /**
@@ -856,10 +869,12 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
                        /* prefetch it if the prefetch TTL expired */
                        if(worker->env.cfg->prefetch && *worker->env.now >=
                                ((struct reply_info*)e->data)->prefetch_ttl) {
+                               uint32_t leeway = ((struct reply_info*)e->
+                                       data)->ttl - *worker->env.now;
                                lock_rw_unlock(&e->lock);
                                reply_and_prefetch(worker, &qinfo, 
                                        ldns_buffer_read_u16_at(c->buffer, 2),
-                                       repinfo);
+                                       repinfo, leeway);
                                return 0;
                        }
                        lock_rw_unlock(&e->lock);
index b49de1d80145e78fdfcc2fc262bde304d40ca793..2979bd302fe98f5bb376a72e9a682e0ed06f8a84 100644 (file)
@@ -5,6 +5,9 @@
          is fixed to no longer block lookup of child side information and
          the iterator is fixed to no longer attempt to get ipv6 when it is
          not enabled and then give up in failure.
+       - test and fixes to make prefetch actually store the answer in the
+         cache.  Considers some rrsets 'already expired' but does not allow
+         overwriting of rrsets considered more secure.
 
 7 January 2010: Wouter
        - Fixup python documentation (thanks Leo Vandewoestijne).
index 62c92cbeb1d096182d26327eb2b4c2c37f8250d2..b5364b615cd893481d8b8f05abe952d59bbe42cb 100644 (file)
@@ -403,9 +403,9 @@ dns_copy_msg(struct dns_msg* from, struct regional* region)
 
 int 
 iter_dns_store(struct module_env* env, struct query_info* msgqinf,
-       struct reply_info* msgrep, int is_referral)
+       struct reply_info* msgrep, int is_referral, uint32_t leeway)
 {
-       return dns_cache_store(env, msgqinf, msgrep, is_referral);
+       return dns_cache_store(env, msgqinf, msgrep, is_referral, leeway);
 }
 
 int 
index e174d341a17b1200947662e4b494250fd89d11a2..747b9a64339a7a52d97b4d303e81c2c7069c25a0 100644 (file)
@@ -118,10 +118,11 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
  * @param rep: reply in dns_msg from dns_alloc_msg for example.
  * @param is_referral: If true, then the given message to be stored is a
  *     referral. The cache implementation may use this as a hint.
+ * @param leeway: prefetch TTL leeway to expire old rrsets quicker.
  * @return 0 on alloc error (out of memory).
  */
 int iter_dns_store(struct module_env* env, struct query_info* qinf,
-       struct reply_info* rep, int is_referral);
+       struct reply_info* rep, int is_referral, uint32_t leeway);
 
 /**
  * Select randomly with n/m probability.
index 3c2cf8bd876d19370d8aad3b21f38e90334522de..d65192e0a0fc2f36f67e52ac33c350ec92879e04 100644 (file)
@@ -251,7 +251,7 @@ error_response_cache(struct module_qstate* qstate, int id, int rcode)
        /* do not waste time trying to validate this servfail */
        err.security = sec_status_indeterminate;
        verbose(VERB_ALGO, "store error response in message cache");
-       if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0)) {
+       if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0)) {
                log_err("error_response_cache: could not store error (nomem)");
        }
        return error_response(qstate, id, rcode);
@@ -1552,7 +1552,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                 * so they sent on their */
                verbose(VERB_DETAIL, "query response was ANSWER");
                if(!iter_dns_store(qstate->env, &iq->response->qinfo,
-                       iq->response->rep, 0))
+                       iq->response->rep, 0, qstate->prefetch_leeway))
                        return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
                /* close down outstanding requests to be discarded */
                outbound_list_clear(&iq->outlist);
@@ -1589,8 +1589,9 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                          )
                    )) {
                        /* Store the referral under the current query */
+                       /* no prefetch-leeway, since its not the answer */
                        if(!iter_dns_store(qstate->env, &iq->response->qinfo,
-                               iq->response->rep, 1))
+                               iq->response->rep, 1, 0))
                                return error_response(qstate, id, 
                                        LDNS_RCODE_SERVFAIL);
                        if(qstate->env->neg_cache)
@@ -1657,8 +1658,9 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                /* cache the CNAME response under the current query */
                /* NOTE : set referral=1, so that rrsets get stored but not 
                 * the partial query answer (CNAME only). */
+               /* prefetchleeway applied because this updates answer parts */
                if(!iter_dns_store(qstate->env, &iq->response->qinfo,
-                       iq->response->rep, 1))
+                       iq->response->rep, 1, qstate->prefetch_leeway))
                        return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
                /* set the current request's qname to the new value. */
                iq->qchase.qname = sname;
@@ -2114,7 +2116,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
                 * from cache does not need to be stored in the msg cache. */
                if(qstate->query_flags&BIT_RD) {
                        if(!iter_dns_store(qstate->env, &qstate->qinfo, 
-                               iq->response->rep, 0))
+                               iq->response->rep, 0, qstate->prefetch_leeway))
                                return error_response(qstate, id, 
                                        LDNS_RCODE_SERVFAIL);
                }
index 3f4d41447c82f67807b9b59e43a9d9156a2390a2..bd0ef3708a3f32a950887635df09c8de0f2ed509 100644 (file)
@@ -65,7 +65,8 @@ int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, st
        return 0;
     }
 
-    return dns_cache_store(qstate->env, qinfo, msgrep, is_referral);
+    return dns_cache_store(qstate->env, qinfo, msgrep, is_referral, 
+       qstate->prefetch_leeway);
 }
 
 /*  Invalidate the message associated with query_info stored in message cache */
index 70458cccf20bfae49d5b8ff049194a85973e0969..2651767e35869ed3c5c0016aeb7c3ceacd477413 100644 (file)
@@ -71,7 +71,7 @@ store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now)
 
 void 
 dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
-       hashvalue_t hash, struct reply_info* rep)
+       hashvalue_t hash, struct reply_info* rep, uint32_t leeway)
 {
        struct msgreply_entry* e;
        uint32_t ttl = rep->ttl;
@@ -84,7 +84,7 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
        }
        reply_info_sortref(rep);
        reply_info_set_ttls(rep, *env->now);
-       store_rrsets(env, rep, *env->now);
+       store_rrsets(env, rep, *env->now+leeway);
        if(ttl == 0) {
                /* we do not store the message, but we did store the RRs,
                 * which could be useful for delegation information */
@@ -714,7 +714,7 @@ dns_cache_lookup(struct module_env* env,
 
 int 
 dns_cache_store(struct module_env* env, struct query_info* msgqinf,
-        struct reply_info* msgrep, int is_referral)
+        struct reply_info* msgrep, int is_referral, uint32_t leeway)
 {
        struct reply_info* rep = NULL;
        /* alloc, malloc properly (not in region, like msg is) */
@@ -723,6 +723,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
                return 0;
        /* ttl must be relative ;i.e. 0..86400 not  time(0)+86400. 
         * the env->now is added to message and RRsets in this routine. */
+       /* the leeway is used to invalidate other rrsets earlier */
 
        if(is_referral) {
                /* store rrsets */
@@ -735,7 +736,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
                        ref.id = rep->rrsets[i]->id;
                        /*ignore ret: it was in the cache, ref updated */
                        (void)rrset_cache_update(env->rrset_cache, &ref, 
-                               env->alloc, *env->now);
+                               env->alloc, *env->now + leeway);
                }
                free(rep);
                return 1;
@@ -756,7 +757,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
                rep->flags |= (BIT_RA | BIT_QR);
                rep->flags &= ~(BIT_AA | BIT_CD);
                h = query_info_hash(&qinf);
-               dns_cache_store_msg(env, &qinf, h, rep);
+               dns_cache_store_msg(env, &qinf, h, rep, leeway);
                /* qname is used inside query_info_entrysetup, and set to 
                 * NULL. If it has not been used, free it. free(0) is safe. */
                free(qinf.qname);
index acd0e26789eb95518855070bd95d204b9cee9ba7..561e13d6434852aa6e2a3f2def7bab58e4303070 100644 (file)
@@ -72,10 +72,12 @@ struct dns_msg {
  * @param is_referral: If true, then the given message to be stored is a
  *      referral. The cache implementation may use this as a hint.
  *      It will store only the RRsets, not the message.
+ * @param leeway: TTL value, if not 0, other rrsets are considered expired
+ *     that many seconds before actual TTL expiry.
  * @return 0 on alloc error (out of memory).
  */
 int dns_cache_store(struct module_env* env, struct query_info* qinf,
-        struct reply_info* rep, int is_referral); 
+        struct reply_info* rep, int is_referral, uint32_t leeway); 
 
 /**
  * Store message in the cache. Stores in message cache and rrset cache.
@@ -88,9 +90,11 @@ int dns_cache_store(struct module_env* env, struct query_info* qinf,
  * @param hash: hash over qinfo.
  * @param rep: reply info, together with qinfo makes up the message.
  *     Adjusts the reply info TTLs to absolute time.
+ * @param leeway: TTL value, if not 0, other rrsets are considered expired
+ *     that many seconds before actual TTL expiry.
  */
 void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
-       hashvalue_t hash, struct reply_info* rep);
+       hashvalue_t hash, struct reply_info* rep, uint32_t leeway);
 
 /**
  * Find a delegation from the cache.
index 5f6f449f181b45a9ab41d0bd5300a57ea05fae28..6a5cc926f49d41f951cd044406c4f0afc80571c3 100644 (file)
@@ -389,7 +389,7 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
 }
 
 void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
-        uint16_t qflags)
+        uint16_t qflags, uint32_t leeway)
 {
        struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags, 0);
        struct rbnode_t* n;
@@ -399,6 +399,8 @@ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
                /* make it ignore the cache from now on */
                if(!s->s.blacklist)
                        sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region);
+               if(s->s.prefetch_leeway < leeway)
+                       s->s.prefetch_leeway = leeway;
                return;
        }
        if(!mesh_make_new_space(mesh)) {
@@ -417,6 +419,7 @@ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
        mesh->num_detached_states++;
        /* make it ignore the cache */
        sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region);
+       s->s.prefetch_leeway = leeway;
 
        if(s->list_select == mesh_no_list) {
                /* move to either the forever or the jostle_list */
@@ -493,6 +496,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
        mstate->s.return_rcode = LDNS_RCODE_NOERROR;
        mstate->s.env = env;
        mstate->s.mesh_info = mstate;
+       mstate->s.prefetch_leeway = 0;
        /* init modules */
        for(i=0; i<env->mesh->mods.num; i++) {
                mstate->s.minfo[i] = NULL;
index 2e79ab2f0baf403a4eb9f3a7eee51c27132f2147..3999d31f8e5bd7c0d604de70190315e49f26174e 100644 (file)
@@ -293,9 +293,10 @@ int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
  * @param mesh: the mesh.
  * @param qinfo: query from client.
  * @param qflags: flags from client query.
+ * @param leeway: TTL leeway what to expire earlier for this update.
  */
 void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
-       uint16_t qflags);
+       uint16_t qflags, uint32_t leeway);
 
 /**
  * Handle new event from the wire. A serviced query has returned.
diff --git a/testdata/iter_prefetch.rpl b/testdata/iter_prefetch.rpl
new file mode 100644 (file)
index 0000000..f2b63a2
--- /dev/null
@@ -0,0 +1,225 @@
+; config options
+server:
+       target-fetch-policy: "0 0 0 0 0"
+       prefetch: "yes"
+
+stub-zone:
+       name: "."
+       stub-addr: 193.0.14.129         # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test resolver prefetch of almost expired data
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+       ADDRESS 193.0.14.129 
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS        K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET.    IN      A       193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com.   IN NS   a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net.    IN      A       192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+       ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com.   IN NS   a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net.    IN      A       192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 40
+       ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com.   IN NS   ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A     10.20.30.40
+SECTION AUTHORITY
+example.com.   3600 IN NS      ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.        3600    IN      A       1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 50 100
+       ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com.   IN NS   ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.                IN      A       1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A     10.20.30.40
+SECTION AUTHORITY
+example.com.   3600 IN NS      ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.        3600    IN      A       1.2.3.4
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A     10.20.30.40
+SECTION AUTHORITY
+example.com.   3600 IN NS      ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.        3600    IN      A       1.2.3.4
+ENTRY_END
+
+; after 1800 secs still the cached answer
+STEP 20 TIME_PASSES ELAPSE 1800
+
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 1800 IN A     10.20.30.40
+SECTION AUTHORITY
+example.com.   1800 IN NS      ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.        1800    IN      A       1.2.3.4
+ENTRY_END
+
+; after 1440 we are 360 seconds before the expiry
+; (the authority changes behind the scenes to detect new lookup)
+STEP 50 TIME_PASSES ELAPSE 1440
+
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 360 IN A      10.20.30.40
+SECTION AUTHORITY
+example.com.   360 IN NS       ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.        360     IN      A       1.2.3.4
+ENTRY_END
+STEP 80 TRAFFIC
+; let traffic flow for prefetch to happen
+
+; above a cache reply with 10% of the original TTL
+; but the actual cache is changed, try to get that
+STEP 120 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 130 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A     10.20.30.40
+SECTION AUTHORITY
+example.com.   3600 IN NS      ns.example.com.
+SECTION ADDITIONAL
+ns.example.com.        3600    IN      A       1.2.3.4
+ENTRY_END
+
+SCENARIO_END
index b27e2d1bbaad393ccff4f836fddb6c951c68e200..d74a500c31e9841267f54567704e79f024fa1e66 100644 (file)
@@ -301,6 +301,8 @@ struct module_qstate {
        struct regional* region;
        /** failure reason information if val-log-level is high */
        struct config_strlist* errinf;
+       /** how many seconds before expiry is this prefetched (0 if not) */
+       uint32_t prefetch_leeway;
 
        /** which module is executing */
        int curmod;
index 27dc9a7b7af8d1242ed8fbffc924380da7e5176d..a735b9b1fa1a6df57d1e1664612e17284e87cfd5 100644 (file)
@@ -1937,13 +1937,14 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
        /* store results in cache */
        if(qstate->query_flags&BIT_RD) {
                if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, 
-                       vq->orig_msg->rep, 0)) {
+                       vq->orig_msg->rep, 0, qstate->prefetch_leeway)) {
                        log_err("out of memory caching validator results");
                }
        } else {
                /* for a referral, store the verified RRsets */
+               /* and this does not get prefetched, so no leeway */
                if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, 
-                       vq->orig_msg->rep, 1)) {
+                       vq->orig_msg->rep, 1, 0)) {
                        log_err("out of memory caching validator results");
                }
        }