]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: wait to gc transactions if they might still give an answer
authorRonan Pigott <ronan@rjp.ie>
Fri, 15 Mar 2024 20:52:30 +0000 (13:52 -0700)
committerLuca Boccassi <luca.boccassi@gmail.com>
Mon, 18 Mar 2024 11:10:11 +0000 (11:10 +0000)
In some cases when a query completes there are still pending
transactions that are no longer useful to answer the query. But if this
query is repeated in the future and we don't have the answers cached,
we're going to ask and ignore the answer again.

Instead of purging these superfluous transactions, let's wait and see if
they produce an answer, since we already asked the question, and use it
to fill our cache.

src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h

index 801bbe8007e136d18684bd459525b813f11473f7..14adb904bdfed6a90d24128d22117b3d2d2945ad 100644 (file)
@@ -57,6 +57,21 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
         }
 }
 
+static void dns_query_candidate_abandon(DnsQueryCandidate *c) {
+        DnsTransaction *t;
+
+        assert(c);
+
+        /* Abandon all the DnsTransactions attached to this query */
+
+        while ((t = set_steal_first(c->transactions))) {
+                t->wait_for_answer = true;
+                set_remove(t->notify_query_candidates, c);
+                set_remove(t->notify_query_candidates_done, c);
+                dns_transaction_gc(t);
+        }
+}
+
 static DnsQueryCandidate* dns_query_candidate_unlink(DnsQueryCandidate *c) {
         assert(c);
 
@@ -354,6 +369,16 @@ static void dns_query_stop(DnsQuery *q) {
                 dns_query_candidate_stop(c);
 }
 
+static void dns_query_abandon(DnsQuery *q) {
+        assert(q);
+
+        /* Thankfully transactions have their own timeouts */
+        event_source_disable(q->timeout_event_source);
+
+        LIST_FOREACH(candidates_by_query, c, q->candidates)
+                dns_query_candidate_abandon(c);
+}
+
 static void dns_query_unlink_candidates(DnsQuery *q) {
         assert(q);
 
@@ -591,7 +616,7 @@ void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
 
         (void) manager_monitor_send(q->manager, q);
 
-        dns_query_stop(q);
+        dns_query_abandon(q);
         if (q->complete)
                 q->complete(q);
 }
index 954841bb7c0a5bd18b6b582a3c5909f26ee1759f..5af550bc592715f73dba31ff45bb433d764113bc 100644 (file)
@@ -181,6 +181,9 @@ DnsTransaction* dns_transaction_gc(DnsTransaction *t) {
         if (t->block_gc > 0)
                 return t;
 
+        if (t->wait_for_answer && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
+                return t;
+
         if (set_isempty(t->notify_query_candidates) &&
             set_isempty(t->notify_query_candidates_done) &&
             set_isempty(t->notify_zone_items) &&
index 6de4cdd749122848d2382287604fa29ca6eed7df..30d2167d645dd35d85c7799a1fd9cb497982197e 100644 (file)
@@ -136,6 +136,11 @@ struct DnsTransaction {
 
         unsigned block_gc;
 
+        /* Set when we're willing to let this transaction live beyond it's usefulness for the original query,
+         * for caching purposes. This blocks gc while there is still a chance we might still receive an
+         * answer. */
+        bool wait_for_answer;
+
         LIST_FIELDS(DnsTransaction, transactions_by_scope);
         LIST_FIELDS(DnsTransaction, transactions_by_stream);
         LIST_FIELDS(DnsTransaction, transactions_by_key);