@autonomys/file-server 1.5.18 → 1.5.19
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/caching/fileCache.d.ts.map +1 -1
- package/dist/caching/fileCache.js +11 -4
- package/dist/caching/index.d.ts +1 -0
- package/dist/caching/index.d.ts.map +1 -1
- package/dist/caching/index.js +1 -0
- package/dist/caching/streamUtils.d.ts +20 -0
- package/dist/caching/streamUtils.d.ts.map +1 -0
- package/dist/caching/streamUtils.js +107 -0
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileCache.d.ts","sourceRoot":"","sources":["../../src/caching/fileCache.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"fileCache.d.ts","sourceRoot":"","sources":["../../src/caching/fileCache.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAU9E,eAAO,MAAM,eAAe,GAAI,QAAQ,eAAe;eA8B7B,MAAM,YAAY,gBAAgB,KAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;eAiCjE,MAAM,gBAAgB,YAAY;eARlC,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;kBAoBtB,MAAM;CA0BlC,CAAA"}
|
|
@@ -22,6 +22,7 @@ import { createCache } from 'cache-manager';
|
|
|
22
22
|
import fs from 'fs';
|
|
23
23
|
import fsPromises from 'fs/promises';
|
|
24
24
|
import path from 'path';
|
|
25
|
+
import { createErrorResilientStream } from './streamUtils.js';
|
|
25
26
|
import { writeFile } from './utils.js';
|
|
26
27
|
const CHARS_PER_PARTITION = 2;
|
|
27
28
|
export const createFileCache = (config) => {
|
|
@@ -54,10 +55,16 @@ export const createFileCache = (config) => {
|
|
|
54
55
|
return null;
|
|
55
56
|
}
|
|
56
57
|
const path = cidToFilePath(cid);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
const sourceStream = fs.createReadStream(path, {
|
|
59
|
+
start: (_a = options === null || options === void 0 ? void 0 : options.byteRange) === null || _a === void 0 ? void 0 : _a[0],
|
|
60
|
+
end: (_b = options === null || options === void 0 ? void 0 : options.byteRange) === null || _b === void 0 ? void 0 : _b[1],
|
|
61
|
+
});
|
|
62
|
+
// Wrap with error-resilient stream for stalled stream detection
|
|
63
|
+
const resilientStream = createErrorResilientStream(sourceStream, {
|
|
64
|
+
stallTimeout: 30000, // 30 seconds
|
|
65
|
+
healthCheckInterval: 5000, // 5 seconds
|
|
66
|
+
});
|
|
67
|
+
return Object.assign(Object.assign({}, data), { data: resilientStream });
|
|
61
68
|
});
|
|
62
69
|
const has = (cid) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
70
|
const path = cidToFilePath(cid);
|
package/dist/caching/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/caching/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/caching/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA"}
|
package/dist/caching/index.js
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Readable } from 'stream';
|
|
2
|
+
export interface StreamHealthMetrics {
|
|
3
|
+
isStalled: boolean;
|
|
4
|
+
lastActivity: number;
|
|
5
|
+
isDestroyed: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ErrorResilientStreamOptions {
|
|
8
|
+
stallTimeout?: number;
|
|
9
|
+
healthCheckInterval?: number;
|
|
10
|
+
}
|
|
11
|
+
interface ErrorResilientStream extends Readable {
|
|
12
|
+
healthCheck?: () => StreamHealthMetrics;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates an error-resilient stream wrapper that prevents consumer errors from stalling the source stream
|
|
16
|
+
* Focuses on detecting and handling stalled streams
|
|
17
|
+
*/
|
|
18
|
+
export declare const createErrorResilientStream: (sourceStream: Readable, options?: ErrorResilientStreamOptions) => ErrorResilientStream;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=streamUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamUtils.d.ts","sourceRoot":"","sources":["../../src/caching/streamUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAE9C,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,UAAU,oBAAqB,SAAQ,QAAQ;IAC7C,WAAW,CAAC,EAAE,MAAM,mBAAmB,CAAA;CACxC;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GACrC,cAAc,QAAQ,EACtB,UAAS,2BAAgC,KACxC,oBAyHF,CAAA"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { PassThrough } from 'stream';
|
|
2
|
+
/**
|
|
3
|
+
* Creates an error-resilient stream wrapper that prevents consumer errors from stalling the source stream
|
|
4
|
+
* Focuses on detecting and handling stalled streams
|
|
5
|
+
*/
|
|
6
|
+
export const createErrorResilientStream = (sourceStream, options = {}) => {
|
|
7
|
+
const { stallTimeout = 30000, // 30 seconds
|
|
8
|
+
healthCheckInterval = 5000, // 5 seconds
|
|
9
|
+
} = options;
|
|
10
|
+
const passThrough = new PassThrough({
|
|
11
|
+
highWaterMark: 64 * 1024, // 64KB buffer to handle backpressure
|
|
12
|
+
objectMode: false,
|
|
13
|
+
});
|
|
14
|
+
// Simple metrics for stalled stream detection
|
|
15
|
+
const metrics = {
|
|
16
|
+
isStalled: false,
|
|
17
|
+
lastActivity: Date.now(),
|
|
18
|
+
isDestroyed: false,
|
|
19
|
+
};
|
|
20
|
+
let healthCheckTimer = null;
|
|
21
|
+
// Health check function - only checks for stalled streams
|
|
22
|
+
const healthCheck = () => {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const timeSinceLastActivity = now - metrics.lastActivity;
|
|
25
|
+
metrics.isStalled = timeSinceLastActivity > stallTimeout && !metrics.isDestroyed;
|
|
26
|
+
return Object.assign({}, metrics);
|
|
27
|
+
};
|
|
28
|
+
// Start periodic health checks for stalled streams
|
|
29
|
+
const startHealthChecks = () => {
|
|
30
|
+
if (healthCheckTimer) {
|
|
31
|
+
clearInterval(healthCheckTimer);
|
|
32
|
+
}
|
|
33
|
+
healthCheckTimer = setInterval(() => {
|
|
34
|
+
const health = healthCheck();
|
|
35
|
+
if (health.isStalled) {
|
|
36
|
+
console.warn('Stream detected as stalled:', {
|
|
37
|
+
timeSinceLastActivity: Date.now() - health.lastActivity,
|
|
38
|
+
stallTimeout,
|
|
39
|
+
});
|
|
40
|
+
// Destroy the stalled stream
|
|
41
|
+
passThrough.destroy(new Error('Stream stalled - no activity detected'));
|
|
42
|
+
}
|
|
43
|
+
}, healthCheckInterval);
|
|
44
|
+
};
|
|
45
|
+
// Set up backpressure-aware data flow
|
|
46
|
+
sourceStream.on('data', (chunk) => {
|
|
47
|
+
try {
|
|
48
|
+
metrics.lastActivity = Date.now();
|
|
49
|
+
metrics.isStalled = false;
|
|
50
|
+
if (!passThrough.destroyed) {
|
|
51
|
+
const canContinue = passThrough.write(chunk);
|
|
52
|
+
if (!canContinue) {
|
|
53
|
+
// Backpressure: pause source stream until drain event
|
|
54
|
+
sourceStream.pause();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error('Error processing stream data:', error);
|
|
60
|
+
// Don't destroy the stream on consumer errors, just log
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
// Resume source stream when pass-through is ready for more data
|
|
64
|
+
passThrough.on('drain', () => {
|
|
65
|
+
if (!sourceStream.destroyed && !passThrough.destroyed) {
|
|
66
|
+
sourceStream.resume();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// Handle source stream end
|
|
70
|
+
sourceStream.on('end', () => {
|
|
71
|
+
if (!passThrough.destroyed) {
|
|
72
|
+
passThrough.end();
|
|
73
|
+
}
|
|
74
|
+
metrics.lastActivity = Date.now();
|
|
75
|
+
});
|
|
76
|
+
// Handle source stream errors
|
|
77
|
+
sourceStream.on('error', (error) => {
|
|
78
|
+
console.error('Source stream error:', error);
|
|
79
|
+
metrics.lastActivity = Date.now();
|
|
80
|
+
passThrough.destroy(error);
|
|
81
|
+
});
|
|
82
|
+
// Handle pass-through stream errors (consumer errors)
|
|
83
|
+
passThrough.on('error', (error) => {
|
|
84
|
+
console.error('Consumer stream error:', error);
|
|
85
|
+
metrics.lastActivity = Date.now();
|
|
86
|
+
// Don't propagate consumer errors to source stream
|
|
87
|
+
});
|
|
88
|
+
// Handle pass-through stream on end
|
|
89
|
+
passThrough.on('end', () => {
|
|
90
|
+
metrics.lastActivity = Date.now();
|
|
91
|
+
if (healthCheckTimer)
|
|
92
|
+
clearInterval(healthCheckTimer);
|
|
93
|
+
});
|
|
94
|
+
// Handle pass-through stream close
|
|
95
|
+
passThrough.on('close', () => {
|
|
96
|
+
metrics.isDestroyed = true;
|
|
97
|
+
metrics.lastActivity = Date.now();
|
|
98
|
+
if (healthCheckTimer) {
|
|
99
|
+
clearInterval(healthCheckTimer);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
// Start health monitoring
|
|
103
|
+
startHealthChecks();
|
|
104
|
+
const errorResilientStream = passThrough;
|
|
105
|
+
errorResilientStream.healthCheck = healthCheck;
|
|
106
|
+
return errorResilientStream;
|
|
107
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autonomys/file-server",
|
|
3
3
|
"packageManager": "yarn@4.7.0",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.19",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"typescript": "^5.8.3"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@autonomys/asynchronous": "^1.5.
|
|
51
|
-
"@autonomys/auto-dag-data": "^1.5.
|
|
52
|
-
"@autonomys/auto-utils": "^1.5.
|
|
50
|
+
"@autonomys/asynchronous": "^1.5.19",
|
|
51
|
+
"@autonomys/auto-dag-data": "^1.5.19",
|
|
52
|
+
"@autonomys/auto-utils": "^1.5.19",
|
|
53
53
|
"@keyvhq/sqlite": "^2.1.7",
|
|
54
54
|
"cache-manager": "^6.4.2",
|
|
55
55
|
"express": "^4.19.2",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"uuid": "^11.1.0",
|
|
62
62
|
"zod": "^3.24.2"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "ada024eb3cb14bf67b3d9f1cb8545e7b7d9d14ed"
|
|
65
65
|
}
|