@torrent-tv/proxy 2.64.2 → 2.64.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.64.2",
3
+ "version": "2.64.3",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
@@ -21,6 +21,8 @@
21
21
 
22
22
  import { readFile, statfs } from "node:fs/promises";
23
23
  import os from "node:os";
24
+ import v8 from "node:v8";
25
+ import path from "node:path";
24
26
 
25
27
  /** How often the reading is taken and written. */
26
28
  export const MEMORY_REPORT_INTERVAL_MS = 60_000;
@@ -234,6 +236,7 @@ export function startMemoryReport({
234
236
  diskPath = "",
235
237
  intervalMs = MEMORY_REPORT_INTERVAL_MS
236
238
  }) {
239
+ let highWaterRss = 0;
237
240
  const tick = async () => {
238
241
  try {
239
242
  let stores = [];
@@ -243,21 +246,37 @@ export function startMemoryReport({
243
246
  // silent-ok: a store list that cannot be read must not stop the reading
244
247
  // that matters, which is the process's own.
245
248
  }
249
+ const processMemory = readProcessMemory();
246
250
  if (scope === "thread") {
247
- log(describeMemory({ scope, label, process: readProcessMemory(), stores }));
251
+ log(describeMemory({ scope, label, process: processMemory, stores }));
248
252
  return;
249
253
  }
250
254
  const { bytes, measured } = await availableMemory();
255
+ const anonymousBytes = await readAnonymousMemory();
256
+ const diskFreeBytes = diskPath ? await readDiskFree(diskPath) : null;
251
257
  log(describeMemory({
252
258
  scope,
253
259
  label,
254
- process: readProcessMemory(),
260
+ process: processMemory,
255
261
  availableBytes: bytes,
256
262
  availableMeasured: measured,
257
- anonymousBytes: await readAnonymousMemory(),
258
- diskFreeBytes: diskPath ? await readDiskFree(diskPath) : null,
263
+ anonymousBytes,
264
+ diskFreeBytes,
259
265
  stores
260
266
  }));
267
+ // High-water and heap snapshot on growth — gives a file to open in Chrome DevTools
268
+ // when the kernel is about to kill the process. One snapshot per high-water.
269
+ if (processMemory.rss > highWaterRss + 100 * 1024 * 1024 && processMemory.rss > 500 * 1024 * 1024) {
270
+ highWaterRss = processMemory.rss;
271
+ try {
272
+ const snapPath = path.join(os.tmpdir(), `heap-${Date.now()}-${processMemory.rss}.heapsnapshot`);
273
+ v8.writeHeapSnapshot(snapPath);
274
+ log(`memory: wrote heap snapshot to ${snapPath} rss=${Math.round(processMemory.rss / (1024 * 1024))}MB anon=${anonymousBytes ? Math.round(anonymousBytes / (1024 * 1024)) : "?"}MB`);
275
+ } catch {}
276
+ }
277
+ if (anonymousBytes !== null && processMemory.rss > 800 * 1024 * 1024) {
278
+ log(`memory: high rss=${Math.round(processMemory.rss / (1024 * 1024))}MB anon=${Math.round(anonymousBytes / (1024 * 1024))}MB heap=${Math.round(processMemory.heapUsed / (1024 * 1024))}MB — watch for OOM`);
279
+ }
261
280
  } catch {
262
281
  // silent-ok: a reading that fails is not worth ending the series over.
263
282
  }