From 3945114c0baae6acfb6a4f07f6193086cd5ed19e Mon Sep 17 00:00:00 2001 From: Jayc Date: Wed, 10 Apr 2024 02:11:01 +0530 Subject: [PATCH 1/2] chore: add caching layer to dns resolved ips --- src/util/utils.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/util/utils.js b/src/util/utils.js index d74603dd7a..f74dadf5aa 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -5,6 +5,7 @@ const { Resolver } = require('dns').promises; const fetch = require('node-fetch'); const util = require('util'); +const NodeCache = require('node-cache'); const logger = require('../logger'); const stats = require('./stats'); @@ -16,11 +17,30 @@ const LOCAL_HOST_NAMES_LIST = ['localhost', '127.0.0.1', '[::]', '[::1]']; const LOCALHOST_OCTET = '127.'; const RECORD_TYPE_A = 4; // ipv4 +const DNS_CACHE_ENABLED = process.env.DNS_CACHE_ENABLED === 'true'; +const DNS_CACHE_TTL = process.env.DNS_CACHE_TTL ? parseInt(process.env.DNS_CACHE_TTL, 10) : 300; +const dnsCache = new NodeCache({ + useClones: false, + stdTTL: DNS_CACHE_TTL, + checkperiod: DNS_CACHE_TTL, +}); + +const fetchResolvedIps = async (hostname) => { + let ips = dnsCache.get(hostname); + if (ips === undefined) { + ips = await resolver.resolve4(hostname); + if (ips?.length > 0) { + dnsCache.set(hostname, ips); + } + } + return ips; +}; + const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { let ips; const resolveStartTime = new Date(); try { - ips = await resolver.resolve4(hostname); + ips = DNS_CACHE_ENABLED ? await fetchResolvedIps(hostname) : await resolver.resolve4(hostname); } catch (error) { logger.error(`DNS Error Code: ${error.code} | Message : ${error.message}`); stats.timing('fetch_dns_resolve_time', resolveStartTime, { From 14d3922fd10856222f0124e265830b7b614d5ead Mon Sep 17 00:00:00 2001 From: Jayc Date: Thu, 25 Apr 2024 23:18:23 +0530 Subject: [PATCH 2/2] chore: include ttl value in dns cache --- src/util/prometheus.js | 2 +- src/util/utils.js | 44 +++++++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/util/prometheus.js b/src/util/prometheus.js index 882dff9e75..7a1adecd40 100644 --- a/src/util/prometheus.js +++ b/src/util/prometheus.js @@ -756,7 +756,7 @@ class Prometheus { name: 'fetch_dns_resolve_time', help: 'fetch_dns_resolve_time', type: 'histogram', - labelNames: ['transformerVersionId', 'error'], + labelNames: ['transformerVersionId', 'error', 'dnsHit'], }, { name: 'geo_call_duration', diff --git a/src/util/utils.js b/src/util/utils.js index f74dadf5aa..893098b04a 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -25,22 +25,29 @@ const dnsCache = new NodeCache({ checkperiod: DNS_CACHE_TTL, }); -const fetchResolvedIps = async (hostname) => { - let ips = dnsCache.get(hostname); - if (ips === undefined) { - ips = await resolver.resolve4(hostname); - if (ips?.length > 0) { - dnsCache.set(hostname, ips); - } - } - return ips; +const fetchResolvedIp = async (hostname) => { + // ex: [{ address: '108.157.0.0', ttl: 600 }] + const addresses = await resolver.resolve4(hostname, { ttl: true }); + return addresses.length > 0 ? addresses[0] : {}; }; const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { - let ips; + let ip; const resolveStartTime = new Date(); + let dnsHit = false; try { - ips = DNS_CACHE_ENABLED ? await fetchResolvedIps(hostname) : await resolver.resolve4(hostname); + if (DNS_CACHE_ENABLED) { + ip = dnsCache.get(hostname); + if (ip !== undefined) { + dnsHit = true; + } else { + const { address, ttl } = await fetchResolvedIp(hostname); + ip = address; + dnsCache.set(hostname, ip, ttl || DNS_CACHE_TTL); + } + } + const { address } = await fetchResolvedIp(hostname); + ip = address; } catch (error) { logger.error(`DNS Error Code: ${error.code} | Message : ${error.message}`); stats.timing('fetch_dns_resolve_time', resolveStartTime, { @@ -50,22 +57,19 @@ const staticLookup = (transformerVersionId) => async (hostname, _, cb) => { cb(null, `unable to resolve IP address for ${hostname}`, RECORD_TYPE_A); return; } - stats.timing('fetch_dns_resolve_time', resolveStartTime, { transformerVersionId }); + stats.timing('fetch_dns_resolve_time', resolveStartTime, { transformerVersionId, dnsHit }); - if (ips.length === 0) { + if (!ip) { cb(null, `resolved empty list of IP address for ${hostname}`, RECORD_TYPE_A); return; } - // eslint-disable-next-line no-restricted-syntax - for (const ip of ips) { - if (ip.startsWith(LOCALHOST_OCTET)) { - cb(null, `cannot use ${ip} as IP address`, RECORD_TYPE_A); - return; - } + if (ip.startsWith(LOCALHOST_OCTET)) { + cb(null, `cannot use ${ip} as IP address`, RECORD_TYPE_A); + return; } - cb(null, ips[0], RECORD_TYPE_A); + cb(null, ip, RECORD_TYPE_A); }; const httpAgentWithDnsLookup = (scheme, transformerVersionId) => {