Watcher Perf: TypeScript 5.5 asks to watch thousands of non-existing paths (fix #214765) (#214842)

This commit is contained in:
Benjamin Pasero 2024-06-11 08:53:04 +02:00 committed by GitHub
parent 3ab5eedb14
commit a5d165b6cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 21 additions and 7 deletions

View file

@ -9,7 +9,7 @@ import { ILogMessage, IRecursiveWatcherWithSubscribe, IUniversalWatchRequest, IW
import { Emitter, Event } from 'vs/base/common/event';
import { FileChangeType, IFileChange } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { DeferredPromise } from 'vs/base/common/async';
import { DeferredPromise, ThrottledDelayer } from 'vs/base/common/async';
export abstract class BaseWatcher extends Disposable implements IWatcher {
@ -28,6 +28,8 @@ export abstract class BaseWatcher extends Disposable implements IWatcher {
private readonly suspendedWatchRequests = this._register(new DisposableMap<number /* correlation ID */>());
private readonly suspendedWatchRequestsWithPolling = new Set<number /* correlation ID */>();
private readonly updateWatchersDelayer = this._register(new ThrottledDelayer<void>(this.getUpdateWatchersDelay()));
protected readonly suspendedWatchRequestPollingInterval: number = 5007; // node.js default
private joinWatch = new DeferredPromise<void>();
@ -88,17 +90,21 @@ export abstract class BaseWatcher extends Disposable implements IWatcher {
}
}
return await this.updateWatchers();
return await this.updateWatchers(false /* not delayed */);
} finally {
this.joinWatch.complete();
}
}
private updateWatchers(): Promise<void> {
return this.doWatch([
private updateWatchers(delayed: boolean): Promise<void> {
return this.updateWatchersDelayer.trigger(() => this.doWatch([
...this.allNonCorrelatedWatchRequests,
...Array.from(this.allCorrelatedWatchRequests.values()).filter(request => !this.suspendedWatchRequests.has(request.correlationId))
]);
]), delayed ? this.getUpdateWatchersDelay() : 0);
}
protected getUpdateWatchersDelay(): number {
return 800;
}
isSuspended(request: IUniversalWatchRequest): 'polling' | boolean {
@ -130,14 +136,14 @@ export abstract class BaseWatcher extends Disposable implements IWatcher {
this.monitorSuspendedWatchRequest(request, disposables);
this.updateWatchers();
this.updateWatchers(true /* delay this call as we might accumulate many failing watch requests on startup */);
}
private resumeWatchRequest(request: IWatchRequestWithCorrelation): void {
this.suspendedWatchRequests.deleteAndDispose(request.correlationId);
this.suspendedWatchRequestsWithPolling.delete(request.correlationId);
this.updateWatchers();
this.updateWatchers(false);
}
private monitorSuspendedWatchRequest(request: IWatchRequestWithCorrelation, disposables: DisposableStore): void {

View file

@ -40,6 +40,10 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int
readonly onWatchFail = this._onDidWatchFail.event;
protected override getUpdateWatchersDelay(): number {
return 0;
}
protected override async doWatch(requests: INonRecursiveWatchRequest[]): Promise<void> {
await super.doWatch(requests);
for (const watcher of this.watchers) {

View file

@ -42,6 +42,10 @@ export class TestParcelWatcher extends ParcelWatcher {
return this.removeDuplicateRequests(requests, false /* validate paths skipped for tests */).map(request => request.path);
}
protected override getUpdateWatchersDelay(): number {
return 0;
}
protected override async doWatch(requests: IRecursiveWatchRequest[]): Promise<void> {
await super.doWatch(requests);
await this.whenReady();