fix(monitoring): improve domain activity aggregation for multi-domain and wildcard routes
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/dcrouter',
|
||||
version: '13.15.0',
|
||||
version: '13.15.1',
|
||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||
}
|
||||
|
||||
@@ -724,8 +724,8 @@ export class MetricsManager {
|
||||
const connectionsByRoute = proxyMetrics.connections.byRoute();
|
||||
const throughputByRoute = proxyMetrics.throughput.byRoute();
|
||||
|
||||
// Map route name → primary domain using dcrouter's route configs
|
||||
const routeToDomain = new Map<string, string>();
|
||||
// Map route name → ALL its domains (not just the first one)
|
||||
const routeDomains = new Map<string, string[]>();
|
||||
if (this.dcRouter.smartProxy) {
|
||||
for (const route of this.dcRouter.smartProxy.routeManager.getRoutes()) {
|
||||
if (!route.name || !route.match.domains) continue;
|
||||
@@ -733,29 +733,101 @@ export class MetricsManager {
|
||||
? route.match.domains
|
||||
: [route.match.domains];
|
||||
if (domains.length > 0) {
|
||||
routeToDomain.set(route.name, domains[0]);
|
||||
routeDomains.set(route.name, domains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate metrics by domain
|
||||
// Use protocol cache to discover actual active domains (resolves wildcards)
|
||||
const activeDomains = new Set<string>();
|
||||
const domainToBackend = new Map<string, string>(); // domain → host:port
|
||||
for (const entry of protocolCache) {
|
||||
if (entry.domain) {
|
||||
activeDomains.add(entry.domain);
|
||||
domainToBackend.set(entry.domain, `${entry.host}:${entry.port}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Build reverse map: domain → route name(s) that handle it
|
||||
// For concrete domains: direct lookup from route config
|
||||
// For wildcard patterns: match active domains from protocol cache
|
||||
const domainToRoutes = new Map<string, string[]>();
|
||||
for (const [routeName, domains] of routeDomains) {
|
||||
for (const pattern of domains) {
|
||||
if (pattern.includes('*')) {
|
||||
// Wildcard pattern — match against active domains from protocol cache
|
||||
const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '[^.]+') + '$');
|
||||
for (const activeDomain of activeDomains) {
|
||||
if (regex.test(activeDomain)) {
|
||||
const existing = domainToRoutes.get(activeDomain);
|
||||
if (existing) { existing.push(routeName); }
|
||||
else { domainToRoutes.set(activeDomain, [routeName]); }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Concrete domain
|
||||
const existing = domainToRoutes.get(pattern);
|
||||
if (existing) { existing.push(routeName); }
|
||||
else { domainToRoutes.set(pattern, [routeName]); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate metrics per domain
|
||||
// For each domain, sum metrics from all routes that serve it,
|
||||
// divided by the number of domains each route serves
|
||||
const domainAgg = new Map<string, {
|
||||
activeConnections: number;
|
||||
bytesInPerSec: number;
|
||||
bytesOutPerSec: number;
|
||||
routeCount: number;
|
||||
}>();
|
||||
|
||||
// Track which routes are accounted for
|
||||
const accountedRoutes = new Set<string>();
|
||||
|
||||
for (const [domain, routeNames] of domainToRoutes) {
|
||||
let totalConns = 0;
|
||||
let totalIn = 0;
|
||||
let totalOut = 0;
|
||||
|
||||
for (const routeName of routeNames) {
|
||||
accountedRoutes.add(routeName);
|
||||
const conns = connectionsByRoute.get(routeName) || 0;
|
||||
const tp = throughputByRoute.get(routeName) || { in: 0, out: 0 };
|
||||
// Count how many resolved domains share this route
|
||||
let domainsInRoute = 0;
|
||||
for (const [, routes] of domainToRoutes) {
|
||||
if (routes.includes(routeName)) domainsInRoute++;
|
||||
}
|
||||
const share = Math.max(domainsInRoute, 1);
|
||||
totalConns += conns / share;
|
||||
totalIn += tp.in / share;
|
||||
totalOut += tp.out / share;
|
||||
}
|
||||
|
||||
domainAgg.set(domain, {
|
||||
activeConnections: Math.round(totalConns),
|
||||
bytesInPerSec: totalIn,
|
||||
bytesOutPerSec: totalOut,
|
||||
routeCount: routeNames.length,
|
||||
});
|
||||
}
|
||||
|
||||
// Include routes with no domain config (fallback: use route name)
|
||||
for (const [routeName, activeConns] of connectionsByRoute) {
|
||||
const domain = routeToDomain.get(routeName) || routeName;
|
||||
if (accountedRoutes.has(routeName)) continue;
|
||||
if (routeDomains.has(routeName)) continue; // has domains but no traffic matched
|
||||
const tp = throughputByRoute.get(routeName) || { in: 0, out: 0 };
|
||||
const existing = domainAgg.get(domain);
|
||||
if (activeConns === 0 && tp.in === 0 && tp.out === 0) continue;
|
||||
const existing = domainAgg.get(routeName);
|
||||
if (existing) {
|
||||
existing.activeConnections += activeConns;
|
||||
existing.bytesInPerSec += tp.in;
|
||||
existing.bytesOutPerSec += tp.out;
|
||||
existing.routeCount++;
|
||||
} else {
|
||||
domainAgg.set(domain, {
|
||||
domainAgg.set(routeName, {
|
||||
activeConnections: activeConns,
|
||||
bytesInPerSec: tp.in,
|
||||
bytesOutPerSec: tp.out,
|
||||
@@ -763,6 +835,7 @@ export class MetricsManager {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const domainActivity = Array.from(domainAgg.entries())
|
||||
.map(([domain, data]) => ({
|
||||
domain,
|
||||
|
||||
Reference in New Issue
Block a user