Compare commits

...

4 Commits

Author SHA1 Message Date
0195a21f30 v11.10.5
Some checks failed
Docker (tags) / security (push) Failing after 4s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2026-03-26 07:10:59 +00:00
4dca747386 fix(build): rename smart tooling config to .smartconfig.json and update package references 2026-03-26 07:10:59 +00:00
7663f502fa v11.10.4
Some checks failed
Docker (tags) / security (push) Failing after 3s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2026-03-24 13:40:28 +00:00
104cd417d8 fix(monitoring): handle multiple protocol cache entries per backend in metrics output 2026-03-24 13:40:28 +00:00
9 changed files with 575 additions and 507 deletions

View File

@@ -1,7 +1,7 @@
{ {
"json.schemas": [ "json.schemas": [
{ {
"fileMatch": ["/npmextra.json"], "fileMatch": ["/.smartconfig.json"],
"schema": { "schema": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -1,5 +1,19 @@
# Changelog # Changelog
## 2026-03-26 - 11.10.5 - fix(build)
rename smart tooling config to .smartconfig.json and update package references
- Moves the shared tool configuration from npmextra.json to .smartconfig.json.
- Updates package.json published files and documentation to reference the new config file.
- Refreshes several development and runtime dependency versions alongside the config migration.
## 2026-03-24 - 11.10.4 - fix(monitoring)
handle multiple protocol cache entries per backend in metrics output
- Group detected protocol cache entries by backend host and port so multiple domain-specific records are preserved.
- Emit one backend metrics row per cached domain and avoid dropping unmatched protocol cache entries by tracking seen entries with a composite host:port:domain key.
- Use cached protocol values when available while keeping backend-only rows for metrics without protocol cache data.
## 2026-03-23 - 11.10.3 - fix(deps) ## 2026-03-23 - 11.10.3 - fix(deps)
bump tstest, smartmetrics, and taskbuffer to latest patch releases bump tstest, smartmetrics, and taskbuffer to latest patch releases

View File

@@ -1,7 +1,7 @@
{ {
"name": "@serve.zone/dcrouter", "name": "@serve.zone/dcrouter",
"private": false, "private": false,
"version": "11.10.3", "version": "11.10.5",
"description": "A multifaceted routing service handling mail and SMS delivery functions.", "description": "A multifaceted routing service handling mail and SMS delivery functions.",
"type": "module", "type": "module",
"exports": { "exports": {
@@ -22,11 +22,11 @@
"watch": "tswatch" "watch": "tswatch"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^4.3.0", "@git.zone/tsbuild": "^4.4.0",
"@git.zone/tsbundle": "^2.9.1", "@git.zone/tsbundle": "^2.10.0",
"@git.zone/tsrun": "^2.0.1", "@git.zone/tsrun": "^2.0.2",
"@git.zone/tstest": "^3.5.1", "@git.zone/tstest": "^3.6.0",
"@git.zone/tswatch": "^3.3.0", "@git.zone/tswatch": "^3.3.2",
"@types/node": "^25.5.0" "@types/node": "^25.5.0"
}, },
"dependencies": { "dependencies": {
@@ -35,13 +35,13 @@
"@api.global/typedserver": "^8.4.6", "@api.global/typedserver": "^8.4.6",
"@api.global/typedsocket": "^4.1.2", "@api.global/typedsocket": "^4.1.2",
"@apiclient.xyz/cloudflare": "^7.1.0", "@apiclient.xyz/cloudflare": "^7.1.0",
"@design.estate/dees-catalog": "^3.48.5", "@design.estate/dees-catalog": "^3.49.0",
"@design.estate/dees-element": "^2.2.3", "@design.estate/dees-element": "^2.2.3",
"@push.rocks/lik": "^6.4.0", "@push.rocks/lik": "^6.4.0",
"@push.rocks/projectinfo": "^5.0.2", "@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/qenv": "^6.1.3", "@push.rocks/qenv": "^6.1.3",
"@push.rocks/smartacme": "^9.3.0", "@push.rocks/smartacme": "^9.3.0",
"@push.rocks/smartdata": "^7.1.0", "@push.rocks/smartdata": "^7.1.2",
"@push.rocks/smartdns": "^7.9.0", "@push.rocks/smartdns": "^7.9.0",
"@push.rocks/smartfile": "^13.1.2", "@push.rocks/smartfile": "^13.1.2",
"@push.rocks/smartguard": "^3.1.0", "@push.rocks/smartguard": "^3.1.0",
@@ -53,17 +53,17 @@
"@push.rocks/smartnetwork": "^4.4.0", "@push.rocks/smartnetwork": "^4.4.0",
"@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3", "@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartproxy": "^26.2.1", "@push.rocks/smartproxy": "^26.2.4",
"@push.rocks/smartradius": "^1.1.1", "@push.rocks/smartradius": "^1.1.1",
"@push.rocks/smartrequest": "^5.0.1", "@push.rocks/smartrequest": "^5.0.1",
"@push.rocks/smartrx": "^3.0.10", "@push.rocks/smartrx": "^3.0.10",
"@push.rocks/smartstate": "^2.2.0", "@push.rocks/smartstate": "^2.2.1",
"@push.rocks/smartunique": "^3.0.9", "@push.rocks/smartunique": "^3.0.9",
"@push.rocks/taskbuffer": "^8.0.2", "@push.rocks/taskbuffer": "^8.0.2",
"@serve.zone/catalog": "^2.9.0", "@serve.zone/catalog": "^2.9.0",
"@serve.zone/interfaces": "^5.3.0", "@serve.zone/interfaces": "^5.3.0",
"@serve.zone/remoteingress": "^4.14.1", "@serve.zone/remoteingress": "^4.14.2",
"@tsclass/tsclass": "^9.4.0", "@tsclass/tsclass": "^9.5.0",
"lru-cache": "^11.2.7", "lru-cache": "^11.2.7",
"uuid": "^13.0.0" "uuid": "^13.0.0"
}, },
@@ -112,7 +112,7 @@
"dist_ts_apiclient/**/*", "dist_ts_apiclient/**/*",
"assets/**/*", "assets/**/*",
"cli.js", "cli.js",
"npmextra.json", ".smartconfig.json",
"readme.md" "readme.md"
] ]
} }

937
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -133,7 +133,7 @@ The project now uses tswatch for development:
```bash ```bash
pnpm run watch pnpm run watch
``` ```
Configuration in `npmextra.json`: Configuration in `.smartconfig.json`:
```json ```json
{ {
"@git.zone/tswatch": { "@git.zone/tswatch": {

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/dcrouter', name: '@serve.zone/dcrouter',
version: '11.10.3', version: '11.10.5',
description: 'A multifaceted routing service handling mail and SMS delivery functions.' description: 'A multifaceted routing service handling mail and SMS delivery functions.'
} }

View File

@@ -595,47 +595,84 @@ export class MetricsManager {
const backendMetrics = proxyMetrics.backends.byBackend(); const backendMetrics = proxyMetrics.backends.byBackend();
const protocolCache = proxyMetrics.backends.detectedProtocols(); const protocolCache = proxyMetrics.backends.detectedProtocols();
// Index protocol cache by "host:port" // Group protocol cache entries by host:port so we can match them to backend metrics.
const cacheByKey = new Map<string, (typeof protocolCache)[number]>(); // The protocol cache is keyed by (host, port, domain) in Rust, so the same host:port
// can have multiple entries for different domains.
const cacheByBackend = new Map<string, (typeof protocolCache)[number][]>();
for (const entry of protocolCache) { for (const entry of protocolCache) {
cacheByKey.set(`${entry.host}:${entry.port}`, entry); const backendKey = `${entry.host}:${entry.port}`;
let entries = cacheByBackend.get(backendKey);
if (!entries) {
entries = [];
cacheByBackend.set(backendKey, entries);
}
entries.push(entry);
} }
const backends: Array<any> = []; const backends: Array<any> = [];
const seen = new Set<string>(); const seenCacheKeys = new Set<string>();
for (const [key, bm] of backendMetrics) { for (const [key, bm] of backendMetrics) {
seen.add(key); const cacheEntries = cacheByBackend.get(key);
const cache = cacheByKey.get(key); if (!cacheEntries || cacheEntries.length === 0) {
backends.push({ // No protocol cache entry — emit one row with backend metrics only
backend: key, backends.push({
domain: cache?.domain ?? null, backend: key,
protocol: bm.protocol, domain: null,
activeConnections: bm.activeConnections, protocol: bm.protocol,
totalConnections: bm.totalConnections, activeConnections: bm.activeConnections,
connectErrors: bm.connectErrors, totalConnections: bm.totalConnections,
handshakeErrors: bm.handshakeErrors, connectErrors: bm.connectErrors,
requestErrors: bm.requestErrors, handshakeErrors: bm.handshakeErrors,
avgConnectTimeMs: Math.round(bm.avgConnectTimeMs * 10) / 10, requestErrors: bm.requestErrors,
poolHitRate: Math.round(bm.poolHitRate * 1000) / 1000, avgConnectTimeMs: Math.round(bm.avgConnectTimeMs * 10) / 10,
h2Failures: bm.h2Failures, poolHitRate: Math.round(bm.poolHitRate * 1000) / 1000,
h2Suppressed: cache?.h2Suppressed ?? false, h2Failures: bm.h2Failures,
h3Suppressed: cache?.h3Suppressed ?? false, h2Suppressed: false,
h2CooldownRemainingSecs: cache?.h2CooldownRemainingSecs ?? null, h3Suppressed: false,
h3CooldownRemainingSecs: cache?.h3CooldownRemainingSecs ?? null, h2CooldownRemainingSecs: null,
h2ConsecutiveFailures: cache?.h2ConsecutiveFailures ?? null, h3CooldownRemainingSecs: null,
h3ConsecutiveFailures: cache?.h3ConsecutiveFailures ?? null, h2ConsecutiveFailures: null,
h3Port: cache?.h3Port ?? null, h3ConsecutiveFailures: null,
cacheAgeSecs: cache?.ageSecs ?? null, h3Port: null,
}); cacheAgeSecs: null,
});
} else {
// One row per domain, each enriched with the shared backend metrics
for (const cache of cacheEntries) {
const compositeKey = `${cache.host}:${cache.port}:${cache.domain ?? ''}`;
seenCacheKeys.add(compositeKey);
backends.push({
backend: key,
domain: cache.domain ?? null,
protocol: cache.protocol ?? bm.protocol,
activeConnections: bm.activeConnections,
totalConnections: bm.totalConnections,
connectErrors: bm.connectErrors,
handshakeErrors: bm.handshakeErrors,
requestErrors: bm.requestErrors,
avgConnectTimeMs: Math.round(bm.avgConnectTimeMs * 10) / 10,
poolHitRate: Math.round(bm.poolHitRate * 1000) / 1000,
h2Failures: bm.h2Failures,
h2Suppressed: cache.h2Suppressed,
h3Suppressed: cache.h3Suppressed,
h2CooldownRemainingSecs: cache.h2CooldownRemainingSecs,
h3CooldownRemainingSecs: cache.h3CooldownRemainingSecs,
h2ConsecutiveFailures: cache.h2ConsecutiveFailures,
h3ConsecutiveFailures: cache.h3ConsecutiveFailures,
h3Port: cache.h3Port,
cacheAgeSecs: cache.ageSecs,
});
}
}
} }
// Include protocol cache entries with no matching backend metric // Include protocol cache entries with no matching backend metric
for (const entry of protocolCache) { for (const entry of protocolCache) {
const key = `${entry.host}:${entry.port}`; const compositeKey = `${entry.host}:${entry.port}:${entry.domain ?? ''}`;
if (!seen.has(key)) { if (!seenCacheKeys.has(compositeKey)) {
backends.push({ backends.push({
backend: key, backend: `${entry.host}:${entry.port}`,
domain: entry.domain, domain: entry.domain,
protocol: entry.protocol, protocol: entry.protocol,
activeConnections: 0, activeConnections: 0,

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/dcrouter', name: '@serve.zone/dcrouter',
version: '11.10.3', version: '11.10.5',
description: 'A multifaceted routing service handling mail and SMS delivery functions.' description: 'A multifaceted routing service handling mail and SMS delivery functions.'
} }