From db4dde6825da1d2b1bb2f1a233b27c5d8750c5f0 Mon Sep 17 00:00:00 2001 From: kshitjj Date: Fri, 8 Sep 2023 20:59:18 +0530 Subject: [PATCH 01/11] dns: implement light dns caching --- lib/dns.js | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/dns.js b/lib/dns.js index ca932ad05f4da7..82c6c96b458ec8 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -98,6 +98,10 @@ const { stopPerf, } = require('internal/perf/observe'); +const { + setInterval, +} = require('timers'); + const dnsException = errors.dnsException; let promises = null; // Lazy loaded @@ -133,10 +137,24 @@ function onlookupall(err, addresses) { } } +function refreshDNSCache() { + const { DateNow } = primordials; + setInterval(() => { + for (const hostname in dnsCache) { + if (dnsCache[hostname].expiresAt <= DateNow) { + delete dnsCache[hostname]; + } + } + }, 1000); +} + +refreshDNSCache(); + // Easy DNS A/AAAA look up // lookup(hostname, [options,] callback) const validFamilies = [0, 4, 6]; +const dnsCache = {}; function lookup(hostname, options, callback) { let hints = 0; let family = 0; @@ -212,11 +230,34 @@ function lookup(hostname, options, callback) { return {}; } + if (dnsCache[hostname]) { + const cachedResult = dnsCache[hostname].result; + if (all) { + process.nextTick(callback, null, cachedResult); + } else { + process.nextTick(callback, null, hostname, cachedResult[0].family); + } + return {}; + } + const req = new GetAddrInfoReqWrap(); req.callback = callback; req.family = family; req.hostname = hostname; - req.oncomplete = all ? onlookupall : onlookup; + req.oncomplete = function(err, result) { + if (!err) { + const { DateNow } = primordials; + dnsCache[hostname] = { + result, + expiresAt: DateNow + 1000, + }; + } + if (all) { + onlookupall(err, result); + } else { + onlookup(err, result); + } + }; const err = cares.getaddrinfo( req, hostname, family, hints, verbatim, From b5a7580acf8d0f7954d247b10a5ad444092c71c0 Mon Sep 17 00:00:00 2001 From: Kshitij Rajgude Date: Sat, 9 Sep 2023 16:38:42 +0530 Subject: [PATCH 02/11] dns: add default value to dnsCache a lookup for, say, "constructor" or "toString" results in a cache hit Co-authored-by: Ben Noordhuis --- lib/dns.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dns.js b/lib/dns.js index 82c6c96b458ec8..384e66d634943e 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -154,7 +154,7 @@ refreshDNSCache(); // Easy DNS A/AAAA look up // lookup(hostname, [options,] callback) const validFamilies = [0, 4, 6]; -const dnsCache = {}; +const dnsCache = { __proto__: null }; function lookup(hostname, options, callback) { let hints = 0; let family = 0; From 3038e59adf29d531792450725a86ad200440f21a Mon Sep 17 00:00:00 2001 From: kshitjj Date: Mon, 11 Sep 2023 18:15:15 +0530 Subject: [PATCH 03/11] fix: dns cache algorithm --- lib/dns.js | 71 +++++++++++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index 384e66d634943e..a97820f6218040 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -98,10 +98,6 @@ const { stopPerf, } = require('internal/perf/observe'); -const { - setInterval, -} = require('timers'); - const dnsException = errors.dnsException; let promises = null; // Lazy loaded @@ -137,19 +133,6 @@ function onlookupall(err, addresses) { } } -function refreshDNSCache() { - const { DateNow } = primordials; - setInterval(() => { - for (const hostname in dnsCache) { - if (dnsCache[hostname].expiresAt <= DateNow) { - delete dnsCache[hostname]; - } - } - }, 1000); -} - -refreshDNSCache(); - // Easy DNS A/AAAA look up // lookup(hostname, [options,] callback) @@ -161,6 +144,9 @@ function lookup(hostname, options, callback) { let all = false; let verbatim = getDefaultVerbatim(); + const cacheKey = `${hostname}_${family || 'default'}`; + const cachedResult = dnsCache[cacheKey]; + // Parse arguments if (hostname) { validateString(hostname, 'hostname'); @@ -209,6 +195,11 @@ function lookup(hostname, options, callback) { } } + if (cachedResult && Date.now() - cachedResult.timestamp < 1000) { + callback(null, cachedResult.address, cachedResult.family); + return {}; + } + if (!hostname) { emitInvalidHostnameWarning(hostname); if (all) { @@ -230,34 +221,20 @@ function lookup(hostname, options, callback) { return {}; } - if (dnsCache[hostname]) { - const cachedResult = dnsCache[hostname].result; - if (all) { - process.nextTick(callback, null, cachedResult); - } else { - process.nextTick(callback, null, hostname, cachedResult[0].family); - } - return {}; - } - const req = new GetAddrInfoReqWrap(); - req.callback = callback; - req.family = family; - req.hostname = hostname; - req.oncomplete = function(err, result) { - if (!err) { - const { DateNow } = primordials; - dnsCache[hostname] = { - result, - expiresAt: DateNow + 1000, + req.callback = (error, address, family) => { + if (!error) { + dnsCache[cacheKey] = { + address, + family, + timestamp: Date.now(), }; } - if (all) { - onlookupall(err, result); - } else { - onlookup(err, result); - } + callback(error, address, family); }; + req.family = family; + req.hostname = hostname; + req.oncomplete = all ? onlookupall : onlookup; const err = cares.getaddrinfo( req, hostname, family, hints, verbatim, @@ -278,6 +255,18 @@ function lookup(hostname, options, callback) { return req; } +// DNS cache checks every 1 second +setInterval(() => { + const currentTime = Date.now(); + for (const key in dnsCache) { + if (dnsCache.hasOwnProperty(key)) { + if (currentTime - dnsCache[key].timestamp >= 1000) { + delete dnsCache[key]; + } + } + } +}, 1000); + ObjectDefineProperty(lookup, customPromisifyArgs, { __proto__: null, value: ['address', 'family'], enumerable: false }); From b4f10252f5e6d5056220f88035db20b2aa3dfcfb Mon Sep 17 00:00:00 2001 From: kshitjj Date: Mon, 11 Sep 2023 19:05:04 +0530 Subject: [PATCH 04/11] fix: removed hasOwnProperty method --- lib/dns.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index a97820f6218040..a95731d374a703 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -259,11 +259,9 @@ function lookup(hostname, options, callback) { setInterval(() => { const currentTime = Date.now(); for (const key in dnsCache) { - if (dnsCache.hasOwnProperty(key)) { if (currentTime - dnsCache[key].timestamp >= 1000) { delete dnsCache[key]; } - } } }, 1000); From c32745bcae05d3e83089c24c697e0945acea7a54 Mon Sep 17 00:00:00 2001 From: kshitjj Date: Mon, 11 Sep 2023 19:12:14 +0530 Subject: [PATCH 05/11] fix: removed hasOwnProperty method --- lib/dns.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index a97820f6218040..a95731d374a703 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -259,11 +259,9 @@ function lookup(hostname, options, callback) { setInterval(() => { const currentTime = Date.now(); for (const key in dnsCache) { - if (dnsCache.hasOwnProperty(key)) { if (currentTime - dnsCache[key].timestamp >= 1000) { delete dnsCache[key]; } - } } }, 1000); From f731c160c8637b115d8eed34c6d1938a2c24ec7a Mon Sep 17 00:00:00 2001 From: kshitjj Date: Sat, 16 Sep 2023 18:58:39 +0530 Subject: [PATCH 06/11] fix: linter --- lib/dns.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index a95731d374a703..596c766b431b85 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -31,6 +31,7 @@ const cares = internalBinding('cares_wrap'); const { isIP } = require('internal/net'); const { customPromisifyArgs } = require('internal/util'); const errors = require('internal/errors'); +const { setInterval } = require('timers'); const { bindDefaultResolver, setDefaultResolver, @@ -195,7 +196,8 @@ function lookup(hostname, options, callback) { } } - if (cachedResult && Date.now() - cachedResult.timestamp < 1000) { + const { DateNow } = primordials; + if (cachedResult && DateNow() - cachedResult.timestamp < 1000) { callback(null, cachedResult.address, cachedResult.family); return {}; } @@ -227,7 +229,7 @@ function lookup(hostname, options, callback) { dnsCache[cacheKey] = { address, family, - timestamp: Date.now(), + timestamp: DateNow(), }; } callback(error, address, family); @@ -257,11 +259,12 @@ function lookup(hostname, options, callback) { // DNS cache checks every 1 second setInterval(() => { - const currentTime = Date.now(); + const { DateNow } = primordials; + const currentTime = DateNow(); for (const key in dnsCache) { - if (currentTime - dnsCache[key].timestamp >= 1000) { - delete dnsCache[key]; - } + if (currentTime - dnsCache[key].timestamp >= 1000) { + delete dnsCache[key]; + } } }, 1000); From 80c60ebd61390440157a45346a1631ac1df35a0b Mon Sep 17 00:00:00 2001 From: kshitjj Date: Sat, 16 Sep 2023 19:08:40 +0530 Subject: [PATCH 07/11] fix: unref timer --- lib/dns.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dns.js b/lib/dns.js index 596c766b431b85..aec953aaf53a35 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -266,7 +266,7 @@ setInterval(() => { delete dnsCache[key]; } } -}, 1000); +}, 1000).unref(); ObjectDefineProperty(lookup, customPromisifyArgs, { __proto__: null, value: ['address', 'family'], enumerable: false }); From 83586ef1e7e68272705fce3f4c7a589f0f92607c Mon Sep 17 00:00:00 2001 From: kshitjj Date: Sat, 16 Sep 2023 19:36:14 +0530 Subject: [PATCH 08/11] Refactor: DNS cache cleanup scheduling --- lib/dns.js | 66 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index aec953aaf53a35..5a1a0255db6cbe 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -31,7 +31,7 @@ const cares = internalBinding('cares_wrap'); const { isIP } = require('internal/net'); const { customPromisifyArgs } = require('internal/util'); const errors = require('internal/errors'); -const { setInterval } = require('timers'); +const { setTimeout, unref } = require('timers'); const { bindDefaultResolver, setDefaultResolver, @@ -134,19 +134,47 @@ function onlookupall(err, addresses) { } } +function cleanDnsCache() { + for (const key in dnsCache) { + delete dnsCache[key]; + } +} + +function scheduleCleanup() { + if (cleanupTimeout) { + return; + } + + cleanupTimeout = setTimeout(() => { + cleanDnsCache(); + cleanupTimeout = null; + }, 1000).ref(); +} + +function addDnsEntry(cacheKey, address, family) { + dnsCache[cacheKey] = { + address, + family, + timestamp: DateNow(), + }; + + scheduleCleanup(); + + return dnsCache[cacheKey]; +} // Easy DNS A/AAAA look up // lookup(hostname, [options,] callback) const validFamilies = [0, 4, 6]; const dnsCache = { __proto__: null }; +const { DateNow } = primordials; +let cleanupTimeout = null; function lookup(hostname, options, callback) { let hints = 0; let family = 0; let all = false; let verbatim = getDefaultVerbatim(); - const cacheKey = `${hostname}_${family || 'default'}`; - const cachedResult = dnsCache[cacheKey]; // Parse arguments if (hostname) { @@ -196,12 +224,6 @@ function lookup(hostname, options, callback) { } } - const { DateNow } = primordials; - if (cachedResult && DateNow() - cachedResult.timestamp < 1000) { - callback(null, cachedResult.address, cachedResult.family); - return {}; - } - if (!hostname) { emitInvalidHostnameWarning(hostname); if (all) { @@ -223,14 +245,19 @@ function lookup(hostname, options, callback) { return {}; } + const cacheKey = `${hostname}_${family || 'default'}`; + const cachedResult = dnsCache[cacheKey]; + + if (cachedResult && DateNow() - cachedResult.timestamp < 1000) { + callback(null, cachedResult.address, cachedResult.family); + return {}; + } + + const req = new GetAddrInfoReqWrap(); req.callback = (error, address, family) => { if (!error) { - dnsCache[cacheKey] = { - address, - family, - timestamp: DateNow(), - }; + addDnsEntry(cacheKey, address, family); } callback(error, address, family); }; @@ -257,17 +284,6 @@ function lookup(hostname, options, callback) { return req; } -// DNS cache checks every 1 second -setInterval(() => { - const { DateNow } = primordials; - const currentTime = DateNow(); - for (const key in dnsCache) { - if (currentTime - dnsCache[key].timestamp >= 1000) { - delete dnsCache[key]; - } - } -}, 1000).unref(); - ObjectDefineProperty(lookup, customPromisifyArgs, { __proto__: null, value: ['address', 'family'], enumerable: false }); From 090a1a094d9fa2e65a7b23d4f5cd5df4ddb2b35c Mon Sep 17 00:00:00 2001 From: kshitjj Date: Sat, 16 Sep 2023 23:28:35 +0530 Subject: [PATCH 09/11] Implement lazy cache cleanup for DNS entries This commit replaces the timer based approach for purging the expired dns cache with lazy cleanup approach. The DNS cache is now cleaned before adding a new DNS entry and before performing a lookup. This change simplifies the code and eliminates the need for a separate cleanup timer. --- lib/dns.js | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index 5a1a0255db6cbe..3d7391d3269d17 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -134,21 +134,13 @@ function onlookupall(err, addresses) { } } -function cleanDnsCache() { +function cleanExpiredDnsCache() { + const currentTime = DateNow(); for (const key in dnsCache) { - delete dnsCache[key]; - } -} - -function scheduleCleanup() { - if (cleanupTimeout) { - return; + if (currentTime - dnsCache[key].timestamp >= 1000) { + delete dnsCache[key]; + } } - - cleanupTimeout = setTimeout(() => { - cleanDnsCache(); - cleanupTimeout = null; - }, 1000).ref(); } function addDnsEntry(cacheKey, address, family) { @@ -158,7 +150,7 @@ function addDnsEntry(cacheKey, address, family) { timestamp: DateNow(), }; - scheduleCleanup(); + cleanExpiredDnsCache(); return dnsCache[cacheKey]; } @@ -175,7 +167,6 @@ function lookup(hostname, options, callback) { let all = false; let verbatim = getDefaultVerbatim(); - // Parse arguments if (hostname) { validateString(hostname, 'hostname'); @@ -248,7 +239,8 @@ function lookup(hostname, options, callback) { const cacheKey = `${hostname}_${family || 'default'}`; const cachedResult = dnsCache[cacheKey]; - if (cachedResult && DateNow() - cachedResult.timestamp < 1000) { + if (cachedResult) { + cleanExpiredDnsCache(); callback(null, cachedResult.address, cachedResult.family); return {}; } From 514f8e2347609bd821fb4cceb1c2464c9408d00d Mon Sep 17 00:00:00 2001 From: kshitjj Date: Fri, 6 Oct 2023 07:51:20 +0530 Subject: [PATCH 10/11] performance: changed deleting method --- lib/dns.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index 3d7391d3269d17..f2476cf7afa020 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -31,7 +31,6 @@ const cares = internalBinding('cares_wrap'); const { isIP } = require('internal/net'); const { customPromisifyArgs } = require('internal/util'); const errors = require('internal/errors'); -const { setTimeout, unref } = require('timers'); const { bindDefaultResolver, setDefaultResolver, @@ -138,7 +137,7 @@ function cleanExpiredDnsCache() { const currentTime = DateNow(); for (const key in dnsCache) { if (currentTime - dnsCache[key].timestamp >= 1000) { - delete dnsCache[key]; + dnsCache[key] = undefined; } } } @@ -160,7 +159,6 @@ function addDnsEntry(cacheKey, address, family) { const validFamilies = [0, 4, 6]; const dnsCache = { __proto__: null }; const { DateNow } = primordials; -let cleanupTimeout = null; function lookup(hostname, options, callback) { let hints = 0; let family = 0; From 1c6a9f6a21f3165d065e9de1bcd8cd7ceb095c1d Mon Sep 17 00:00:00 2001 From: kshitjj Date: Sat, 7 Oct 2023 16:12:34 +0530 Subject: [PATCH 11/11] dns: fix test/test-tls-connect-address-family.js --- lib/dns.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/dns.js b/lib/dns.js index f2476cf7afa020..c9b913f7e38bfb 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -234,7 +234,7 @@ function lookup(hostname, options, callback) { return {}; } - const cacheKey = `${hostname}_${family || 'default'}`; + const cacheKey = `${hostname}_${family || 'default'}_${all ? 'all' : 'single'}`; const cachedResult = dnsCache[cacheKey]; if (cachedResult) { @@ -243,7 +243,6 @@ function lookup(hostname, options, callback) { return {}; } - const req = new GetAddrInfoReqWrap(); req.callback = (error, address, family) => { if (!error) {