perf: cache walked authority suffixes in find-authority

ober

6bdd841c95fe085d49fe288340e3d9f72a7c7b1b

diff --git a/lib/jerboa-dns/lookup.ss b/lib/jerboa-dns/lookup.ss
index af8fde6..faf0eb1 100644
--- a/lib/jerboa-dns/lookup.ss
+++ b/lib/jerboa-dns/lookup.ss
@@ -59,6 +59,40 @@
 
   ;; ========== Authority Detection ==========
 
+  ;; check-authority's result depends only on the walked suffix, so it is
+  ;; cached to avoid one CDB lookup per label on deep names.  The cache is
+  ;; keyed by the snapshot reader (a fresh one per CDB generation) so a hot
+  ;; reload starts clean, is bounded to cap memory under diverse query
+  ;; traffic, and is guarded by a mutex since workers query concurrently.
+  (def *authority-mutex* (make-mutex))
+  (def *authority-reader* #f)
+  (def *authority-cache* (make-hash-table))
+  (def *authority-count* 0)
+  (def authority-cache-limit 4096)
+
+  (def (with-authority-lock thunk)
+    (dynamic-wind
+      (lambda () (mutex-acquire *authority-mutex*))
+      thunk
+      (lambda () (mutex-release *authority-mutex*))))
+
+  (def (authority-cache-ref cdb-reader key)
+    (with-authority-lock
+      (lambda ()
+        (unless (eq? cdb-reader *authority-reader*)
+          (set! *authority-reader* cdb-reader)
+          (set! *authority-cache* (make-hash-table))
+          (set! *authority-count* 0))
+        (hash-ref *authority-cache* key #f))))
+
+  (def (authority-cache-put! cdb-reader key has-soa? has-ns?)
+    (with-authority-lock
+      (lambda ()
+        (when (and (eq? cdb-reader *authority-reader*)
+                   (< *authority-count* authority-cache-limit))
+          (hash-put! *authority-cache* key (cons has-soa? has-ns?))
+          (set! *authority-count* (+ *authority-count* 1))))))
+
   (def (find-authority cdb-reader qname)
     ;; Walk up the domain hierarchy looking for SOA/NS records.
     ;; Returns (values auth-offset has-soa? has-ns?) or (values #f #f #f)
@@ -78,12 +112,19 @@
   (def (check-authority cdb-reader qname offset)
     ;; Check if domain at offset has SOA and/or NS records in CDB
     (let* ([key (dns-domain-copy qname offset)]
-           [records (cdb-find-all cdb-reader key 0 (bytevector-length key))]
-           [has-soa? (any-record-type? records DNS-T-SOA)]
-           [has-ns? (any-record-type? records DNS-T-NS)])
-      (if (or has-soa? has-ns?)
-        (values offset has-soa? has-ns?)
-        (values #f #f #f))))
+           [cached (authority-cache-ref cdb-reader key)])
+      (if cached
+        (let ([has-soa? (car cached)] [has-ns? (cdr cached)])
+          (if (or has-soa? has-ns?)
+            (values offset has-soa? has-ns?)
+            (values #f #f #f)))
+        (let* ([records (cdb-find-all cdb-reader key 0 (bytevector-length key))]
+               [has-soa? (any-record-type? records DNS-T-SOA)]
+               [has-ns? (any-record-type? records DNS-T-NS)])
+          (authority-cache-put! cdb-reader key has-soa? has-ns?)
+          (if (or has-soa? has-ns?)
+            (values offset has-soa? has-ns?)
+            (values #f #f #f))))))
 
   (def (any-record-type? records rtype)
     (any (lambda (val) (= (cdb-val-type val) rtype)) records))