UNPKG

consul-resolver

Version:

A load balancer for Consul services with Redis-based metrics

135 lines (134 loc) 5.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DNSManager = void 0; const utils_1 = require("@brimble/utils"); const dns_query_1 = require("dns-query"); const algorithms_1 = require("../algorithms"); const types_1 = require("../types"); class DNSManager { constructor(redis, cachePrefix, cacheTTL, cacheEnabled, debug, dnsEndpoints, dnsTimeout, dnsRetries) { this.redis = redis; this.cachePrefix = cachePrefix; this.cacheTTL = cacheTTL; this.cacheEnabled = cacheEnabled; this.debug = debug; this.dnsEndpoints = dnsEndpoints; this.dnsTimeout = dnsTimeout; this.dnsRetries = dnsRetries; } getDNSCacheKey(service) { return `${this.cachePrefix}:dns:${service}`; } async resolveDNS(service) { var _a, _b, _c, _d; const cacheKey = this.getDNSCacheKey(service); if (this.cacheEnabled) { try { const cachedData = await Promise.race([ (_a = this.redis) === null || _a === void 0 ? void 0 : _a.get(cacheKey), new Promise((_, reject) => setTimeout(() => reject(new Error('Redis get timeout')), 200)) ]); if (cachedData) { if (this.debug) { utils_1.log.debug(`DNS cache hit for ${service}`); } return JSON.parse(cachedData); } } catch (e) { if (this.debug) utils_1.log.error('Redis get error or timeout:', e); } } try { const result = await (0, dns_query_1.query)({ question: { type: 'SRV', name: service } }, { endpoints: this.dnsEndpoints, timeout: (_b = this.dnsTimeout) !== null && _b !== void 0 ? _b : 1500, retries: (_c = this.dnsRetries) !== null && _c !== void 0 ? _c : 2 }); if (this.debug) { utils_1.log.debug("DNS QUERY RESULT", result); } if (!result.answers || result.answers.length === 0) { if (this.debug) { utils_1.log.debug(`No SRV records found for ${service}`); } return []; } const additionalsByName = {}; if (result.additionals) { for (const additional of result.additionals) { if (additional.type === 'A') { additionalsByName[additional.name] = additional; } } } const records = result.answers.map((answer) => { const target = answer.data.target; const aRecord = additionalsByName[target]; return { name: target, ip: (aRecord === null || aRecord === void 0 ? void 0 : aRecord.data) || '', port: answer.data.port, priority: answer.data.priority, weight: answer.data.weight }; }).filter(record => record.ip); if (this.cacheEnabled) { try { await Promise.race([ (_d = this.redis) === null || _d === void 0 ? void 0 : _d.set(cacheKey, JSON.stringify(records), 'EX', this.cacheTTL), new Promise((_, reject) => setTimeout(() => reject(new Error('Redis set timeout')), 200)) ]); } catch (e) { if (this.debug) utils_1.log.error('Redis set error or timeout:', e); } } return records; } catch (error) { if (this.debug) { utils_1.log.error('DNS resolution error:', error); } return []; } } sortByPriority(records) { return [...records].sort((a, b) => (a.priority || 0) - (b.priority || 0)); } selectFromSrvRecords(records, algorithm, currentIndex) { if (!records || records.length === 0) { return { selected: null, nextIndex: currentIndex }; } switch (algorithm) { case types_1.SelectionAlgorithm.RoundRobin: const rrResult = (0, algorithms_1.roundRobinSrvSelection)(records, currentIndex); if (!rrResult) { return { selected: null, nextIndex: currentIndex }; } return { selected: rrResult.selected, nextIndex: rrResult.nextIndex }; case types_1.SelectionAlgorithm.WeightedRoundRobin: const wrResult = (0, algorithms_1.weightedSrvRecordSelection)(records, currentIndex); if (!wrResult) { return { selected: null, nextIndex: currentIndex }; } return { selected: wrResult.selected, nextIndex: wrResult.nextIndex }; case types_1.SelectionAlgorithm.LeastConnection: const lcResult = (0, algorithms_1.roundRobinSrvSelection)(records, currentIndex); if (!lcResult) { return { selected: null, nextIndex: currentIndex }; } return { selected: lcResult.selected, nextIndex: lcResult.nextIndex }; default: return { selected: records[0], nextIndex: currentIndex }; } } } exports.DNSManager = DNSManager;