]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Remove TTL-based cleaning from the QPcache
authorOndřej Surý <ondrej@isc.org>
Wed, 14 Jan 2026 12:43:48 +0000 (13:43 +0100)
committerOndřej Surý <ondrej@isc.org>
Mon, 30 Mar 2026 19:46:44 +0000 (21:46 +0200)
The experiments show that the SIEVE-LRU based mechanism is good enough
as the only mechanism for cleaning up the expired entries from the
cache.

This simplifies the internal logic and memory usage of the cache.

The disadvantage is that the cache use will organically grow until it
hits the overmem cleaning mechanism.

The advantage is that the measurements show that BIND 9 is well behaved
even with 512 MB cache under heavy load.

lib/dns/include/dns/rdataslab.h
lib/dns/qpcache.c
lib/dns/rdataslab.c

index 5b7e8ffc76726a9f873200cb33f102f33ab4fe2c..40ccb47e68cd38470bb55f1c18fb3810cc24cee7 100644 (file)
@@ -45,7 +45,6 @@
 #include <stdbool.h>
 
 #include <isc/atomic.h>
-#include <isc/heap.h>
 #include <isc/stdtime.h>
 #include <isc/urcu.h>
 
@@ -97,10 +96,6 @@ struct dns_slabheader {
        isc_stdtime_t  expire;
        dns_typepair_t typepair;
 
-       /* TTL-cleaning (cache) */
-       unsigned int heap_index;
-       isc_heap_t  *heap;
-
        dns_slabheader_proof_t *noqname;
        dns_slabheader_proof_t *closest;
 
index db0d4f99777c89fa632eb1706c0c8b069cbc3a1f..e22857087e26cf9fc856cc96a0dd6a2e5aa717d2 100644 (file)
@@ -21,7 +21,6 @@
 #include <isc/async.h>
 #include <isc/atomic.h>
 #include <isc/file.h>
-#include <isc/heap.h>
 #include <isc/hex.h>
 #include <isc/list.h>
 #include <isc/log.h>
 
 #define HEADERNODE(h) ((qpcnode_t *)((h)->node))
 
-/*
- * Allow clients with a virtual time of up to 10 seconds in the past to see
- * records that would have otherwise have expired.
- */
-#define QPDB_VIRTUAL 10
-
-/*
- * This defines the number of headers that we try to expire each time the
- * expire_ttl_headers() is run.  The number should be small enough, so the
- * TTL-based header expiration doesn't take too long, but it should be large
- * enough, so we expire enough headers if their TTL is clustered.
- */
-#define DNS_QPDB_EXPIRE_TTL_COUNT 10
-
 /*%
  * Forward declarations
  */
@@ -179,20 +164,12 @@ typedef struct qpcache_bucket {
        /* Per-bucket lock. */
        isc_rwlock_t lock;
 
-       /*
-        * The heap is used for TTL based expiry.  Note that qpcache->hmctx
-        * is the memory context to use for heap memory; this differs from
-        * the main database memory context, which is qpcache->common.mctx.
-        */
-       isc_heap_t *heap;
-
        /* SIEVE-LRU cache cleaning state. */
        ISC_SIEVE(dns_slabtop_t) sieve;
 
        /* Padding to prevent false sharing between locks. */
        uint8_t __padding[ISC_OS_CACHELINE_SIZE -
                          (sizeof(isc_queue_t) + sizeof(isc_rwlock_t) +
-                          sizeof(isc_heap_t *) +
                           sizeof(ISC_SIEVE(dns_slabtop_t))) %
                                  ISC_OS_CACHELINE_SIZE];
 
@@ -518,9 +495,6 @@ qpcache_miss(qpcache_t *qpdb, dns_slabheader_t *newheader,
             isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) {
        uint32_t idx = HEADERNODE(newheader)->locknum;
 
-       isc_heap_insert(qpdb->buckets[idx].heap, newheader);
-       newheader->heap = qpdb->buckets[idx].heap;
-
        if (isc_mem_isovermem(qpdb->common.mctx)) {
                /*
                 * Maximum estimated size of the data being added: The size
@@ -908,23 +882,7 @@ mark(dns_slabheader_t *header, uint_least16_t flag) {
 
 static void
 setttl(dns_slabheader_t *header, isc_stdtime_t newts) {
-       isc_stdtime_t oldts = header->expire;
-
        header->expire = newts;
-
-       if (header->heap == NULL || header->heap_index == 0 || newts == oldts) {
-               return;
-       }
-
-       if (newts < oldts) {
-               isc_heap_increased(header->heap, header->heap_index);
-       } else {
-               isc_heap_decreased(header->heap, header->heap_index);
-       }
-
-       if (newts == 0) {
-               isc_heap_delete(header->heap, header->heap_index);
-       }
 }
 
 static void
@@ -2056,28 +2014,6 @@ qpcnode_expiredata(dns_dbnode_t *node, void *data) {
        INSIST(tlocktype == isc_rwlocktype_none);
 }
 
-/*%
- * These functions allow the heap code to rank the priority of each
- * element.  It returns true if v1 happens "sooner" than v2.
- */
-static bool
-ttl_sooner(void *v1, void *v2) {
-       dns_slabheader_t *h1 = v1;
-       dns_slabheader_t *h2 = v2;
-
-       return h1->expire < h2->expire;
-}
-
-/*%
- * This function sets the heap index into the header.
- */
-static void
-set_index(void *what, unsigned int idx) {
-       dns_slabheader_t *h = what;
-
-       h->heap_index = idx;
-}
-
 static void
 qpcache__destroy(qpcache_t *qpdb) {
        unsigned int i;
@@ -2104,8 +2040,6 @@ qpcache__destroy(qpcache_t *qpdb) {
 
                INSIST(isc_queue_empty(&qpdb->buckets[i].deadnodes));
                isc_queue_destroy(&qpdb->buckets[i].deadnodes);
-
-               isc_heap_destroy(&qpdb->buckets[i].heap);
        }
 
        dns_stats_detach(&qpdb->rrsetstats);
@@ -2829,11 +2763,6 @@ cleanup:
        return result;
 }
 
-static void
-expire_ttl_headers(qpcache_t *qpdb, unsigned int locknum,
-                  isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep,
-                  isc_stdtime_t now DNS__DB_FLARG);
-
 static isc_result_t
 qpcache_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                    isc_stdtime_t __now, dns_rdataset_t *rdataset,
@@ -2925,9 +2854,6 @@ qpcache_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
 
        NODE_WRLOCK(nlock, &nlocktype);
 
-       expire_ttl_headers(qpdb, qpnode->locknum, &nlocktype, &tlocktype,
-                          now DNS__DB_FLARG_PASS);
-
        if (newnsec && !qpnode->havensec) {
                qpcnode_t *nsecnode = NULL;
 
@@ -3075,10 +3001,6 @@ dns__qpcache_create(isc_mem_t *mctx, const dns_name_t *origin,
        for (i = 0; i < (int)qpdb->buckets_count; i++) {
                ISC_SIEVE_INIT(qpdb->buckets[i].sieve);
 
-               qpdb->buckets[i].heap = NULL;
-               isc_heap_create(hmctx, ttl_sooner, set_index, 0,
-                               &qpdb->buckets[i].heap);
-
                isc_queue_init(&qpdb->buckets[i].deadnodes);
 
                NODE_INITLOCK(&qpdb->buckets[i].lock);
@@ -3550,10 +3472,6 @@ qpcnode_deletedata(dns_dbnode_t *node ISC_ATTR_UNUSED, void *data) {
                ISC_LIST_UNLINK(HEADERNODE(header)->dirty, header, dirtylink);
        }
 
-       if (header->heap != NULL && header->heap_index != 0) {
-               isc_heap_delete(header->heap, header->heap_index);
-       }
-
        /*
         * This place is the only place where we actually need header->typepair.
         */
@@ -3568,40 +3486,6 @@ qpcnode_deletedata(dns_dbnode_t *node ISC_ATTR_UNUSED, void *data) {
        }
 }
 
-/*
- * Caller must be holding the node write lock.
- */
-static void
-expire_ttl_headers(qpcache_t *qpdb, unsigned int locknum,
-                  isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep,
-                  isc_stdtime_t now DNS__DB_FLARG) {
-       isc_heap_t *heap = qpdb->buckets[locknum].heap;
-
-       for (size_t i = 0; i < DNS_QPDB_EXPIRE_TTL_COUNT; i++) {
-               dns_slabheader_t *header = isc_heap_element(heap, 1);
-
-               if (header == NULL) {
-                       /* No headers left on this TTL heap; exit cleaning */
-                       return;
-               }
-
-               dns_ttl_t ttl = header->expire + STALE_TTL(header, qpdb);
-
-               if (ttl >= now - QPDB_VIRTUAL) {
-                       /*
-                        * The header at the top of this TTL heap is not yet
-                        * eligible for expiry, so none of the other headers on
-                        * the same heap can be eligible for expiry, either;
-                        * exit cleaning.
-                        */
-                       return;
-               }
-
-               (void)expireheader(header, nlocktypep, tlocktypep,
-                                  dns_expire_ttl DNS__DB_FLARG_PASS);
-       }
-}
-
 static void
 setmaxrrperset(dns_db_t *db, uint32_t value) {
        qpcache_t *qpdb = (qpcache_t *)db;
index e0a43e84347e3c0f69f3773016e2bb2fd1251afd..ef38df0be3857cd7b2e0e1512c26fb234006a1f1 100644 (file)
@@ -16,8 +16,6 @@
 #include <stdbool.h>
 #include <stdlib.h>
 
-#include <urcu/list.h>
-
 #include <isc/ascii.h>
 #include <isc/atomic.h>
 #include <isc/list.h>
@@ -25,6 +23,7 @@
 #include <isc/region.h>
 #include <isc/result.h>
 #include <isc/string.h>
+#include <isc/urcu.h>
 #include <isc/util.h>
 
 #include <dns/db.h>
@@ -459,8 +458,6 @@ dns_rdataslab_equalx(dns_slabheader_t *slab1, dns_slabheader_t *slab2,
 
 void
 dns_slabheader_reset(dns_slabheader_t *h, dns_dbnode_t *node) {
-       h->heap_index = 0;
-       h->heap = NULL;
        h->node = node;
 
        atomic_init(&h->attributes, 0);