@push.rocks/smartproxy 19.6.12 → 19.6.13

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.
@@ -51,18 +51,16 @@ export class ThroughputTracker {
51
51
  if (relevantSamples.length === 0) {
52
52
  return { in: 0, out: 0 };
53
53
  }
54
- // Sum bytes in the window
54
+ // Calculate total bytes in window
55
55
  const totalBytesIn = relevantSamples.reduce((sum, s) => sum + s.bytesIn, 0);
56
56
  const totalBytesOut = relevantSamples.reduce((sum, s) => sum + s.bytesOut, 0);
57
- // Calculate actual window duration (might be less than requested if not enough data)
58
- const actualWindowSeconds = Math.min(windowSeconds, (now - relevantSamples[0].timestamp) / 1000);
59
- // Avoid division by zero
60
- if (actualWindowSeconds === 0) {
61
- return { in: 0, out: 0 };
62
- }
57
+ // Use actual number of seconds covered by samples for accurate rate
58
+ const oldestSampleTime = relevantSamples[0].timestamp;
59
+ const newestSampleTime = relevantSamples[relevantSamples.length - 1].timestamp;
60
+ const actualSeconds = Math.max(1, (newestSampleTime - oldestSampleTime) / 1000 + 1);
63
61
  return {
64
- in: Math.round(totalBytesIn / actualWindowSeconds),
65
- out: Math.round(totalBytesOut / actualWindowSeconds)
62
+ in: Math.round(totalBytesIn / actualSeconds),
63
+ out: Math.round(totalBytesOut / actualSeconds)
66
64
  };
67
65
  }
