Skip to content

Commit 4b73041

Browse files
committed
fix(stats): unsubscribe from stats subscription when leaving dashboard
1 parent a9ec7e5 commit 4b73041

File tree

4 files changed

+83
-50
lines changed

4 files changed

+83
-50
lines changed

e2e/140_dashboard.e2e.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ describe('Dashboard', () => {
3434

3535
it('should go to dashboard and see memory widget', () => {
3636
return browser.get('/dashboard')
37-
.then((): any => browser.wait(() => element(by.css('[name="memory-usage"]')).isPresent()))
38-
.then(() => element(by.css('[name="memory-usage"]')).getCssValue('width'))
39-
.then(width => expect(width).to.not.equals('0px'));
37+
.then((): any => browser.wait(() => element(by.css('[name="memory-usage"]')).isPresent()))
38+
.then(() => element(by.css('[name="memory-usage"]')).getCssValue('width'))
39+
.then(width => expect(width).to.not.equals('0px'));
4040
});
4141
});

src/api/docker-stats.ts

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,41 @@
11
import { Observable, Observer } from 'rxjs';
22
import * as utils from './utils';
3-
import * as dockerode from 'dockerode';
3+
import { listContainers, calculateContainerStats } from './docker';
44
import { processes } from './process-manager';
55

6-
export const docker = new dockerode();
7-
86
export function getContainersStats(): Observable<any> {
97
return new Observable(observer => {
10-
Observable
8+
const sub = Observable
119
.interval(2000)
1210
.timeInterval()
1311
.mergeMap(() => {
14-
return docker.listContainers().then(containers => {
15-
return Promise.all(containers.map(container => {
16-
return docker.getContainer(container.Id).stats().then((stream: any) => {
17-
let json = '';
18-
return new Promise(resolve => {
19-
stream.on('data', buf => {
20-
let rawJson = json + buf.toString();
21-
try {
22-
let data = JSON.parse(rawJson);
23-
24-
if (data && data.precpu_stats.system_cpu_usage) {
25-
const jobId = container.Names[0].split('_')[2] || -1;
26-
const job = processes.find(p => p.job_id === Number(jobId));
27-
let debug = false;
28-
if (job) {
29-
debug = job.debug || false;
30-
}
31-
32-
const stats = {
33-
id: container.Id,
34-
name: container.Names[0].substr(1) || '',
35-
cpu: getCpuData(data),
36-
network: getNetworkData(data),
37-
memory: getMemory(data),
38-
debug: debug
39-
};
40-
41-
stream.destroy();
42-
resolve(stats);
43-
}
44-
} catch (e) {
45-
json = rawJson;
46-
}
47-
});
48-
});
49-
});
50-
})).then(stats => stats);
51-
});
12+
return listContainers()
13+
.then(containers => Promise.all(containers.map(c => getContainerStats(c))));
5214
})
5315
.map(stats => observer.next({ type: 'containersStats', data: stats }))
5416
.subscribe();
17+
18+
return () => {
19+
if (sub) {
20+
sub.unsubscribe();
21+
}
22+
};
5523
}).share();
5624
}
5725

26+
function getContainerStats(container: any): Promise<any> {
27+
return calculateContainerStats(container, processes).then(stats => {
28+
return {
29+
id: stats.id,
30+
name: stats.name,
31+
cpu: getCpuData(stats.data),
32+
network: getNetworkData(stats.data),
33+
memory: getMemory(stats.data),
34+
debug: stats.debug
35+
};
36+
});
37+
}
38+
5839
function getCpuData(json: any): { usage: string, cores: number } {
5940
const postCpuStats = json.cpu_stats;
6041
const preCpuStats = json.precpu_stats;

src/api/docker.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,12 @@ export function attachExec(id: string, cmd: any): Observable<any> {
137137
});
138138
}
139139

140+
export function listContainers(): Promise<dockerode.ContainerInfo[]> {
141+
return docker.listContainers();
142+
}
143+
140144
export function stopAllContainers(): Promise<any[]> {
141-
return docker.listContainers()
145+
return listContainers()
142146
.then(containers => {
143147
return Promise.all(containers.map(containerInfo => {
144148
return docker.getContainer(containerInfo.Id).stop();
@@ -255,3 +259,42 @@ export function isDockerInstalled(): Observable<boolean> {
255259
});
256260
});
257261
}
262+
263+
export function calculateContainerStats(
264+
container: dockerode.ContainerInfo,
265+
processes: any
266+
): Promise<any> {
267+
return docker.getContainer(container.Id).stats()
268+
.then(stream => {
269+
let json = '';
270+
return new Promise((resolve, reject) => {
271+
stream.on('data', buf => {
272+
let rawJson = json + buf.toString();
273+
try {
274+
let data = JSON.parse(rawJson);
275+
276+
if (data && data.precpu_stats.system_cpu_usage) {
277+
const jobId = container.Names[0].split('_')[2] || -1;
278+
const job = processes.find(p => p.job_id === Number(jobId));
279+
let debug = false;
280+
if (job) {
281+
debug = job.debug || false;
282+
}
283+
284+
const stats = {
285+
id: container.Id,
286+
name: container.Names[0].substr(1) || '',
287+
debug: debug,
288+
data: data
289+
};
290+
291+
stream.destroy();
292+
resolve(stats);
293+
}
294+
} catch (e) {
295+
json = rawJson;
296+
}
297+
});
298+
});
299+
});
300+
}

src/api/socket.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export interface Client {
4141
session: { cookie: any, ip: string, userId: number, email: string, isAdmin: boolean };
4242
socket: uws.Socket;
4343
send: Function;
44+
subscriptions: { stats: Subscription };
4445
}
4546

4647
export class SocketServer {
@@ -107,7 +108,8 @@ export class SocketServer {
107108
sessionID: socket.upgradeReq.sessionID,
108109
session: socket.upgradeReq.session,
109110
socket: socket,
110-
send: (message: any) => client.socket.send(JSON.stringify(message))
111+
send: (message: any) => client.socket.send(JSON.stringify(message)),
112+
subscriptions: { stats: null }
111113
};
112114
this.addClient(client);
113115

@@ -306,9 +308,16 @@ export class SocketServer {
306308
break;
307309

308310
case 'subscribeToStats':
309-
Observable.merge(...[
310-
memory(), cpu(), getContainersStats()
311-
]).subscribe(event => client.send(event));
311+
client.subscriptions.stats =
312+
Observable.merge(...[
313+
memory(), cpu(), getContainersStats()
314+
]).subscribe(event => client.send(event));
315+
break;
316+
317+
case 'unsubscribeFromStats':
318+
if (client.subscriptions.stats) {
319+
client.subscriptions.stats.unsubscribe();
320+
}
312321
break;
313322
}
314323
}

0 commit comments

Comments
 (0)