Optimize real-time updates in downloads service with throttling to reduce CPU usage
This commit is contained in:
parent
84c6418f91
commit
0a807de861
2 changed files with 34 additions and 4 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { AsyncPipe, DatePipe, KeyValuePipe } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, viewChild, inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Observable, map, distinctUntilChanged } from 'rxjs';
|
||||
import { Observable, map, distinctUntilChanged, auditTime } from 'rxjs';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||
|
|
@ -250,8 +250,13 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
|||
this.rebuildSortedDone();
|
||||
this.cdr.markForCheck();
|
||||
});
|
||||
// Subscribe to real-time updates
|
||||
this.downloads.updated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
||||
// Subscribe to real-time updates (throttled to reduce CPU on large queues).
|
||||
this.downloads.updated
|
||||
.pipe(
|
||||
auditTime(200),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.updateMetrics();
|
||||
this.cdr.markForCheck();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ export class DownloadsService {
|
|||
ytdlOptionsChanged = new Subject<Record<string, unknown>>();
|
||||
configurationChanged = new Subject<Record<string, unknown>>();
|
||||
updated = new Subject<void>();
|
||||
private updateRefreshScheduled = false;
|
||||
private readonly foregroundRefreshMs = 250;
|
||||
private readonly backgroundRefreshMs = 30000;
|
||||
|
||||
configuration: Record<string, unknown> = {};
|
||||
customDirs: Record<string, string[]> = {};
|
||||
|
|
@ -68,7 +71,7 @@ export class DownloadsService {
|
|||
data.checked = !!dl?.checked;
|
||||
data.deleting = !!dl?.deleting;
|
||||
this.queue.set(data.url, data);
|
||||
this.updated.next();
|
||||
this.scheduleUpdatedRefresh();
|
||||
});
|
||||
this.socket.fromEvent('completed')
|
||||
.pipe(takeUntilDestroyed())
|
||||
|
|
@ -117,6 +120,28 @@ export class DownloadsService {
|
|||
});
|
||||
}
|
||||
|
||||
private scheduleUpdatedRefresh() {
|
||||
// Coalesce high-frequency download progress events into a capped refresh rate.
|
||||
// requestAnimationFrame (~60fps) is smooth but expensive on large queues.
|
||||
// We trade a little smoothness for much lower CPU:
|
||||
// - foreground: ~8fps
|
||||
// - background: ~1fps
|
||||
if (this.updateRefreshScheduled) {
|
||||
return;
|
||||
}
|
||||
this.updateRefreshScheduled = true;
|
||||
|
||||
const flush = () => {
|
||||
this.updateRefreshScheduled = false;
|
||||
this.updated.next();
|
||||
};
|
||||
|
||||
const delay = document.hidden
|
||||
? this.backgroundRefreshMs
|
||||
: this.foregroundRefreshMs;
|
||||
setTimeout(() => flush(), delay);
|
||||
}
|
||||
|
||||
handleHTTPError(error: HttpErrorResponse) {
|
||||
const msg = error.error instanceof ErrorEvent
|
||||
? error.error.message
|
||||
|
|
|
|||
Loading…
Reference in a new issue