68
66
  /**
@@ -114,4 +112,4 @@ export class ThroughputTracker {
114
112
  return this.samples.length;
115
113
  }
116
114
  }
117
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhyb3VnaHB1dC10cmFja2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJveGllcy9zbWFydC1wcm94eS90aHJvdWdocHV0LXRyYWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUE7O0dBRUc7QUFDSCxNQUFNLE9BQU8saUJBQWlCO0lBTzVCLFlBQVksbUJBQTJCLElBQUk7UUFObkMsWUFBTyxHQUF3QixFQUFFLENBQUM7UUFFbEMsdUJBQWtCLEdBQVcsQ0FBQyxDQUFDO1FBQy9CLHdCQUFtQixHQUFXLENBQUMsQ0FBQztRQUNoQyxtQkFBYyxHQUFXLENBQUMsQ0FBQztRQUdqQywrREFBK0Q7UUFDL0QsSUFBSSxDQUFDLFVBQVUsR0FBRyxnQkFBZ0IsQ0FBQztJQUNyQyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxXQUFXLENBQUMsT0FBZSxFQUFFLFFBQWdCO1FBQ2xELElBQUksQ0FBQyxrQkFBa0IsSUFBSSxPQUFPLENBQUM7UUFDbkMsSUFBSSxDQUFDLG1CQUFtQixJQUFJLFFBQVEsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxVQUFVO1FBQ2YsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRXZCLDZDQUE2QztRQUM3QyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztZQUNoQixTQUFTLEVBQUUsR0FBRztZQUNkLE9BQU8sRUFBRSxJQUFJLENBQUMsa0JBQWtCO1lBQ2hDLFFBQVEsRUFBRSxJQUFJLENBQUMsbUJBQW1CO1NBQ25DLENBQUMsQ0FBQztRQUVILHFCQUFxQjtRQUNyQixJQUFJLENBQUMsa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO1FBQzVCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxDQUFDLENBQUM7UUFDN0IsSUFBSSxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUM7UUFFMUIsbURBQW1EO1FBQ25ELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdB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
115
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhyb3VnaHB1dC10cmFja2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJveGllcy9zbWFydC1wcm94eS90aHJvdWdocHV0LXRyYWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUE7O0dBRUc7QUFDSCxNQUFNLE9BQU8saUJBQWlCO0lBTzVCLFlBQVksbUJBQTJCLElBQUk7UUFObkMsWUFBTyxHQUF3QixFQUFFLENBQUM7UUFFbEMsdUJBQWtCLEdBQVcsQ0FBQyxDQUFDO1FBQy9CLHdCQUFtQixHQUFXLENBQUMsQ0FBQztRQUNoQyxtQkFBYyxHQUFXLENBQUMsQ0FBQztRQUdqQywrREFBK0Q7UUFDL0QsSUFBSSxDQUFDLFVBQVUsR0FBRyxnQkFBZ0IsQ0FBQztJQUNyQyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxXQUFXLENBQUMsT0FBZSxFQUFFLFFBQWdCO1FBQ2xELElBQUksQ0FBQyxrQkFBa0IsSUFBSSxPQUFPLENBQUM7UUFDbkMsSUFBSSxDQUFDLG1CQUFtQixJQUFJLFFBQVEsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxVQUFVO1FBQ2YsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRXZCLDZDQUE2QztRQUM3QyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztZQUNoQixTQUFTLEVBQUUsR0FBRztZQUNkLE9BQU8sRUFBRSxJQUFJLENBQUMsa0JBQWtCO1lBQ2hDLFFBQVEsRUFBRSxJQUFJLENBQUMsbUJBQW1CO1NBQ25DLENBQUMsQ0FBQztRQUVILHFCQUFxQjtRQUNyQixJQUFJLENBQUMsa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO1FBQzVCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxDQUFDLENBQUM7UUFDN0IsSUFBSSxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUM7UUFFMUIsbURBQW1EO1FBQ25ELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdB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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartproxy",
3
- "version": "19.6.12",
3
+ "version": "19.6.13",
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
@@ -142,4 +142,45 @@ Keep-alive connections receive special treatment based on `keepAliveTreatment` s
142
142
  The system supports both receiving and sending PROXY protocol:
143
143
  - **Receiving**: Automatically detected from trusted proxy IPs (configured in `proxyIPs`)
144
144
  - **Sending**: Enabled per-route or globally via `sendProxyProtocol` setting
145
- - Real client IP is preserved and used for all connection tracking and security checks
145
+ - Real client IP is preserved and used for all connection tracking and security checks
146
+
147
+ ## Metrics and Throughput Calculation
148
+
149
+ The metrics system tracks throughput using per-second sampling:
150
+
151
+ 1. **Byte Recording**: Bytes are recorded as data flows through connections
152
+ 2. **Sampling**: Every second, accumulated bytes are stored as a sample
153
+ 3. **Rate Calculation**: Throughput is calculated by summing bytes over a time window
154
+ 4. **Per-Route/IP Tracking**: Separate ThroughputTracker instances for each route and IP
155
+
156
+ Key implementation details:
157
+ - Bytes are recorded in the bidirectional forwarding callbacks
158
+ - The instant() method returns throughput over the last 1 second
159
+ - The recent() method returns throughput over the last 10 seconds
160
+ - Custom windows can be specified for different averaging periods
161
+
162
+ ### Throughput Spikes Issue
163
+
164
+ There's a fundamental difference between application-layer and network-layer throughput:
165
+
166
+ **Application Layer (what we measure)**:
167
+ - Bytes are recorded when delivered to/from the application
168
+ - Large chunks can arrive "instantly" due to kernel/Node.js buffering
169
+ - Shows spikes when buffers are flushed (e.g., 20MB in 1 second = 160 Mbit/s)
170
+
171
+ **Network Layer (what Unifi shows)**:
172
+ - Actual packet flow through the network interface
173
+ - Limited by physical network speed (e.g., 20 Mbit/s)
174
+ - Data transfers over time, not in bursts
175
+
176
+ The spikes occur because:
177
+ 1. Data flows over network at 20 Mbit/s (takes 8 seconds for 20MB)
178
+ 2. Kernel/Node.js buffers this incoming data
179
+ 3. When buffer is flushed, application receives large chunk at once
180
+ 4. We record entire chunk in current second, creating artificial spike
181
+
182
+ **Potential Solutions**:
183
+ 1. Use longer window for "instant" measurements (e.g., 5 seconds instead of 1)
184
+ 2. Track socket write backpressure to estimate actual network flow
185
+ 3. Implement bandwidth estimation based on connection duration
186
+ 4. Accept that application-layer != network-layer throughput
@@ -65,24 +65,18 @@ export class ThroughputTracker {
65
65
  return { in: 0, out: 0 };
66
66
  }
67
67
 
68
- // Sum bytes in the window
68
+ // Calculate total bytes in window
69
69
  const totalBytesIn = relevantSamples.reduce((sum, s) => sum + s.bytesIn, 0);
70
70
  const totalBytesOut = relevantSamples.reduce((sum, s) => sum + s.bytesOut, 0);
71
71
 
72
- // Calculate actual window duration (might be less than requested if not enough data)
73
- const actualWindowSeconds = Math.min(
74
- windowSeconds,
75
- (now - relevantSamples[0].timestamp) / 1000
76
- );
77
-
78
- // Avoid division by zero
79
- if (actualWindowSeconds === 0) {
80
- return { in: 0, out: 0 };
81
- }
72
+ // Use actual number of seconds covered by samples for accurate rate
73
+ const oldestSampleTime = relevantSamples[0].timestamp;
74
+ const newestSampleTime = relevantSamples[relevantSamples.length - 1].timestamp;
75
+ const actualSeconds = Math.max(1, (newestSampleTime - oldestSampleTime) / 1000 + 1);
82
76
 
83
77
  return {
84
- in: Math.round(totalBytesIn / actualWindowSeconds),
85
- out: Math.round(totalBytesOut / actualWindowSeconds)
78
+ in: Math.round(totalBytesIn / actualSeconds),
79
+ out: Math.round(totalBytesOut / actualSeconds)
86
80
  };
87
81
  }
88
82