consul-resolver
Version:
A load balancer for Consul services with Redis-based metrics
135 lines (134 loc) • 5.64 kB
JavaScript
;
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;