@push.rocks/smartproxy 19.6.10 → 19.6.11
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/dist_ts/proxies/smart-proxy/metrics-collector.js +80 -38
- package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +5 -0
- package/package.json +1 -1
- package/readme.hints.md +20 -3
- package/ts/proxies/smart-proxy/metrics-collector.ts +84 -39
- package/ts/proxies/smart-proxy/models/metrics-types.ts +6 -0
|
@@ -75,32 +75,46 @@ export class MetricsCollector {
|
|
|
75
75
|
const routeThroughput = new Map();
|
|
76
76
|
const now = Date.now();
|
|
77
77
|
const windowStart = now - (windowSeconds * 1000);
|
|
78
|
-
// Aggregate bytes by route
|
|
78
|
+
// Aggregate bytes by route - calculate actual bytes transferred in window
|
|
79
79
|
const routeData = new Map();
|
|
80
80
|
for (const [_, tracker] of this.connectionByteTrackers) {
|
|
81
81
|
// Only include connections that were active within the window
|
|
82
|
-
if (tracker.lastUpdate > windowStart
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
if (tracker.lastUpdate > windowStart) {
|
|
83
|
+
let windowBytesIn = 0;
|
|
84
|
+
let windowBytesOut = 0;
|
|
85
|
+
if (tracker.windowSnapshots && tracker.windowSnapshots.length > 0) {
|
|
86
|
+
// Find the earliest snapshot within or just before the window
|
|
87
|
+
let startSnapshot = { timestamp: tracker.startTime, bytesIn: 0, bytesOut: 0 };
|
|
88
|
+
for (const snapshot of tracker.windowSnapshots) {
|
|
89
|
+
if (snapshot.timestamp <= windowStart) {
|
|
90
|
+
startSnapshot = snapshot;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Calculate bytes transferred since window start
|
|
97
|
+
windowBytesIn = tracker.bytesIn - startSnapshot.bytesIn;
|
|
98
|
+
windowBytesOut = tracker.bytesOut - startSnapshot.bytesOut;
|
|
93
99
|
}
|
|
100
|
+
else if (tracker.startTime > windowStart) {
|
|
101
|
+
// Connection started within window, use all its bytes
|
|
102
|
+
windowBytesIn = tracker.bytesIn;
|
|
103
|
+
windowBytesOut = tracker.bytesOut;
|
|
104
|
+
}
|
|
105
|
+
// Add to route totals
|
|
106
|
+
const current = routeData.get(tracker.routeName) || { bytesIn: 0, bytesOut: 0 };
|
|
107
|
+
current.bytesIn += windowBytesIn;
|
|
108
|
+
current.bytesOut += windowBytesOut;
|
|
109
|
+
routeData.set(tracker.routeName, current);
|
|
94
110
|
}
|
|
95
111
|
}
|
|
96
112
|
// Convert to rates (bytes per second)
|
|
97
113
|
for (const [route, data] of routeData) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
}
|
|
114
|
+
routeThroughput.set(route, {
|
|
115
|
+
in: Math.round(data.bytesIn / windowSeconds),
|
|
116
|
+
out: Math.round(data.bytesOut / windowSeconds)
|
|
117
|
+
});
|
|
104
118
|
}
|
|
105
119
|
return routeThroughput;
|
|
106
120
|
},
|
|
@@ -108,32 +122,46 @@ export class MetricsCollector {
|
|
|
108
122
|
const ipThroughput = new Map();
|
|
109
123
|
const now = Date.now();
|
|
110
124
|
const windowStart = now - (windowSeconds * 1000);
|
|
111
|
-
// Aggregate bytes by IP
|
|
125
|
+
// Aggregate bytes by IP - calculate actual bytes transferred in window
|
|
112
126
|
const ipData = new Map();
|
|
113
127
|
for (const [_, tracker] of this.connectionByteTrackers) {
|
|
114
128
|
// Only include connections that were active within the window
|
|
115
|
-
if (tracker.lastUpdate > windowStart
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
if (tracker.lastUpdate > windowStart) {
|
|
130
|
+
let windowBytesIn = 0;
|
|
131
|
+
let windowBytesOut = 0;
|
|
132
|
+
if (tracker.windowSnapshots && tracker.windowSnapshots.length > 0) {
|
|
133
|
+
// Find the earliest snapshot within or just before the window
|
|
134
|
+
let startSnapshot = { timestamp: tracker.startTime, bytesIn: 0, bytesOut: 0 };
|
|
135
|
+
for (const snapshot of tracker.windowSnapshots) {
|
|
136
|
+
if (snapshot.timestamp <= windowStart) {
|
|
137
|
+
startSnapshot = snapshot;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Calculate bytes transferred since window start
|
|
144
|
+
windowBytesIn = tracker.bytesIn - startSnapshot.bytesIn;
|
|
145
|
+
windowBytesOut = tracker.bytesOut - startSnapshot.bytesOut;
|
|
146
|
+
}
|
|
147
|
+
else if (tracker.startTime > windowStart) {
|
|
148
|
+
// Connection started within window, use all its bytes
|
|
149
|
+
windowBytesIn = tracker.bytesIn;
|
|
150
|
+
windowBytesOut = tracker.bytesOut;
|
|
126
151
|
}
|
|
152
|
+
// Add to IP totals
|
|
153
|
+
const current = ipData.get(tracker.remoteIP) || { bytesIn: 0, bytesOut: 0 };
|
|
154
|
+
current.bytesIn += windowBytesIn;
|
|
155
|
+
current.bytesOut += windowBytesOut;
|
|
156
|
+
ipData.set(tracker.remoteIP, current);
|
|
127
157
|
}
|
|
128
158
|
}
|
|
129
159
|
// Convert to rates (bytes per second)
|
|
130
160
|
for (const [ip, data] of ipData) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
}
|
|
161
|
+
ipThroughput.set(ip, {
|
|
162
|
+
in: Math.round(data.bytesIn / windowSeconds),
|
|
163
|
+
out: Math.round(data.bytesOut / windowSeconds)
|
|
164
|
+
});
|
|
137
165
|
}
|
|
138
166
|
return ipThroughput;
|
|
139
167
|
}
|
|
@@ -217,7 +245,8 @@ export class MetricsCollector {
|
|
|
217
245
|
bytesIn: 0,
|
|
218
246
|
bytesOut: 0,
|
|
219
247
|
startTime: now,
|
|
220
|
-
lastUpdate: now
|
|
248
|
+
lastUpdate: now,
|
|
249
|
+
windowSnapshots: [] // Initialize empty snapshots array
|
|
221
250
|
});
|
|
222
251
|
// Cleanup old request timestamps
|
|
223
252
|
if (this.requestTimestamps.length > 5000) {
|
|
@@ -242,6 +271,19 @@ export class MetricsCollector {
|
|
|
242
271
|
tracker.bytesIn += bytesIn;
|
|
243
272
|
tracker.bytesOut += bytesOut;
|
|
244
273
|
tracker.lastUpdate = Date.now();
|
|
274
|
+
// Initialize snapshots array if not present
|
|
275
|
+
if (!tracker.windowSnapshots) {
|
|
276
|
+
tracker.windowSnapshots = [];
|
|
277
|
+
}
|
|
278
|
+
// Add current snapshot - we'll use these for accurate windowed calculations
|
|
279
|
+
tracker.windowSnapshots.push({
|
|
280
|
+
timestamp: Date.now(),
|
|
281
|
+
bytesIn: tracker.bytesIn,
|
|
282
|
+
bytesOut: tracker.bytesOut
|
|
283
|
+
});
|
|
284
|
+
// Keep only snapshots from last 5 minutes to prevent memory growth
|
|
285
|
+
const fiveMinutesAgo = Date.now() - 300000;
|
|
286
|
+
tracker.windowSnapshots = tracker.windowSnapshots.filter(s => s.timestamp > fiveMinutesAgo);
|
|
245
287
|
}
|
|
246
288
|
}
|
|
247
289
|
/**
|
|
@@ -312,4 +354,4 @@ export class MetricsCollector {
|
|
|
312
354
|
this.stop();
|
|
313
355
|
}
|
|
314
356
|
}
|
|
315
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1jb2xsZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21ldHJpY3MtY29sbGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFRNUMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQW1CM0IsWUFDVSxVQUFzQixFQUM5QixNQUdDO1FBSk8sZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQWhCaEMsbUJBQW1CO1FBQ1gsc0JBQWlCLEdBQWEsRUFBRSxDQUFDO1FBQ2pDLGtCQUFhLEdBQVcsQ0FBQyxDQUFDO1FBRWxDLG9EQUFvRDtRQUM1QywyQkFBc0IsR0FBRyxJQUFJLEdBQUcsRUFBd0IsQ0FBQztRQXNCakUsb0NBQW9DO1FBQzdCLGdCQUFXLEdBQUc7WUFDbkIsTUFBTSxFQUFFLEdBQVcsRUFBRTtnQkFDbkIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDaEUsQ0FBQztZQUVELEtBQUssRUFBRSxHQUFXLEVBQUU7Z0JBQ2xCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztnQkFDdEUsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO2dCQUVuRSxLQUFLLE1BQU0sTUFBTSxJQUFJLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQztvQkFDcEMsS0FBSyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQ2xDLENBQUM7Z0JBRUQs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
|
|
357
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1jb2xsZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21ldHJpY3MtY29sbGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFRNUMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQW1CM0IsWUFDVSxVQUFzQixFQUM5QixNQUdDO1FBSk8sZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQWhCaEMsbUJBQW1CO1FBQ1gsc0JBQWlCLEdBQWEsRUFBRSxDQUFDO1FBQ2pDLGtCQUFhLEdBQVcsQ0FBQyxDQUFDO1FBRWxDLG9EQUFvRDtRQUM1QywyQkFBc0IsR0FBRyxJQUFJLEdBQUcsRUFBd0IsQ0FBQztRQXNCakUsb0NBQW9DO1FBQzdCLGdCQUFXLEdBQUc7WUFDbkIsTUFBTSxFQUFFLEdBQVcsRUFBRTtnQkFDbkIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDaEUsQ0FBQztZQUVELEtBQUssRUFBRSxHQUFXLEVBQUU7Z0JBQ2xCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztnQkFDdEUsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO2dCQUVuRSxLQUFLLE1BQU0sTUFBTSxJQUFJLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQztvQkFDcEMsS0FBSyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQ2xDLENBQUM7Z0JBRUQs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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartproxy",
|
|
3
|
-
"version": "19.6.
|
|
3
|
+
"version": "19.6.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/readme.hints.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## Byte Tracking and Metrics
|
|
4
4
|
|
|
5
|
+
### Throughput Drift Issue (Fixed)
|
|
6
|
+
|
|
7
|
+
**Problem**: Throughput numbers were gradually increasing over time for long-lived connections.
|
|
8
|
+
|
|
9
|
+
**Root Cause**: The `byRoute()` and `byIP()` methods were dividing cumulative total bytes (since connection start) by the window duration, causing rates to appear higher as connections aged:
|
|
10
|
+
- Hour 1: 1GB total / 60s = 17 MB/s ✓
|
|
11
|
+
- Hour 2: 2GB total / 60s = 34 MB/s ✗ (appears doubled!)
|
|
12
|
+
- Hour 3: 3GB total / 60s = 50 MB/s ✗ (keeps rising!)
|
|
13
|
+
|
|
14
|
+
**Solution**: Implemented snapshot-based byte tracking that calculates actual bytes transferred within each time window:
|
|
15
|
+
- Store periodic snapshots of byte counts with timestamps
|
|
16
|
+
- Calculate delta between window start and end snapshots
|
|
17
|
+
- Divide delta by window duration for accurate throughput
|
|
18
|
+
|
|
5
19
|
### What Gets Counted (Network Interface Throughput)
|
|
6
20
|
|
|
7
21
|
The byte tracking is designed to match network interface throughput (what Unifi/network monitoring tools show):
|
|
@@ -41,10 +55,13 @@ The byte tracking is designed to match network interface throughput (what Unifi/
|
|
|
41
55
|
|
|
42
56
|
The metrics system has three layers:
|
|
43
57
|
1. **Connection Records** (`record.bytesReceived/bytesSent`): Track total bytes per connection
|
|
44
|
-
2. **ThroughputTracker**: Accumulates bytes between samples for rate calculations (
|
|
45
|
-
3. **connectionByteTrackers**: Track bytes per connection with
|
|
58
|
+
2. **ThroughputTracker**: Accumulates bytes between samples for global rate calculations (resets each second)
|
|
59
|
+
3. **connectionByteTrackers**: Track bytes per connection with snapshots for accurate windowed per-route/IP metrics
|
|
46
60
|
|
|
47
|
-
|
|
61
|
+
Key features:
|
|
62
|
+
- Global throughput uses sampling with accumulator reset (accurate)
|
|
63
|
+
- Per-route/IP throughput uses snapshots to calculate window-specific deltas (accurate)
|
|
64
|
+
- All byte counting happens exactly once at the data flow point
|
|
48
65
|
|
|
49
66
|
### Understanding "High" Byte Counts
|
|
50
67
|
|
|
@@ -124,35 +124,49 @@ export class MetricsCollector implements IMetrics {
|
|
|
124
124
|
const now = Date.now();
|
|
125
125
|
const windowStart = now - (windowSeconds * 1000);
|
|
126
126
|
|
|
127
|
-
// Aggregate bytes by route
|
|
128
|
-
const routeData = new Map<string, { bytesIn: number; bytesOut: number
|
|
127
|
+
// Aggregate bytes by route - calculate actual bytes transferred in window
|
|
128
|
+
const routeData = new Map<string, { bytesIn: number; bytesOut: number }>();
|
|
129
129
|
|
|
130
130
|
for (const [_, tracker] of this.connectionByteTrackers) {
|
|
131
131
|
// Only include connections that were active within the window
|
|
132
|
-
if (tracker.lastUpdate > windowStart
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const connectionEnd = tracker.lastUpdate;
|
|
136
|
-
const durationInWindow = (connectionEnd - connectionStart) / 1000; // Convert to seconds
|
|
132
|
+
if (tracker.lastUpdate > windowStart) {
|
|
133
|
+
let windowBytesIn = 0;
|
|
134
|
+
let windowBytesOut = 0;
|
|
137
135
|
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
136
|
+
if (tracker.windowSnapshots && tracker.windowSnapshots.length > 0) {
|
|
137
|
+
// Find the earliest snapshot within or just before the window
|
|
138
|
+
let startSnapshot = { timestamp: tracker.startTime, bytesIn: 0, bytesOut: 0 };
|
|
139
|
+
for (const snapshot of tracker.windowSnapshots) {
|
|
140
|
+
if (snapshot.timestamp <= windowStart) {
|
|
141
|
+
startSnapshot = snapshot;
|
|
142
|
+
} else {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Calculate bytes transferred since window start
|
|
148
|
+
windowBytesIn = tracker.bytesIn - startSnapshot.bytesIn;
|
|
149
|
+
windowBytesOut = tracker.bytesOut - startSnapshot.bytesOut;
|
|
150
|
+
} else if (tracker.startTime > windowStart) {
|
|
151
|
+
// Connection started within window, use all its bytes
|
|
152
|
+
windowBytesIn = tracker.bytesIn;
|
|
153
|
+
windowBytesOut = tracker.bytesOut;
|
|
144
154
|
}
|
|
155
|
+
|
|
156
|
+
// Add to route totals
|
|
157
|
+
const current = routeData.get(tracker.routeName) || { bytesIn: 0, bytesOut: 0 };
|
|
158
|
+
current.bytesIn += windowBytesIn;
|
|
159
|
+
current.bytesOut += windowBytesOut;
|
|
160
|
+
routeData.set(tracker.routeName, current);
|
|
145
161
|
}
|
|
146
162
|
}
|
|
147
163
|
|
|
148
164
|
// Convert to rates (bytes per second)
|
|
149
165
|
for (const [route, data] of routeData) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
});
|
|
155
|
-
}
|
|
166
|
+
routeThroughput.set(route, {
|
|
167
|
+
in: Math.round(data.bytesIn / windowSeconds),
|
|
168
|
+
out: Math.round(data.bytesOut / windowSeconds)
|
|
169
|
+
});
|
|
156
170
|
}
|
|
157
171
|
|
|
158
172
|
return routeThroughput;
|
|
@@ -163,35 +177,49 @@ export class MetricsCollector implements IMetrics {
|
|
|
163
177
|
const now = Date.now();
|
|
164
178
|
const windowStart = now - (windowSeconds * 1000);
|
|
165
179
|
|
|
166
|
-
// Aggregate bytes by IP
|
|
167
|
-
const ipData = new Map<string, { bytesIn: number; bytesOut: number
|
|
180
|
+
// Aggregate bytes by IP - calculate actual bytes transferred in window
|
|
181
|
+
const ipData = new Map<string, { bytesIn: number; bytesOut: number }>();
|
|
168
182
|
|
|
169
183
|
for (const [_, tracker] of this.connectionByteTrackers) {
|
|
170
184
|
// Only include connections that were active within the window
|
|
171
|
-
if (tracker.lastUpdate > windowStart
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const connectionEnd = tracker.lastUpdate;
|
|
175
|
-
const durationInWindow = (connectionEnd - connectionStart) / 1000; // Convert to seconds
|
|
185
|
+
if (tracker.lastUpdate > windowStart) {
|
|
186
|
+
let windowBytesIn = 0;
|
|
187
|
+
let windowBytesOut = 0;
|
|
176
188
|
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
if (tracker.windowSnapshots && tracker.windowSnapshots.length > 0) {
|
|
190
|
+
// Find the earliest snapshot within or just before the window
|
|
191
|
+
let startSnapshot = { timestamp: tracker.startTime, bytesIn: 0, bytesOut: 0 };
|
|
192
|
+
for (const snapshot of tracker.windowSnapshots) {
|
|
193
|
+
if (snapshot.timestamp <= windowStart) {
|
|
194
|
+
startSnapshot = snapshot;
|
|
195
|
+
} else {
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Calculate bytes transferred since window start
|
|
201
|
+
windowBytesIn = tracker.bytesIn - startSnapshot.bytesIn;
|
|
202
|
+
windowBytesOut = tracker.bytesOut - startSnapshot.bytesOut;
|
|
203
|
+
} else if (tracker.startTime > windowStart) {
|
|
204
|
+
// Connection started within window, use all its bytes
|
|
205
|
+
windowBytesIn = tracker.bytesIn;
|
|
206
|
+
windowBytesOut = tracker.bytesOut;
|
|
183
207
|
}
|
|
208
|
+
|
|
209
|
+
// Add to IP totals
|
|
210
|
+
const current = ipData.get(tracker.remoteIP) || { bytesIn: 0, bytesOut: 0 };
|
|
211
|
+
current.bytesIn += windowBytesIn;
|
|
212
|
+
current.bytesOut += windowBytesOut;
|
|
213
|
+
ipData.set(tracker.remoteIP, current);
|
|
184
214
|
}
|
|
185
215
|
}
|
|
186
216
|
|
|
187
217
|
// Convert to rates (bytes per second)
|
|
188
218
|
for (const [ip, data] of ipData) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
194
|
-
}
|
|
219
|
+
ipThroughput.set(ip, {
|
|
220
|
+
in: Math.round(data.bytesIn / windowSeconds),
|
|
221
|
+
out: Math.round(data.bytesOut / windowSeconds)
|
|
222
|
+
});
|
|
195
223
|
}
|
|
196
224
|
|
|
197
225
|
return ipThroughput;
|
|
@@ -294,7 +322,8 @@ export class MetricsCollector implements IMetrics {
|
|
|
294
322
|
bytesIn: 0,
|
|
295
323
|
bytesOut: 0,
|
|
296
324
|
startTime: now,
|
|
297
|
-
lastUpdate: now
|
|
325
|
+
lastUpdate: now,
|
|
326
|
+
windowSnapshots: [] // Initialize empty snapshots array
|
|
298
327
|
});
|
|
299
328
|
|
|
300
329
|
// Cleanup old request timestamps
|
|
@@ -323,6 +352,22 @@ export class MetricsCollector implements IMetrics {
|
|
|
323
352
|
tracker.bytesIn += bytesIn;
|
|
324
353
|
tracker.bytesOut += bytesOut;
|
|
325
354
|
tracker.lastUpdate = Date.now();
|
|
355
|
+
|
|
356
|
+
// Initialize snapshots array if not present
|
|
357
|
+
if (!tracker.windowSnapshots) {
|
|
358
|
+
tracker.windowSnapshots = [];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Add current snapshot - we'll use these for accurate windowed calculations
|
|
362
|
+
tracker.windowSnapshots.push({
|
|
363
|
+
timestamp: Date.now(),
|
|
364
|
+
bytesIn: tracker.bytesIn,
|
|
365
|
+
bytesOut: tracker.bytesOut
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Keep only snapshots from last 5 minutes to prevent memory growth
|
|
369
|
+
const fiveMinutesAgo = Date.now() - 300000;
|
|
370
|
+
tracker.windowSnapshots = tracker.windowSnapshots.filter(s => s.timestamp > fiveMinutesAgo);
|
|
326
371
|
}
|
|
327
372
|
}
|
|
328
373
|
|
|
@@ -109,4 +109,10 @@ export interface IByteTracker {
|
|
|
109
109
|
bytesOut: number;
|
|
110
110
|
startTime: number;
|
|
111
111
|
lastUpdate: number;
|
|
112
|
+
// Track bytes at window boundaries for rate calculation
|
|
113
|
+
windowSnapshots?: Array<{
|
|
114
|
+
timestamp: number;
|
|
115
|
+
bytesIn: number;
|
|
116
|
+
bytesOut: number;
|
|
117
|
+
}>;
|
|
112
118
|
}
|