@rljson/fs-agent 0.0.42 → 0.0.43
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/fs-agent.js +52 -3
- package/dist/fs-scanner.d.ts +26 -0
- package/package.json +1 -1
package/dist/fs-agent.js
CHANGED
|
@@ -443,6 +443,10 @@ class FsScanner {
|
|
|
443
443
|
_cacheLoaded = false;
|
|
444
444
|
/** Entries that vanished mid-scan, counted per {@link scan} for one summary. */
|
|
445
445
|
_vanishedDuringScan = 0;
|
|
446
|
+
/** The scan pass running right now, if any. See {@link _scanAfterNow}. */
|
|
447
|
+
_activeScan = null;
|
|
448
|
+
/** The single follow-up pass everyone who arrived mid-scan is waiting on. */
|
|
449
|
+
_pendingScan = null;
|
|
446
450
|
constructor(rootPath, options = {}) {
|
|
447
451
|
this._rootPath = rootPath;
|
|
448
452
|
this._options = {
|
|
@@ -749,7 +753,7 @@ class FsScanner {
|
|
|
749
753
|
}
|
|
750
754
|
const prevKey = this._tree ? this._safetyContentKey(this._tree) : null;
|
|
751
755
|
try {
|
|
752
|
-
await this.
|
|
756
|
+
await this._scanAfterNow();
|
|
753
757
|
} catch (err) {
|
|
754
758
|
console.warn(
|
|
755
759
|
`[fs-scanner] safety rescan failed: ${FsScanner._errMessage(err)}`
|
|
@@ -840,7 +844,7 @@ class FsScanner {
|
|
|
840
844
|
try {
|
|
841
845
|
if (exists) {
|
|
842
846
|
const existingTree = this._findTreeByPath(relativePath);
|
|
843
|
-
await this.
|
|
847
|
+
await this._scanAfterNow();
|
|
844
848
|
await this._notifyChange({
|
|
845
849
|
type: existingTree ? "modified" : "added",
|
|
846
850
|
path: relativePath
|
|
@@ -855,12 +859,57 @@ class FsScanner {
|
|
|
855
859
|
this.stopWatch();
|
|
856
860
|
}
|
|
857
861
|
if (rootExists) {
|
|
858
|
-
await this.
|
|
862
|
+
await this._scanAfterNow();
|
|
859
863
|
await this._notifyChange({ type: "deleted", path: relativePath });
|
|
860
864
|
}
|
|
861
865
|
} catch {
|
|
862
866
|
}
|
|
863
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* A scan that is guaranteed to have STARTED after this call, sharing one
|
|
870
|
+
* pass with everyone else who asked while it was running.
|
|
871
|
+
*
|
|
872
|
+
* `scan()` is a whole-tree operation: it walks every directory, stats every
|
|
873
|
+
* file and writes a blob for anything new. Calling it once per watcher event
|
|
874
|
+
* therefore costs O(files²) on a burst — and fs.watch does not await its
|
|
875
|
+
* callback, so those scans all run AT ONCE. Copying 1 200 tiny files into a
|
|
876
|
+
* watched folder took 1 200 concurrent full scans: the CPU sat 82% idle
|
|
877
|
+
* waiting on the filesystem, RSS climbed from 263 MB to 785 MB, and after two
|
|
878
|
+
* minutes the peer had received nothing. On the customer's 3 702-file folder
|
|
879
|
+
* the same burst wedged the client outright.
|
|
880
|
+
*
|
|
881
|
+
* Started-after is the part that cannot be traded away. Joining a scan that
|
|
882
|
+
* began BEFORE the event would let a push carry a tree that predates the file
|
|
883
|
+
* that triggered it — which is precisely the partial trees the lab saw
|
|
884
|
+
* advertised: 64 nodes, then 103, then 204, each a stale snapshot of a folder
|
|
885
|
+
* that already held twelve hundred files. So a caller either starts a scan
|
|
886
|
+
* now, or waits for the one that begins when the current pass ends. A burst
|
|
887
|
+
* of any size collapses to at most two scans, and no change is missed.
|
|
888
|
+
*/
|
|
889
|
+
_scanAfterNow() {
|
|
890
|
+
if (!this._activeScan) {
|
|
891
|
+
const started = (async () => {
|
|
892
|
+
try {
|
|
893
|
+
return await this.scan();
|
|
894
|
+
} finally {
|
|
895
|
+
this._activeScan = null;
|
|
896
|
+
}
|
|
897
|
+
})();
|
|
898
|
+
this._activeScan = started;
|
|
899
|
+
return started;
|
|
900
|
+
}
|
|
901
|
+
if (this._pendingScan) return this._pendingScan;
|
|
902
|
+
const queued = (async () => {
|
|
903
|
+
try {
|
|
904
|
+
await this._activeScan;
|
|
905
|
+
} catch {
|
|
906
|
+
}
|
|
907
|
+
this._pendingScan = null;
|
|
908
|
+
return this._scanAfterNow();
|
|
909
|
+
})();
|
|
910
|
+
this._pendingScan = queued;
|
|
911
|
+
return queued;
|
|
912
|
+
}
|
|
864
913
|
_findTreeByPath(relativePath) {
|
|
865
914
|
if (!this._tree) return void 0;
|
|
866
915
|
for (const tree of this._tree.trees.values()) {
|
package/dist/fs-scanner.d.ts
CHANGED
|
@@ -119,6 +119,10 @@ export declare class FsScanner {
|
|
|
119
119
|
private _cacheLoaded;
|
|
120
120
|
/** Entries that vanished mid-scan, counted per {@link scan} for one summary. */
|
|
121
121
|
private _vanishedDuringScan;
|
|
122
|
+
/** The scan pass running right now, if any. See {@link _scanAfterNow}. */
|
|
123
|
+
private _activeScan;
|
|
124
|
+
/** The single follow-up pass everyone who arrived mid-scan is waiting on. */
|
|
125
|
+
private _pendingScan;
|
|
122
126
|
constructor(rootPath: string, options?: FsScanOptions);
|
|
123
127
|
get tree(): FsTree | null;
|
|
124
128
|
/** Whether a watcher is currently installed. */
|
|
@@ -174,6 +178,28 @@ export declare class FsScanner {
|
|
|
174
178
|
/** Whether the host is Windows — gates Windows-specific watcher hardening. */
|
|
175
179
|
private static get _isWindows();
|
|
176
180
|
private _handleFileChange;
|
|
181
|
+
/**
|
|
182
|
+
* A scan that is guaranteed to have STARTED after this call, sharing one
|
|
183
|
+
* pass with everyone else who asked while it was running.
|
|
184
|
+
*
|
|
185
|
+
* `scan()` is a whole-tree operation: it walks every directory, stats every
|
|
186
|
+
* file and writes a blob for anything new. Calling it once per watcher event
|
|
187
|
+
* therefore costs O(files²) on a burst — and fs.watch does not await its
|
|
188
|
+
* callback, so those scans all run AT ONCE. Copying 1 200 tiny files into a
|
|
189
|
+
* watched folder took 1 200 concurrent full scans: the CPU sat 82% idle
|
|
190
|
+
* waiting on the filesystem, RSS climbed from 263 MB to 785 MB, and after two
|
|
191
|
+
* minutes the peer had received nothing. On the customer's 3 702-file folder
|
|
192
|
+
* the same burst wedged the client outright.
|
|
193
|
+
*
|
|
194
|
+
* Started-after is the part that cannot be traded away. Joining a scan that
|
|
195
|
+
* began BEFORE the event would let a push carry a tree that predates the file
|
|
196
|
+
* that triggered it — which is precisely the partial trees the lab saw
|
|
197
|
+
* advertised: 64 nodes, then 103, then 204, each a stale snapshot of a folder
|
|
198
|
+
* that already held twelve hundred files. So a caller either starts a scan
|
|
199
|
+
* now, or waits for the one that begins when the current pass ends. A burst
|
|
200
|
+
* of any size collapses to at most two scans, and no change is missed.
|
|
201
|
+
*/
|
|
202
|
+
private _scanAfterNow;
|
|
177
203
|
private _findTreeByPath;
|
|
178
204
|
/**
|
|
179
205
|
* Whether this notification should escape the pause.
|