]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Use automatically-resizing hash table for resolver buckets
authorEvan Hunt <each@isc.org>
Fri, 6 May 2022 21:51:12 +0000 (14:51 -0700)
committerOndřej Surý <ondrej@isc.org>
Thu, 19 May 2022 07:27:28 +0000 (09:27 +0200)
Replace the statically-sized hash table used for the resolver
fetch contexts with an isc_ht_t.

lib/dns/resolver.c

index 76a14c859d6c7a85cf48b882211d54fdf6c2dcbe..e8aa64658ec6328430cc09eb6bc8a93c432cd6ce 100644 (file)
@@ -296,6 +296,11 @@ typedef enum {
        badns_forwarder,
 } badnstype_t;
 
+typedef struct fctxbucket {
+       isc_mutex_t lock;
+       ISC_LIST(fetchctx_t) fctxs;
+} fctxbucket_t;
+
 typedef struct fctxcount fctxcount_t;
 struct fctxcount {
        dns_fixedname_t dfname;
@@ -320,7 +325,7 @@ struct fetchctx {
        dns_name_t *name;
        dns_rdatatype_t type;
        unsigned int options;
-       unsigned int bucketnum;
+       fctxbucket_t *bucket;
        zonebucket_t *zbucket;
        char *info;
        isc_mem_t *mctx;
@@ -330,7 +335,7 @@ struct fetchctx {
        /* Atomic */
        isc_refcount_t references;
 
-       /*% Locked by appropriate bucket lock. */
+       /*% Locked by bucket lock. */
        fetchstate_t state;
        atomic_bool want_shutdown;
        bool cloned;
@@ -501,13 +506,6 @@ struct dns_fetch {
 #define DNS_FETCH_MAGIC               ISC_MAGIC('F', 't', 'c', 'h')
 #define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC)
 
-typedef struct fctxbucket {
-       isc_task_t *task;
-       isc_mutex_t lock;
-       ISC_LIST(fetchctx_t) fctxs;
-       atomic_bool exiting;
-} fctxbucket_t;
-
 typedef struct alternate {
        bool isaddress;
        union {
@@ -538,10 +536,12 @@ struct dns_resolver {
        dns_dispatchset_t *dispatches6;
        isc_dscp_t querydscp4;
        isc_dscp_t querydscp6;
-       unsigned int nbuckets;
-       fctxbucket_t *buckets;
+       isc_ht_t *buckets;
+       isc_rwlock_t hash_lock;
        isc_ht_t *zonebuckets;
        isc_rwlock_t zonehash_lock;
+       unsigned int ntasks;
+       isc_task_t **tasks;
        uint32_t lame_ttl;
        ISC_LIST(alternate_t) alternates;
        uint16_t udpsize;
@@ -570,7 +570,6 @@ struct dns_resolver {
        atomic_bool priming;
 
        /* Locked by lock. */
-       isc_refcount_t activebuckets;
        unsigned int spillat; /* clients-per-query */
 
        dns_badcache_t *badcache; /* Bad cache. */
@@ -643,7 +642,7 @@ fctx_shutdown(fetchctx_t *fctx);
 static isc_result_t
 fctx_minimize_qname(fetchctx_t *fctx);
 static void
-fctx_destroy(fetchctx_t *fctx, bool exiting);
+fctx_destroy(fetchctx_t *fctx);
 static isc_result_t
 ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                  dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl,
@@ -1173,8 +1172,6 @@ munge:
 static void
 resquery_destroy(resquery_t *query) {
        fetchctx_t *fctx = query->fctx;
-       dns_resolver_t *res = fctx->res;
-       unsigned int bucket = fctx->bucketnum;
 
        if (ISC_LINK_LINKED(query, link)) {
                ISC_LIST_UNLINK(fctx->queries, query, link);
@@ -1198,9 +1195,9 @@ resquery_destroy(resquery_t *query) {
 
        isc_refcount_destroy(&query->references);
 
-       LOCK(&res->buckets[bucket].lock);
+       LOCK(&fctx->bucket->lock);
        atomic_fetch_sub_release(&fctx->nqueries, 1);
-       UNLOCK(&res->buckets[bucket].lock);
+       UNLOCK(&fctx->bucket->lock);
        fctx_detach(&query->fctx);
 
        if (query->rmessage != NULL) {
@@ -1486,11 +1483,11 @@ fctx_cancelquery(resquery_t **queryp, isc_time_t *finish, bool no_response,
                dns_dispatch_cancel(&query->dispentry);
        }
 
-       LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        if (ISC_LINK_LINKED(query, link)) {
                ISC_LIST_UNLINK(fctx->queries, query, link);
        }
-       UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        resquery_detach(queryp);
 }
@@ -1548,9 +1545,9 @@ fctx_cancelqueries(fetchctx_t *fctx, bool no_response, bool age_untried) {
         * Move the queries to a local list so we can cancel
         * them without holding the lock.
         */
-       LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        ISC_LIST_MOVE(queries, fctx->queries);
-       UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        for (query = ISC_LIST_HEAD(queries); query != NULL; query = next_query)
        {
@@ -1808,14 +1805,12 @@ static void
 fctx__done_detach(fetchctx_t **fctxp, isc_result_t result, const char *file,
                  unsigned int line, const char *func) {
        fetchctx_t *fctx = NULL;
-       dns_resolver_t *res = NULL;
        bool no_response = false;
        bool age_untried = false;
 
        REQUIRE(fctxp != NULL && VALID_FCTX(*fctxp));
 
        fctx = *fctxp;
-       res = fctx->res;
 
        FCTXTRACE("done");
 
@@ -1828,10 +1823,10 @@ fctx__done_detach(fetchctx_t **fctxp, isc_result_t result, const char *file,
        UNUSED(func);
 #endif
 
-       LOCK(&res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        INSIST(fctx->state != fetchstate_done);
        fctx->state = fetchstate_done;
-       UNLOCK(&res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        if (result == ISC_R_SUCCESS) {
                if (fctx->qmin_warning != ISC_R_SUCCESS) {
@@ -1863,11 +1858,11 @@ fctx__done_detach(fetchctx_t **fctxp, isc_result_t result, const char *file,
        fctx_cancelqueries(fctx, no_response, age_untried);
        fctx_stoptimer(fctx);
 
-       LOCK(&res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        FCTX_ATTR_CLR(fctx, FCTX_ATTR_ADDRWAIT);
        fctx_sendevents(fctx, result, line);
        fctx_shutdown(fctx);
-       UNLOCK(&res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        fctx_detach(fctxp);
 }
@@ -2045,7 +2040,7 @@ resquery_timeout(resquery_t *query) {
        /*
         * Send the TRYSTALE events.
         */
-       LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        for (event = ISC_LIST_HEAD(fctx->events); event != NULL; event = next) {
                isc_task_t *sender = NULL;
 
@@ -2060,7 +2055,7 @@ resquery_timeout(resquery_t *query) {
                event->result = ISC_R_TIMEDOUT;
                isc_task_sendanddetach(&sender, ISC_EVENT_PTR(&event));
        }
-       UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        /*
         * If the next timeout is more than 1ms in the future,
@@ -2266,10 +2261,10 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
                dns_adb_beginudpfetch(fctx->adb, addrinfo);
        }
 
-       LOCK(&res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        ISC_LIST_APPEND(fctx->queries, query, link);
        atomic_fetch_add_relaxed(&fctx->nqueries, 1);
-       UNLOCK(&res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        /* Set up the dispatch and set the query ID */
        result = dns_dispatch_add(
@@ -2995,21 +2990,16 @@ static void
 fctx_finddone(isc_task_t *task, isc_event_t *event) {
        fetchctx_t *fctx = event->ev_arg;
        dns_adbfind_t *find = event->ev_sender;
-       dns_resolver_t *res;
        bool want_try = false;
        bool want_done = false;
-       unsigned int bucketnum;
 
        REQUIRE(VALID_FCTX(fctx));
-       res = fctx->res;
 
        UNUSED(task);
 
        FCTXTRACE("finddone");
 
-       bucketnum = fctx->bucketnum;
-       LOCK(&res->buckets[bucketnum].lock);
-
+       LOCK(&fctx->bucket->lock);
        INSIST(atomic_fetch_sub_release(&fctx->pending, 1) > 0);
 
        if (ADDRWAIT(fctx)) {
@@ -3035,7 +3025,7 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) {
        }
 
        isc_event_free(&event);
-       UNLOCK(&res->buckets[bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        dns_adb_destroyfind(&find);
 
@@ -3375,10 +3365,10 @@ findname(fetchctx_t *fctx, const dns_name_t *name, in_port_t port,
         * See what we know about this address.
         */
        fctx_addref(fctx);
-       result = dns_adb_createfind(
-               fctx->adb, res->buckets[fctx->bucketnum].task, fctx_finddone,
-               fctx, name, fctx->name, fctx->type, options, now, NULL,
-               res->view->dstport, fctx->depth + 1, fctx->qc, &find);
+       result = dns_adb_createfind(fctx->adb, fctx->task, fctx_finddone, fctx,
+                                   name, fctx->name, fctx->type, options, now,
+                                   NULL, res->view->dstport, fctx->depth + 1,
+                                   fctx->qc, &find);
 
        isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
                      DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
@@ -4066,16 +4056,13 @@ static void
 fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) {
        isc_result_t result;
        dns_adbaddrinfo_t *addrinfo = NULL;
-       dns_resolver_t *res;
-       isc_task_t *task;
-       unsigned int bucketnum;
+       dns_resolver_t *res = NULL;
 
        FCTXTRACE5("try", "fctx->qc=", isc_counter_used(fctx->qc));
 
        REQUIRE(!ADDRWAIT(fctx));
 
        res = fctx->res;
-       bucketnum = fctx->bucketnum;
 
        /* We've already exceeded maximum query count */
        if (isc_counter_used(fctx->qc) > res->maxqueries) {
@@ -4182,11 +4169,10 @@ fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) {
                        options |= DNS_FETCHOPT_NOFOLLOW;
                }
                fctx_addref(fctx);
-               task = res->buckets[bucketnum].task;
                result = dns_resolver_createfetch(
                        fctx->res, fctx->qminname, fctx->qmintype, fctx->domain,
                        &fctx->nameservers, NULL, NULL, 0, options, 0, fctx->qc,
-                       task, resume_qmin, fctx, &fctx->qminrrset, NULL,
+                       fctx->task, resume_qmin, fctx, &fctx->qminrrset, NULL,
                        &fctx->qminfetch);
                if (result != ISC_R_SUCCESS) {
                        fctx_unref(fctx);
@@ -4219,7 +4205,6 @@ resume_qmin(isc_task_t *task, isc_event_t *event) {
        dns_resolver_t *res = NULL;
        fetchctx_t *fctx = NULL;
        isc_result_t result;
-       unsigned int bucketnum;
        unsigned int findoptions = 0;
        dns_name_t *fname = NULL, *dcname = NULL;
        dns_fixedname_t ffixed, dcfixed;
@@ -4244,8 +4229,6 @@ resume_qmin(isc_task_t *task, isc_event_t *event) {
                dns_db_detach(&fevent->db);
        }
 
-       bucketnum = fctx->bucketnum;
-
        if (dns_rdataset_isassociated(fevent->rdataset)) {
                dns_rdataset_disassociate(fevent->rdataset);
        }
@@ -4255,14 +4238,14 @@ resume_qmin(isc_task_t *task, isc_event_t *event) {
 
        dns_resolver_destroyfetch(&fctx->qminfetch);
 
-       LOCK(&res->buckets[bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        if (SHUTTINGDOWN(fctx)) {
                maybe_cancel_validators(fctx);
-               UNLOCK(&res->buckets[bucketnum].lock);
+               UNLOCK(&fctx->bucket->lock);
                fctx_detach(&fctx);
                return;
        }
-       UNLOCK(&res->buckets[bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        if (result == ISC_R_CANCELED) {
                goto cleanup;
@@ -4356,11 +4339,11 @@ cleanup:
 }
 
 static void
-fctx_destroy(fetchctx_t *fctx, bool exiting) {
+fctx_destroy(fetchctx_t *fctx) {
        dns_resolver_t *res = NULL;
        isc_sockaddr_t *sa = NULL, *next_sa = NULL;
        struct tried *tried = NULL;
-       unsigned int bucketnum;
+       fctxbucket_t *bucket = NULL;
 
        REQUIRE(VALID_FCTX(fctx));
        REQUIRE(ISC_LIST_EMPTY(fctx->events));
@@ -4373,22 +4356,15 @@ fctx_destroy(fetchctx_t *fctx, bool exiting) {
        FCTXTRACE("destroy");
 
        res = fctx->res;
-       bucketnum = fctx->bucketnum;
-
-       LOCK(&res->buckets[bucketnum].lock);
-       REQUIRE(fctx->state != fetchstate_active);
-       ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link);
-
-       INSIST(atomic_fetch_sub_release(&res->nfctx, 1) > 0);
+       bucket = fctx->bucket;
 
        dec_stats(res, dns_resstatscounter_nfetch);
 
-       if (exiting && atomic_load_acquire(&res->buckets[bucketnum].exiting) &&
-           ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs))
-       {
-               isc_refcount_decrement(&res->activebuckets);
-       }
-       UNLOCK(&res->buckets[bucketnum].lock);
+       LOCK(&bucket->lock);
+       REQUIRE(fctx->state != fetchstate_active);
+       ISC_LIST_UNLINK(bucket->fctxs, fctx, link);
+       INSIST(atomic_fetch_sub_release(&res->nfctx, 1) > 0);
+       UNLOCK(&bucket->lock);
 
        isc_refcount_destroy(&fctx->references);
 
@@ -4457,25 +4433,19 @@ fctx_shutdown(fetchctx_t *fctx) {
        if (fctx->state != fetchstate_init) {
                FCTXTRACE("posting control event");
                cevent = &fctx->control_event;
-               isc_task_send(fctx->res->buckets[fctx->bucketnum].task,
-                             &cevent);
+               isc_task_send(fctx->task, &cevent);
        }
 }
 
 static void
 fctx_doshutdown(isc_task_t *task, isc_event_t *event) {
        fetchctx_t *fctx = event->ev_arg;
-       dns_resolver_t *res = NULL;
-       unsigned int bucketnum;
        dns_validator_t *validator = NULL;
 
        REQUIRE(VALID_FCTX(fctx));
 
        UNUSED(task);
 
-       res = fctx->res;
-       bucketnum = fctx->bucketnum;
-
        FCTXTRACE("doshutdown");
 
        /*
@@ -4511,7 +4481,7 @@ fctx_doshutdown(isc_task_t *task, isc_event_t *event) {
        fctx_cancelqueries(fctx, false, false);
        fctx_cleanup(fctx);
 
-       LOCK(&res->buckets[bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
 
        FCTX_ATTR_SET(fctx, FCTX_ATTR_SHUTTINGDOWN);
 
@@ -4527,7 +4497,7 @@ fctx_doshutdown(isc_task_t *task, isc_event_t *event) {
                fctx_unref(fctx);
        }
 
-       UNLOCK(&res->buckets[bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        fctx_detach(&fctx);
 }
@@ -4535,20 +4505,15 @@ fctx_doshutdown(isc_task_t *task, isc_event_t *event) {
 static void
 fctx_start(isc_task_t *task, isc_event_t *event) {
        fetchctx_t *fctx = event->ev_arg;
-       dns_resolver_t *res = NULL;
-       unsigned int bucketnum;
        isc_result_t result;
 
        REQUIRE(VALID_FCTX(fctx));
 
        UNUSED(task);
 
-       res = fctx->res;
-       bucketnum = fctx->bucketnum;
-
        FCTXTRACE("start");
 
-       LOCK(&res->buckets[bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
 
        INSIST(fctx->state == fetchstate_init);
        if (atomic_load_acquire(&fctx->want_shutdown)) {
@@ -4561,7 +4526,7 @@ fctx_start(isc_task_t *task, isc_event_t *event) {
                INSIST(atomic_load_acquire(&fctx->pending) == 0);
                INSIST(atomic_load_acquire(&fctx->nqueries) == 0);
                INSIST(ISC_LIST_EMPTY(fctx->validators));
-               UNLOCK(&res->buckets[bucketnum].lock);
+               UNLOCK(&fctx->bucket->lock);
 
                FCTX_ATTR_SET(fctx, FCTX_ATTR_SHUTTINGDOWN);
 
@@ -4583,7 +4548,7 @@ fctx_start(isc_task_t *task, isc_event_t *event) {
        ISC_EVENT_INIT(event, sizeof(*event), 0, NULL, DNS_EVENT_FETCHCONTROL,
                       fctx_doshutdown, fctx, NULL, NULL, NULL);
 
-       UNLOCK(&res->buckets[bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        /*
         * As a backstop, we also set a timer to stop the fetch
@@ -4683,39 +4648,40 @@ fctx_expired(isc_task_t *task, isc_event_t *event) {
        isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
                      DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
                      "shut down hung fetch while resolving '%s'", fctx->info);
-       LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        fctx_shutdown(fctx);
-       UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
        isc_event_free(&event);
 }
 
 static isc_result_t
-fctx_create(dns_resolver_t *res, isc_task_t *task, const dns_name_t *name,
-           dns_rdatatype_t type, const dns_name_t *domain,
-           dns_rdataset_t *nameservers, const isc_sockaddr_t *client,
-           unsigned int options, unsigned int bucketnum, unsigned int depth,
-           isc_counter_t *qc, fetchctx_t **fctxp) {
+fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
+           const dns_name_t *domain, dns_rdataset_t *nameservers,
+           const isc_sockaddr_t *client, unsigned int options,
+           fctxbucket_t *bucket, unsigned int depth, isc_counter_t *qc,
+           fetchctx_t **fctxp) {
        fetchctx_t *fctx = NULL;
        isc_result_t result;
        isc_result_t iresult;
        isc_interval_t interval;
        unsigned int findoptions = 0;
        char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE + 1];
+       unsigned int tid;
        size_t p;
 
        /*
-        * Caller must be holding the lock for bucket number
-        * 'bucketnum'.
+        * Caller must be holding the lock for 'bucket'
         */
        REQUIRE(fctxp != NULL && *fctxp == NULL);
 
+       tid = isc_random_uniform(res->ntasks);
        fctx = isc_mem_get(res->mctx, sizeof(*fctx));
        *fctx = (fetchctx_t){
                .type = type,
                .qmintype = type,
                .options = options,
-               .task = task,
-               .bucketnum = bucketnum,
+               .bucket = bucket,
+               .task = res->tasks[tid],
                .state = fetchstate_init,
                .depth = depth,
                .qmin_labels = 1,
@@ -4903,8 +4869,8 @@ fctx_create(dns_resolver_t *res, isc_task_t *task, const dns_name_t *name,
         * lifetime. It will be made active when the fetch is
         * started.
         */
-       isc_timer_create(res->timermgr, res->buckets[bucketnum].task,
-                        fctx_expired, fctx, &fctx->timer);
+       isc_timer_create(res->timermgr, fctx->task, fctx_expired, fctx,
+                        &fctx->timer);
 
        /*
         * Default retry interval initialization.  We set the interval
@@ -4960,7 +4926,7 @@ fctx_create(dns_resolver_t *res, isc_task_t *task, const dns_name_t *name,
                }
        }
 
-       ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link);
+       ISC_LIST_APPEND(bucket->fctxs, fctx, link);
 
        INSIST(atomic_fetch_add_relaxed(&res->nfctx, 1) < UINT32_MAX);
 
@@ -5383,7 +5349,6 @@ validated(isc_task_t *task, isc_event_t *event) {
        isc_stdtime_t now;
        uint32_t ttl;
        unsigned options;
-       uint32_t bucketnum;
        dns_fixedname_t fwild;
        dns_name_t *wild = NULL;
        dns_message_t *message = NULL;
@@ -5410,11 +5375,10 @@ validated(isc_task_t *task, isc_event_t *event) {
        vevent = (dns_validatorevent_t *)event;
        fctx->vresult = vevent->result;
 
-       bucketnum = fctx->bucketnum;
-       LOCK(&res->buckets[bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        ISC_LIST_UNLINK(fctx->validators, vevent->validator, link);
        fctx->validator = NULL;
-       UNLOCK(&res->buckets[bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        /*
         * Destroy the validator early so that we can
@@ -5430,7 +5394,7 @@ validated(isc_task_t *task, isc_event_t *event) {
 
        negative = (vevent->rdataset == NULL);
 
-       LOCK(&res->buckets[bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        sentresponse = ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0);
 
        /*
@@ -5439,7 +5403,7 @@ validated(isc_task_t *task, isc_event_t *event) {
         * events; if so, destroy the fctx.
         */
        if (SHUTTINGDOWN(fctx) && !sentresponse) {
-               UNLOCK(&res->buckets[bucketnum].lock);
+               UNLOCK(&fctx->bucket->lock);
                fctx_detach(&fctx);
                goto cleanup_event;
        }
@@ -5543,7 +5507,7 @@ validated(isc_task_t *task, isc_event_t *event) {
                dns_message_detach(&message);
                isc_event_free(&event);
 
-               UNLOCK(&res->buckets[bucketnum].lock);
+               UNLOCK(&fctx->bucket->lock);
                INSIST(fctx->validator == NULL);
 
                fctx->validator = ISC_LIST_HEAD(fctx->validators);
@@ -5699,7 +5663,7 @@ validated(isc_task_t *task, isc_event_t *event) {
                if (SHUTTINGDOWN(fctx)) {
                        maybe_cancel_validators(fctx);
                }
-               UNLOCK(&res->buckets[bucketnum].lock);
+               UNLOCK(&fctx->bucket->lock);
                fctx_detach(&fctx);
                goto cleanup_event;
        }
@@ -5715,7 +5679,7 @@ validated(isc_task_t *task, isc_event_t *event) {
                 * be validated.
                 */
                dns_db_detachnode(fctx->cache, &node);
-               UNLOCK(&res->buckets[bucketnum].lock);
+               UNLOCK(&fctx->bucket->lock);
                dns_validator_send(ISC_LIST_HEAD(fctx->validators));
                fctx_detach(&fctx);
                goto cleanup_event;
@@ -5873,7 +5837,7 @@ noanswer_response:
                dns_db_detachnode(fctx->cache, &node);
        }
 
-       UNLOCK(&res->buckets[bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
        /* Detach the extra reference that was set in valcreate() */
        fctx_unref(fctx);
        fctx_done_detach(&fctx, result); /* Locks bucket. */
@@ -6047,7 +6011,6 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
        isc_result_t result, eresult = ISC_R_SUCCESS;
        dns_fetchevent_t *event = NULL;
        unsigned int options;
-       isc_task_t *task;
        bool fail;
        unsigned int valoptions = 0;
        bool checknta = true;
@@ -6057,7 +6020,6 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
        /*
         * The appropriate bucket lock must be held.
         */
-       task = res->buckets[fctx->bucketnum].task;
 
        /*
         * Is DNSSEC validation required for this name?
@@ -6366,7 +6328,8 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
                                        result = valcreate(
                                                fctx, message, addrinfo, name,
                                                rdataset->type, rdataset,
-                                               sigrdataset, valoptions, task);
+                                               sigrdataset, valoptions,
+                                               fctx->task);
                                }
                        } else if (CHAINING(rdataset)) {
                                if (rdataset->type == dns_rdatatype_cname) {
@@ -6473,7 +6436,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
 
                result = valcreate(fctx, message, addrinfo, name, vtype,
                                   valrdataset, valsigrdataset, valoptions,
-                                  task);
+                                  fctx->task);
        }
 
        if (result == ISC_R_SUCCESS && have_answer) {
@@ -6519,7 +6482,7 @@ cache_message(fetchctx_t *fctx, dns_message_t *message,
 
        FCTX_ATTR_CLR(fctx, FCTX_ATTR_WANTCACHE);
 
-       LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
 
        for (section = DNS_SECTION_ANSWER; section <= DNS_SECTION_ADDITIONAL;
             section++) {
@@ -6544,7 +6507,7 @@ cache_message(fetchctx_t *fctx, dns_message_t *message,
                result = ISC_R_SUCCESS;
        }
 
-       UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        return (result);
 }
@@ -6693,8 +6656,7 @@ ncache_message(fetchctx_t *fctx, dns_message_t *message,
                 * Do negative response validation.
                 */
                result = valcreate(fctx, message, addrinfo, name, fctx->type,
-                                  NULL, NULL, valoptions,
-                                  res->buckets[fctx->bucketnum].task);
+                                  NULL, NULL, valoptions, fctx->task);
                /*
                 * If validation is necessary, return now.  Otherwise
                 * continue to process the message, letting the
@@ -6703,7 +6665,7 @@ ncache_message(fetchctx_t *fctx, dns_message_t *message,
                return (result);
        }
 
-       LOCK(&res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
 
        if (!HAVE_ANSWER(fctx)) {
                event = ISC_LIST_HEAD(fctx->events);
@@ -6756,7 +6718,7 @@ ncache_message(fetchctx_t *fctx, dns_message_t *message,
        }
 
 unlock:
-       UNLOCK(&res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        if (node != NULL) {
                dns_db_detachnode(fctx->cache, &node);
@@ -7266,7 +7228,7 @@ fctx__detach(fetchctx_t **fctxp, const char *file, unsigned int line,
 #endif
 
        if (refs == 1) {
-               fctx_destroy(fctx, true);
+               fctx_destroy(fctx);
        }
 }
 
@@ -7302,10 +7264,10 @@ resume_dslookup(isc_task_t *task, isc_event_t *event) {
        result = fevent->result;
        isc_event_free(&event);
 
-       LOCK(&res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        if (SHUTTINGDOWN(fctx)) {
                maybe_cancel_validators(fctx);
-               UNLOCK(&res->buckets[fctx->bucketnum].lock);
+               UNLOCK(&fctx->bucket->lock);
 
                if (dns_rdataset_isassociated(frdataset)) {
                        dns_rdataset_disassociate(frdataset);
@@ -7315,7 +7277,7 @@ resume_dslookup(isc_task_t *task, isc_event_t *event) {
                fctx_detach(&fctx);
                return;
        }
-       UNLOCK(&res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        /*
         * Detach the extra reference that was set in rctx_chaseds()
@@ -9718,7 +9680,6 @@ static void
 rctx_chaseds(respctx_t *rctx, dns_message_t *message,
             dns_adbaddrinfo_t *addrinfo, isc_result_t result) {
        fetchctx_t *fctx = rctx->fctx;
-       isc_task_t *task = NULL;
        unsigned int n;
 
        add_bad(fctx, message, addrinfo, result, rctx->broken_type);
@@ -9731,11 +9692,10 @@ rctx_chaseds(respctx_t *rctx, dns_message_t *message,
        FCTXTRACE("suspending DS lookup to find parent's NS records");
 
        fctx_addref(fctx);
-       task = fctx->res->buckets[fctx->bucketnum].task;
        result = dns_resolver_createfetch(
                fctx->res, fctx->nsname, dns_rdatatype_ns, NULL, NULL, NULL,
-               NULL, 0, fctx->options, 0, NULL, task, resume_dslookup, fctx,
-               &fctx->nsrrset, NULL, &fctx->nsfetch);
+               NULL, 0, fctx->options, 0, NULL, fctx->task, resume_dslookup,
+               fctx, &fctx->nsrrset, NULL, &fctx->nsfetch);
        if (result != ISC_R_SUCCESS) {
                if (result == DNS_R_DUPLICATE) {
                        result = DNS_R_SERVFAIL;
@@ -9798,11 +9758,11 @@ rctx_done(respctx_t *rctx, isc_result_t result) {
        /*
         * If nobody's waiting for results, don't resend.
         */
-       LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
        if (ISC_LIST_EMPTY(fctx->events)) {
                rctx->resend = false;
        }
-       UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        if (rctx->next_server) {
                rctx_nextserver(rctx, message, addrinfo, result);
@@ -10082,7 +10042,6 @@ rctx_delonly_zone(respctx_t *rctx) {
 static void
 destroy(dns_resolver_t *res) {
        isc_result_t result;
-       unsigned int i;
        alternate_t *a = NULL;
        isc_ht_iter_t *it = NULL;
 
@@ -10110,13 +10069,25 @@ destroy(dns_resolver_t *res) {
 
        isc_mutex_destroy(&res->primelock);
        isc_mutex_destroy(&res->lock);
-       for (i = 0; i < res->nbuckets; i++) {
-               INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs));
-               isc_task_detach(&res->buckets[i].task);
-               isc_mutex_destroy(&res->buckets[i].lock);
+
+       for (size_t i = 0; i < res->ntasks; i++) {
+               isc_task_detach(&res->tasks[i]);
+       }
+       isc_mem_put(res->mctx, res->tasks, res->ntasks * sizeof(res->tasks[0]));
+
+       isc_ht_iter_create(res->buckets, &it);
+       for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
+            result = isc_ht_iter_delcurrent_next(it))
+       {
+               fctxbucket_t *bucket = NULL;
+               isc_ht_iter_current(it, (void **)&bucket);
+
+               INSIST(ISC_LIST_EMPTY(bucket->fctxs));
+               isc_mutex_destroy(&bucket->lock);
+               isc_mem_put(res->mctx, bucket, sizeof(*bucket));
        }
-       isc_mem_put(res->mctx, res->buckets,
-                   res->nbuckets * sizeof(fctxbucket_t));
+       isc_ht_iter_destroy(&it);
+       isc_ht_destroy(&res->buckets);
 
        isc_ht_iter_create(res->zonebuckets, &it);
        for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
@@ -10219,6 +10190,7 @@ dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr,
                                 .taskmgr = taskmgr,
                                 .dispatchmgr = dispatchmgr,
                                 .options = options,
+                                .ntasks = ntasks,
                                 .udpsize = DEFAULT_EDNS_BUFSIZE,
                                 .spillatmin = 10,
                                 .spillat = 10,
@@ -10228,12 +10200,9 @@ dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr,
                                 .query_timeout = DEFAULT_QUERY_TIMEOUT,
                                 .maxdepth = DEFAULT_RECURSION_DEPTH,
                                 .maxqueries = DEFAULT_MAX_QUERIES,
-                                .nbuckets = ntasks,
                                 .querydscp4 = -1,
                                 .querydscp6 = -1 };
 
-       atomic_init(&res->activebuckets, ntasks);
-
        dns_view_weakattach(view, &res->view);
        isc_mem_attach(view->mctx, &res->mctx);
 
@@ -10248,31 +10217,26 @@ dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr,
                goto cleanup_res;
        }
 
-       res->buckets = isc_mem_get(view->mctx,
-                                  ntasks * sizeof(res->buckets[0]));
+       res->tasks = isc_mem_get(view->mctx, ntasks * sizeof(res->tasks[0]));
        for (uint32_t i = 0; i < ntasks; i++) {
-               res->buckets[i] = (fctxbucket_t){ 0 };
-
-               isc_mutex_init(&res->buckets[i].lock);
-
                /*
                 * Since we have a pool of tasks we bind them to task
                 * queues to spread the load evenly
                 */
-               result = isc_task_create_bound(taskmgr, 0,
-                                              &res->buckets[i].task, i);
+               res->tasks[i] = NULL;
+               result = isc_task_create_bound(taskmgr, 0, &res->tasks[i], i);
                if (result != ISC_R_SUCCESS) {
-                       isc_mutex_destroy(&res->buckets[i].lock);
-                       goto cleanup_buckets;
+                       goto cleanup_tasks;
                }
 
                snprintf(name, sizeof(name), "res%" PRIu32, i);
-               isc_task_setname(res->buckets[i].task, name, res);
-
-               ISC_LIST_INIT(res->buckets[i].fctxs);
-               atomic_init(&res->buckets[i].exiting, false);
+               isc_task_setname(res->tasks[i], name, res);
        }
 
+       isc_ht_init(&res->buckets, view->mctx, RES_DOMAIN_HASH_BITS,
+                   ISC_HT_CASE_INSENSITIVE);
+       isc_rwlock_init(&res->hash_lock, 0, 0);
+
        isc_ht_init(&res->zonebuckets, view->mctx, RES_DOMAIN_HASH_BITS,
                    ISC_HT_CASE_INSENSITIVE);
        isc_rwlock_init(&res->zonehash_lock, 0, 0);
@@ -10320,13 +10284,15 @@ cleanup_primelock:
        isc_rwlock_destroy(&res->zonehash_lock);
        isc_ht_destroy(&res->zonebuckets);
 
-cleanup_buckets:
+       isc_rwlock_destroy(&res->hash_lock);
+       isc_ht_destroy(&res->buckets);
+
+cleanup_tasks:
        for (size_t i = 0; i < ntasks; i++) {
-               isc_mutex_destroy(&res->buckets[i].lock);
-               isc_task_detach(&res->buckets[i].task);
+               isc_task_detach(&res->tasks[i]);
        }
-       isc_mem_put(view->mctx, res->buckets,
-                   res->nbuckets * sizeof(fctxbucket_t));
+       isc_mem_put(view->mctx, res->tasks,
+                   res->ntasks * sizeof(res->tasks[0]));
 
        dns_badcache_destroy(&res->badcache);
 
@@ -10422,9 +10388,8 @@ dns_resolver_prime(dns_resolver_t *res) {
                INSIST(res->primefetch == NULL);
                result = dns_resolver_createfetch(
                        res, dns_rootname, dns_rdatatype_ns, NULL, NULL, NULL,
-                       NULL, 0, DNS_FETCHOPT_NOFORWARD, 0, NULL,
-                       res->buckets[0].task, prime_done, res, rdataset, NULL,
-                       &res->primefetch);
+                       NULL, 0, DNS_FETCHOPT_NOFORWARD, 0, NULL, res->tasks[0],
+                       prime_done, res, rdataset, NULL, &res->primefetch);
                UNLOCK(&res->primelock);
 
                if (result != ISC_R_SUCCESS) {
@@ -10462,8 +10427,6 @@ dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp) {
 
 void
 dns_resolver_shutdown(dns_resolver_t *res) {
-       unsigned int i;
-       fetchctx_t *fctx;
        isc_result_t result;
        bool is_false = false;
 
@@ -10471,28 +10434,35 @@ dns_resolver_shutdown(dns_resolver_t *res) {
 
        RTRACE("shutdown");
 
-       LOCK(&res->lock);
        if (atomic_compare_exchange_strong(&res->exiting, &is_false, true)) {
+               isc_ht_iter_t *it = NULL;
+
                RTRACE("exiting");
 
-               for (i = 0; i < res->nbuckets; i++) {
-                       LOCK(&res->buckets[i].lock);
-                       for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs);
-                            fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link))
+               RWLOCK(&res->hash_lock, isc_rwlocktype_read);
+               isc_ht_iter_create(res->buckets, &it);
+               for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
+                    result = isc_ht_iter_next(it))
+               {
+                       fctxbucket_t *bucket = NULL;
+                       fetchctx_t *fctx = NULL;
+
+                       isc_ht_iter_current(it, (void **)&bucket);
+                       LOCK(&bucket->lock);
+                       for (fctx = ISC_LIST_HEAD(bucket->fctxs); fctx != NULL;
+                            fctx = ISC_LIST_NEXT(fctx, link))
                        {
                                fctx_shutdown(fctx);
                        }
-                       atomic_store(&res->buckets[i].exiting, true);
-                       if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) {
-                               isc_refcount_decrement(&res->activebuckets);
-                       }
-                       UNLOCK(&res->buckets[i].lock);
+                       UNLOCK(&bucket->lock);
                }
+               isc_ht_iter_destroy(&it);
+               RWUNLOCK(&res->hash_lock, isc_rwlocktype_read);
+
                result = isc_timer_reset(res->spillattimer,
                                         isc_timertype_inactive, NULL, true);
                RUNTIME_CHECK(result == ISC_R_SUCCESS);
        }
-       UNLOCK(&res->lock);
 }
 
 void
@@ -10507,7 +10477,6 @@ dns_resolver_detach(dns_resolver_t **resp) {
        RTRACE("detach");
 
        if (isc_refcount_decrement(&res->references) == 1) {
-               isc_refcount_destroy(&res->activebuckets);
                INSIST(atomic_load_acquire(&res->exiting));
                destroy(res);
        }
@@ -10641,6 +10610,42 @@ fctx_minimize_qname(fetchctx_t *fctx) {
        return (result);
 }
 
+static fctxbucket_t *
+get_fctxbucket(dns_resolver_t *res, const dns_name_t *domain) {
+       isc_result_t result;
+       isc_rwlocktype_t ltype = isc_rwlocktype_read;
+       fctxbucket_t *bucket = NULL;
+
+       RWLOCK(&res->hash_lock, ltype);
+       result = isc_ht_find(res->buckets, domain->ndata, domain->length,
+                            (void **)&bucket);
+       if (result != ISC_R_SUCCESS) {
+               bucket = isc_mem_get(res->mctx, sizeof(*bucket));
+               *bucket = (fctxbucket_t){ .fctxs = { 0 } };
+               ISC_LIST_INIT(bucket->fctxs);
+               isc_mutex_init(&bucket->lock);
+               RWUNLOCK(&res->hash_lock, ltype);
+
+               ltype = isc_rwlocktype_write;
+               RWLOCK(&res->hash_lock, ltype);
+               result = isc_ht_add(res->buckets, domain->ndata, domain->length,
+                                   bucket);
+               if (result == ISC_R_SUCCESS) {
+                       inc_stats(res, dns_resstatscounter_buckets);
+               } else {
+                       /* Another thread must have created it */
+                       isc_mutex_destroy(&bucket->lock);
+                       isc_mem_put(res->mctx, bucket, sizeof(*bucket));
+                       result = isc_ht_find(res->buckets, domain->ndata,
+                                            domain->length, (void **)&bucket);
+               }
+       }
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       RWUNLOCK(&res->hash_lock, ltype);
+
+       return (bucket);
+}
+
 isc_result_t
 dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
                         dns_rdatatype_t type, const dns_name_t *domain,
@@ -10652,16 +10657,16 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
                         isc_taskaction_t action, void *arg,
                         dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
                         dns_fetch_t **fetchp) {
-       dns_fetch_t *fetch;
+       dns_fetch_t *fetch = NULL;
        fetchctx_t *fctx = NULL;
        isc_result_t result = ISC_R_SUCCESS;
-       unsigned int bucketnum;
        bool new_fctx = false;
-       isc_event_t *event;
+       isc_event_t *event = NULL;
        unsigned int count = 0;
        unsigned int spillat;
        unsigned int spillatmin;
        bool dodestroy = false;
+       fctxbucket_t *bucket = NULL;
 
        UNUSED(forwarders);
 
@@ -10691,22 +10696,17 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
        dns_resolver_attach(res, &fetch->res);
        isc_mem_attach(res->mctx, &fetch->mctx);
 
-       bucketnum = dns_name_fullhash(name, false) % res->nbuckets;
-
        LOCK(&res->lock);
        spillat = res->spillat;
        spillatmin = res->spillatmin;
        UNLOCK(&res->lock);
-       LOCK(&res->buckets[bucketnum].lock);
 
-       if (atomic_load(&res->buckets[bucketnum].exiting)) {
-               result = ISC_R_SHUTTINGDOWN;
-               goto unlock;
-       }
+       bucket = get_fctxbucket(res, name);
+       LOCK(&bucket->lock);
 
        if ((options & DNS_FETCHOPT_UNSHARED) == 0) {
-               for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs);
-                    fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link))
+               for (fctx = ISC_LIST_HEAD(bucket->fctxs); fctx != NULL;
+                    fctx = ISC_LIST_NEXT(fctx, link))
                {
                        if (fctx_match(fctx, name, type, options)) {
                                break;
@@ -10743,9 +10743,8 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
        }
 
        if (fctx == NULL) {
-               result = fctx_create(res, task, name, type, domain, nameservers,
-                                    client, options, bucketnum, depth, qc,
-                                    &fctx);
+               result = fctx_create(res, name, type, domain, nameservers,
+                                    client, options, bucket, depth, qc, &fctx);
                if (result != ISC_R_SUCCESS) {
                        goto unlock;
                }
@@ -10774,17 +10773,17 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
                        ISC_EVENT_INIT(event, sizeof(*event), 0, NULL,
                                       DNS_EVENT_FETCHCONTROL, fctx_start, fctx,
                                       NULL, NULL, NULL);
-                       isc_task_send(res->buckets[bucketnum].task, &event);
+                       isc_task_send(fctx->task, &event);
                } else {
                        dodestroy = true;
                }
        }
 
 unlock:
-       UNLOCK(&res->buckets[bucketnum].lock);
+       UNLOCK(&bucket->lock);
 
        if (dodestroy) {
-               fctx_destroy(fctx, false);
+               fctx_destroy(fctx);
        }
 
        if (result == ISC_R_SUCCESS) {
@@ -10801,17 +10800,15 @@ unlock:
 void
 dns_resolver_cancelfetch(dns_fetch_t *fetch) {
        fetchctx_t *fctx = NULL;
-       dns_resolver_t *res = NULL;
        dns_fetchevent_t *event = NULL;
 
        REQUIRE(DNS_FETCH_VALID(fetch));
        fctx = fetch->private;
        REQUIRE(VALID_FCTX(fctx));
-       res = fctx->res;
 
        FTRACE("cancelfetch");
 
-       LOCK(&res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
 
        /*
         * Find the completion event for this fetch (as opposed
@@ -10840,7 +10837,7 @@ dns_resolver_cancelfetch(dns_fetch_t *fetch) {
         * The fctx continues running even if no fetches remain;
         * the answer is still cached.
         */
-       UNLOCK(&res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 }
 
 void
@@ -10848,7 +10845,6 @@ dns_resolver_destroyfetch(dns_fetch_t **fetchp) {
        dns_fetch_t *fetch = NULL;
        dns_resolver_t *res = NULL;
        fetchctx_t *fctx = NULL;
-       unsigned int bucketnum;
 
        REQUIRE(fetchp != NULL);
        fetch = *fetchp;
@@ -10860,8 +10856,7 @@ dns_resolver_destroyfetch(dns_fetch_t **fetchp) {
 
        FTRACE("destroyfetch");
 
-       bucketnum = fctx->bucketnum;
-       LOCK(&res->buckets[bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
 
        /*
         * Sanity check: the caller should have gotten its event before
@@ -10875,7 +10870,7 @@ dns_resolver_destroyfetch(dns_fetch_t **fetchp) {
                        RUNTIME_CHECK(event->fetch != fetch);
                }
        }
-       UNLOCK(&res->buckets[bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 
        isc_mem_putanddetach(&fetch->mctx, fetch, sizeof(*fetch));
 
@@ -10887,16 +10882,14 @@ void
 dns_resolver_logfetch(dns_fetch_t *fetch, isc_log_t *lctx,
                      isc_logcategory_t *category, isc_logmodule_t *module,
                      int level, bool duplicateok) {
-       fetchctx_t *fctx;
-       dns_resolver_t *res;
+       fetchctx_t *fctx = NULL;
        char domainbuf[DNS_NAME_FORMATSIZE];
 
        REQUIRE(DNS_FETCH_VALID(fetch));
        fctx = fetch->private;
        REQUIRE(VALID_FCTX(fctx));
-       res = fctx->res;
 
-       LOCK(&res->buckets[fctx->bucketnum].lock);
+       LOCK(&fctx->bucket->lock);
 
        INSIST(fctx->exitline >= 0);
        if (!fctx->logged || duplicateok) {
@@ -10920,7 +10913,7 @@ dns_resolver_logfetch(dns_fetch_t *fetch, isc_log_t *lctx,
                fctx->logged = true;
        }
 
-       UNLOCK(&res->buckets[fctx->bucketnum].lock);
+       UNLOCK(&fctx->bucket->lock);
 }
 
 dns_dispatchmgr_t *
@@ -11543,7 +11536,7 @@ dns_resolver_setstats(dns_resolver_t *res, isc_stats_t *stats) {
        isc_stats_attach(stats, &res->stats);
 
        /* initialize the bucket "counter"; it's a static value */
-       set_stats(res, dns_resstatscounter_buckets, res->nbuckets);
+       set_stats(res, dns_resstatscounter_buckets, res->ntasks);
 }
 
 void