Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add debounce to the status observers to reduce unnecessary CPU loops #108952

Merged
merged 2 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions src/core/server/status/plugins_status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ describe('PluginStatusService', () => {
subscription.unsubscribe();

expect(statusUpdates).toEqual([
{ a: { level: ServiceStatusLevels.available, summary: 'All dependencies are available' } },
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer needed since we don't emit the initial transient status anymore.

{ a: { level: ServiceStatusLevels.degraded, summary: 'a degraded' } },
{ a: { level: ServiceStatusLevels.unavailable, summary: 'a unavailable' } },
{ a: { level: ServiceStatusLevels.available, summary: 'a available' } },
Expand All @@ -274,7 +273,6 @@ describe('PluginStatusService', () => {
subscription.unsubscribe();

expect(statusUpdates).toEqual([
{ a: { level: ServiceStatusLevels.available, summary: 'All dependencies are available' } },
{ a: { level: ServiceStatusLevels.degraded, summary: 'a degraded' } },
{ a: { level: ServiceStatusLevels.unavailable, summary: 'a unavailable' } },
{ a: { level: ServiceStatusLevels.available, summary: 'a available' } },
Expand Down Expand Up @@ -357,6 +355,35 @@ describe('PluginStatusService', () => {
}).toThrowError();
});

it('debounces plugins custom status registration', async () => {
const service = new PluginsStatusService({
core$: coreAllAvailable$,
pluginDependencies,
});
const available: ServiceStatus = {
level: ServiceStatusLevels.available,
summary: 'a available',
};

const statusUpdates: Array<Record<string, ServiceStatus>> = [];
const subscription = service
.getDependenciesStatus$('b')
.subscribe((status) => statusUpdates.push(status));

const pluginA$ = new BehaviorSubject(available);
service.set('a', pluginA$);

expect(statusUpdates).toStrictEqual([]);

const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

// Waiting for the debounce timeout should cut a new update
await delay(25);
subscription.unsubscribe();

expect(statusUpdates).toStrictEqual([{ a: available }]);
});

it('debounces events in quick succession', async () => {
const service = new PluginsStatusService({
core$: coreAllAvailable$,
Expand Down
1 change: 1 addition & 0 deletions src/core/server/status/plugins_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export class PluginsStatusService {

public getDerivedStatus$(plugin: PluginName): Observable<ServiceStatus> {
return this.update$.pipe(
debounceTime(25), // Avoid calling the plugin's custom status logic for every plugin that depends on it.
switchMap(() => {
// Only go up the dependency tree if any of this plugin's dependencies have a custom status
// Helps eliminate memory overhead of creating thousands of Observables unnecessarily.
Expand Down