@serve.zone/dcrouter 18.9.3 → 18.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.js +5 -4
- package/dist_ts/db/documents/classes.authentication-event.doc.d.ts +0 -1
- package/dist_ts/db/documents/classes.authentication-event.doc.js +176 -259
- package/dist_ts/db/documents/classes.email-traffic-bucket.doc.js +110 -188
- package/dist_ts/dns/manager.dns-authority.d.ts +5 -0
- package/dist_ts/dns/manager.dns-authority.js +97 -53
- package/dist_ts/dns/manager.dns.js +23 -5
- package/dist_ts/email/classes.email-domain.manager.js +14 -3
- package/dist_ts/email/classes.email-settings.manager.js +26 -6
- package/dist_ts/email/classes.mail-dns-sync.js +22 -2
- package/dist_ts/monitoring/classes.metricscache.d.ts +28 -19
- package/dist_ts/monitoring/classes.metricscache.js +133 -62
- package/dist_ts/monitoring/classes.metricsmanager.js +7 -5
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/remoteingress/classes.remoteingress-manager.js +46 -9
- package/dist_ts/security/classes.authentication-event-manager.d.ts +5 -1
- package/dist_ts/security/classes.authentication-event-manager.js +32 -22
- package/dist_ts/vpn/classes.vpn-manager.js +34 -2
- package/dist_ts_migrations/index.d.ts +4 -1
- package/dist_ts_migrations/index.js +18 -3
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/package.json +7 -6
- package/readme.hints.md +72 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +4 -3
- package/ts/db/documents/classes.authentication-event.doc.ts +64 -87
- package/ts/db/documents/classes.email-traffic-bucket.doc.ts +10 -9
- package/ts/dns/manager.dns-authority.ts +92 -54
- package/ts/dns/manager.dns.ts +20 -4
- package/ts/email/classes.email-domain.manager.ts +12 -2
- package/ts/email/classes.email-settings.manager.ts +23 -5
- package/ts/email/classes.mail-dns-sync.ts +23 -1
- package/ts/monitoring/classes.metricscache.ts +133 -79
- package/ts/monitoring/classes.metricsmanager.ts +6 -4
- package/ts/plugins.ts +2 -0
- package/ts/remoteingress/classes.remoteingress-manager.ts +42 -8
- package/ts/security/classes.authentication-event-manager.ts +31 -20
- package/ts/vpn/classes.vpn-manager.ts +32 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -1,34 +1,43 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
1
2
|
export interface ICacheEntry<T> {
|
|
2
3
|
data: T;
|
|
3
4
|
timestamp: number;
|
|
4
5
|
ttl: number;
|
|
5
6
|
}
|
|
7
|
+
export interface IMetricsCacheOptions {
|
|
8
|
+
maxAggregateEntries?: number;
|
|
9
|
+
maxSnapshotEntries?: number;
|
|
10
|
+
maxEstimatedBytes?: number;
|
|
11
|
+
maxInFlight?: number;
|
|
12
|
+
}
|
|
13
|
+
type TCacheCategory = 'aggregate' | 'snapshot';
|
|
14
|
+
/** Uses the existing TypedRequest error envelope; polling callers retain their last result. */
|
|
15
|
+
export declare class MetricsCapacityError extends plugins.typedrequest.TypedResponseError {
|
|
16
|
+
constructor();
|
|
17
|
+
}
|
|
6
18
|
export declare class MetricsCache {
|
|
7
|
-
private cache;
|
|
8
|
-
private inFlight;
|
|
9
19
|
private readonly defaultTTL;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
private readonly caches;
|
|
21
|
+
private readonly inFlight;
|
|
22
|
+
private readonly admitted;
|
|
23
|
+
private readonly maxInFlight;
|
|
24
|
+
private readonly maxEstimatedBytes;
|
|
25
|
+
private stopped;
|
|
26
|
+
private generation;
|
|
27
|
+
private activeComputations;
|
|
28
|
+
constructor(defaultTTL?: number, options?: IMetricsCacheOptions);
|
|
29
|
+
private createCache;
|
|
30
|
+
get<T>(key: string, computeFn: () => T | Promise<T>, ttl?: number, category?: TCacheCategory): T | Promise<T>;
|
|
18
31
|
invalidate(key: string): void;
|
|
19
|
-
/**
|
|
20
|
-
* Clear all cache entries
|
|
21
|
-
*/
|
|
22
32
|
clear(): void;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*/
|
|
33
|
+
start(): void;
|
|
34
|
+
stop(): Promise<void>;
|
|
26
35
|
getStats(): {
|
|
27
36
|
size: number;
|
|
28
37
|
keys: string[];
|
|
38
|
+
inFlight: number;
|
|
39
|
+
estimatedBytes: number;
|
|
29
40
|
};
|
|
30
|
-
/**
|
|
31
|
-
* Clean up expired entries
|
|
32
|
-
*/
|
|
33
41
|
cleanup(): void;
|
|
34
42
|
}
|
|
43
|
+
export {};
|
|
@@ -1,85 +1,156 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
/** Uses the existing TypedRequest error envelope; polling callers retain their last result. */
|
|
3
|
+
export class MetricsCapacityError extends plugins.typedrequest.TypedResponseError {
|
|
4
|
+
constructor() {
|
|
5
|
+
super('Metrics are busy; retry after 500 ms', { code: 'METRICS_CAPACITY', retryAfterMs: 500 });
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** Conservative retained-payload estimate, including Maps and shared object graphs. */
|
|
9
|
+
function estimateBytes(value, budget, seen = new Set()) {
|
|
10
|
+
if (budget <= 0)
|
|
11
|
+
return 1;
|
|
12
|
+
if (typeof value === 'string')
|
|
13
|
+
return 24 + value.length * 2;
|
|
14
|
+
if (typeof value !== 'object' || value === null)
|
|
15
|
+
return 16;
|
|
16
|
+
if (seen.has(value))
|
|
17
|
+
return 8;
|
|
18
|
+
seen.add(value);
|
|
19
|
+
if (ArrayBuffer.isView(value))
|
|
20
|
+
return 64 + value.byteLength;
|
|
21
|
+
if (value instanceof ArrayBuffer)
|
|
22
|
+
return 64 + value.byteLength;
|
|
23
|
+
let size = 64;
|
|
24
|
+
if (value instanceof Map) {
|
|
25
|
+
for (const [key, entry] of value) {
|
|
26
|
+
size += 32 + estimateBytes(key, budget - size, seen) + estimateBytes(entry, budget - size, seen);
|
|
27
|
+
if (size > budget)
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (value instanceof Set) {
|
|
32
|
+
for (const entry of value) {
|
|
33
|
+
size += 16 + estimateBytes(entry, budget - size, seen);
|
|
34
|
+
if (size > budget)
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
for (const key of Object.keys(value)) {
|
|
40
|
+
size += 24 + key.length * 2 + estimateBytes(value[key], budget - size, seen);
|
|
41
|
+
if (size > budget)
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return size;
|
|
46
|
+
}
|
|
1
47
|
export class MetricsCache {
|
|
2
|
-
cache = new Map();
|
|
3
|
-
inFlight = new Map();
|
|
4
48
|
defaultTTL;
|
|
5
|
-
|
|
49
|
+
caches;
|
|
50
|
+
inFlight = new Map();
|
|
51
|
+
admitted = new Set();
|
|
52
|
+
maxInFlight;
|
|
53
|
+
maxEstimatedBytes;
|
|
54
|
+
stopped = false;
|
|
55
|
+
generation = 0;
|
|
56
|
+
activeComputations = 0;
|
|
57
|
+
constructor(defaultTTL = 500, options = {}) {
|
|
6
58
|
this.defaultTTL = defaultTTL;
|
|
59
|
+
this.maxInFlight = options.maxInFlight ?? 32;
|
|
60
|
+
this.maxEstimatedBytes = options.maxEstimatedBytes ?? 8 * 1024 * 1024;
|
|
61
|
+
this.caches = {
|
|
62
|
+
aggregate: this.createCache(options.maxAggregateEntries ?? 256),
|
|
63
|
+
snapshot: this.createCache(options.maxSnapshotEntries ?? 64),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
createCache(max) {
|
|
67
|
+
return new plugins.lruCache.LRUCache({
|
|
68
|
+
max,
|
|
69
|
+
maxSize: this.maxEstimatedBytes,
|
|
70
|
+
// lru-cache owns bounded, unref'ed expiry timers, including idle keys.
|
|
71
|
+
ttlAutopurge: true,
|
|
72
|
+
ttlResolution: 0,
|
|
73
|
+
sizeCalculation: (entry, key) => 64 + key.length * 2 + estimateBytes(entry.data, this.maxEstimatedBytes),
|
|
74
|
+
});
|
|
7
75
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const cached =
|
|
13
|
-
|
|
14
|
-
const actualTTL = ttl ?? this.defaultTTL;
|
|
15
|
-
if (cached && (now - cached.timestamp) < cached.ttl) {
|
|
76
|
+
get(key, computeFn, ttl = this.defaultTTL, category = 'aggregate') {
|
|
77
|
+
if (this.stopped)
|
|
78
|
+
throw new Error('Metrics cache is stopped');
|
|
79
|
+
const cache = this.caches[category];
|
|
80
|
+
const cached = cache.get(key);
|
|
81
|
+
if (cached && Date.now() - cached.timestamp < cached.ttl)
|
|
16
82
|
return cached.data;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const pending = this.inFlight.get(key);
|
|
22
|
-
if (pending) {
|
|
83
|
+
cache.delete(key);
|
|
84
|
+
const flightKey = JSON.stringify([category, key]);
|
|
85
|
+
const pending = this.inFlight.get(flightKey);
|
|
86
|
+
if (pending)
|
|
23
87
|
return pending;
|
|
88
|
+
if (this.activeComputations >= this.maxInFlight)
|
|
89
|
+
throw new MetricsCapacityError();
|
|
90
|
+
const generation = this.generation;
|
|
91
|
+
this.activeComputations++;
|
|
92
|
+
let result;
|
|
93
|
+
try {
|
|
94
|
+
result = computeFn();
|
|
24
95
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
let inFlightPromise;
|
|
29
|
-
inFlightPromise = result.then((data) => {
|
|
30
|
-
if (this.inFlight.get(key) === inFlightPromise) {
|
|
31
|
-
this.inFlight.delete(key);
|
|
32
|
-
this.cache.set(key, { data, timestamp: Date.now(), ttl: actualTTL });
|
|
33
|
-
}
|
|
34
|
-
return data;
|
|
35
|
-
}, (error) => {
|
|
36
|
-
if (this.inFlight.get(key) === inFlightPromise) {
|
|
37
|
-
this.inFlight.delete(key);
|
|
38
|
-
this.cache.delete(key);
|
|
39
|
-
}
|
|
40
|
-
throw error;
|
|
41
|
-
});
|
|
42
|
-
this.inFlight.set(key, inFlightPromise);
|
|
43
|
-
return inFlightPromise;
|
|
96
|
+
catch (error) {
|
|
97
|
+
this.activeComputations--;
|
|
98
|
+
throw error;
|
|
44
99
|
}
|
|
45
|
-
|
|
46
|
-
this.
|
|
100
|
+
const remember = (data) => {
|
|
101
|
+
if (!this.stopped && generation === this.generation && ttl > 0) {
|
|
102
|
+
cache.set(key, { data, timestamp: Date.now(), ttl }, { ttl });
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
if (!(result instanceof Promise)) {
|
|
106
|
+
this.activeComputations--;
|
|
107
|
+
remember(result);
|
|
47
108
|
return result;
|
|
48
109
|
}
|
|
110
|
+
const run = result.then((data) => {
|
|
111
|
+
if (this.inFlight.get(flightKey) === run)
|
|
112
|
+
remember(data);
|
|
113
|
+
return data;
|
|
114
|
+
}).finally(() => {
|
|
115
|
+
this.activeComputations--;
|
|
116
|
+
this.admitted.delete(run);
|
|
117
|
+
if (this.inFlight.get(flightKey) === run)
|
|
118
|
+
this.inFlight.delete(flightKey);
|
|
119
|
+
});
|
|
120
|
+
this.admitted.add(run);
|
|
121
|
+
this.inFlight.set(flightKey, run);
|
|
122
|
+
return run;
|
|
49
123
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Invalidate a specific cache entry
|
|
52
|
-
*/
|
|
53
124
|
invalidate(key) {
|
|
54
|
-
|
|
55
|
-
|
|
125
|
+
for (const category of ['aggregate', 'snapshot']) {
|
|
126
|
+
this.caches[category].delete(key);
|
|
127
|
+
this.inFlight.delete(JSON.stringify([category, key]));
|
|
128
|
+
}
|
|
56
129
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Clear all cache entries
|
|
59
|
-
*/
|
|
60
130
|
clear() {
|
|
61
|
-
this.
|
|
131
|
+
this.generation++;
|
|
132
|
+
this.caches.aggregate.clear();
|
|
133
|
+
this.caches.snapshot.clear();
|
|
62
134
|
this.inFlight.clear();
|
|
135
|
+
// Invalidated work retains its admission slot until it actually settles.
|
|
136
|
+
}
|
|
137
|
+
start() { this.stopped = false; }
|
|
138
|
+
async stop() {
|
|
139
|
+
this.stopped = true;
|
|
140
|
+
this.clear();
|
|
141
|
+
await Promise.allSettled(this.admitted);
|
|
63
142
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Get cache statistics
|
|
66
|
-
*/
|
|
67
143
|
getStats() {
|
|
68
144
|
return {
|
|
69
|
-
size: this.
|
|
70
|
-
keys:
|
|
145
|
+
size: this.caches.aggregate.size + this.caches.snapshot.size,
|
|
146
|
+
keys: [...this.caches.aggregate.keys(), ...this.caches.snapshot.keys()],
|
|
147
|
+
inFlight: this.activeComputations,
|
|
148
|
+
estimatedBytes: this.caches.aggregate.calculatedSize + this.caches.snapshot.calculatedSize,
|
|
71
149
|
};
|
|
72
150
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Clean up expired entries
|
|
75
|
-
*/
|
|
76
151
|
cleanup() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (now - entry.timestamp > entry.ttl) {
|
|
80
|
-
this.cache.delete(key);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
152
|
+
this.caches.aggregate.purgeStale();
|
|
153
|
+
this.caches.snapshot.purgeStale();
|
|
83
154
|
}
|
|
84
155
|
}
|
|
85
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
156
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tZXRyaWNzY2FjaGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tb25pdG9yaW5nL2NsYXNzZXMubWV0cmljc2NhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBaUJ6QywrRkFBK0Y7QUFDL0YsTUFBTSxPQUFPLG9CQUFxQixTQUFRLE9BQU8sQ0FBQyxZQUFZLENBQUMsa0JBQWtCO0lBQy9FO1FBQ0UsS0FBSyxDQUFDLHNDQUFzQyxFQUFFLEVBQUUsSUFBSSxFQUFFLGtCQUFrQixFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQ2pHLENBQUM7Q0FDRjtBQUVELHVGQUF1RjtBQUN2RixTQUFTLGFBQWEsQ0FBQyxLQUFjLEVBQUUsTUFBYyxFQUFFLE9BQU8sSUFBSSxHQUFHLEVBQVU7SUFDN0UsSUFBSSxNQUFNLElBQUksQ0FBQztRQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzFCLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUTtRQUFFLE9BQU8sRUFBRSxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQzVELElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFDM0QsSUFBSSxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQztRQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzlCLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDaEIsSUFBSSxXQUFXLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztRQUFFLE9BQU8sRUFBRSxHQUFHLEtBQUssQ0FBQyxVQUFVLENBQUM7SUFDNUQsSUFBSSxLQUFLLFlBQVksV0FBVztRQUFFLE9BQU8sRUFBRSxHQUFHLEtBQUssQ0FBQyxVQUFVLENBQUM7SUFDL0QsSUFBSSxJQUFJLEdBQUcsRUFB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
|