Skip to content

Commit 15ff0e7

Browse files
committed
PR comments
1 parent 464162e commit 15ff0e7

File tree

5 files changed

+103
-20
lines changed

5 files changed

+103
-20
lines changed

src/core/server/metrics/collectors/cgroup.ts

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,49 +29,43 @@ interface OsCgroupMetricsCollectorOptions {
2929
}
3030

3131
export class OsCgroupMetricsCollector implements MetricsCollector<OsCgroupMetrics> {
32-
// Used to prevent unnecessary file reads on systems not using cgroups
32+
/** Used to prevent unnecessary file reads on systems not using cgroups. */
3333
private noCgroupPresent = false;
34+
private cpuPath?: string;
35+
private cpuAcctPath?: string;
3436

3537
constructor(private readonly options: OsCgroupMetricsCollectorOptions) {}
3638

3739
public async collect(): Promise<OsCgroupMetrics> {
38-
if (this.noCgroupPresent) {
39-
return {};
40-
}
41-
4240
try {
43-
const cgroups = await readControlGroups();
44-
const cpuPath = this.options.cpuPath || cgroups[GROUP_CPU];
45-
const cpuAcctPath = this.options.cpuAcctPath || cgroups[GROUP_CPUACCT];
46-
47-
// prevents undefined cgroup paths
48-
if (!cpuPath || !cpuAcctPath) {
49-
this.noCgroupPresent = true;
41+
await this.initializePaths();
42+
if (this.noCgroupPresent || !this.cpuAcctPath || !this.cpuPath) {
5043
return {};
5144
}
5245

5346
const [cpuAcctUsage, cpuFsPeriod, cpuFsQuota, cpuStat] = await Promise.all([
54-
readCPUAcctUsage(cpuAcctPath),
55-
readCPUFsPeriod(cpuPath),
56-
readCPUFsQuota(cpuPath),
57-
readCPUStat(cpuPath),
47+
readCPUAcctUsage(this.cpuAcctPath),
48+
readCPUFsPeriod(this.cpuPath),
49+
readCPUFsQuota(this.cpuPath),
50+
readCPUStat(this.cpuPath),
5851
]);
5952

6053
return {
6154
cpuacct: {
62-
control_group: cpuAcctPath,
55+
control_group: this.cpuAcctPath,
6356
usage_nanos: cpuAcctUsage,
6457
},
6558

6659
cpu: {
67-
control_group: cpuPath,
60+
control_group: this.cpuPath,
6861
cfs_period_micros: cpuFsPeriod,
6962
cfs_quota_micros: cpuFsQuota,
7063
stat: cpuStat,
7164
},
7265
};
7366
} catch (err) {
7467
if (err.code === 'ENOENT') {
68+
this.noCgroupPresent = true;
7569
return {};
7670
} else {
7771
throw err;
@@ -80,6 +74,29 @@ export class OsCgroupMetricsCollector implements MetricsCollector<OsCgroupMetric
8074
}
8175

8276
public reset() {}
77+
78+
private async initializePaths() {
79+
// Perform this setup lazily on the first collect call and then memoize the results.
80+
// Makes the assumption this data doesn't change while the process is running.
81+
if (this.cpuPath && this.cpuAcctPath) {
82+
return;
83+
}
84+
85+
// Only read the file if both options are undefined.
86+
if (!this.options.cpuPath || !this.options.cpuAcctPath) {
87+
const cgroups = await readControlGroups();
88+
this.cpuPath = this.options.cpuPath || cgroups[GROUP_CPU];
89+
this.cpuAcctPath = this.options.cpuAcctPath || cgroups[GROUP_CPUACCT];
90+
} else {
91+
this.cpuPath = this.options.cpuPath;
92+
this.cpuAcctPath = this.options.cpuAcctPath;
93+
}
94+
95+
// prevents undefined cgroup paths
96+
if (!this.cpuPath || !this.cpuAcctPath) {
97+
this.noCgroupPresent = true;
98+
}
99+
}
83100
}
84101

85102
const CONTROL_GROUP_RE = new RegExp('\\d+:([^:]+):(/.*)');
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { MetricsCollector } from './types';
21+
22+
const createCollector = (collectReturnValue: any = {}): jest.Mocked<MetricsCollector<any>> => {
23+
const collector: jest.Mocked<MetricsCollector<any>> = {
24+
collect: jest.fn().mockResolvedValue(collectReturnValue),
25+
reset: jest.fn(),
26+
};
27+
28+
return collector;
29+
};
30+
31+
export const metricsCollectorMock = {
32+
create: createCollector,
33+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { metricsCollectorMock } from './collector.mock';
21+
22+
export const cgroupCollectorMock = metricsCollectorMock.create();
23+
jest.doMock('./cgroup', () => ({
24+
OsCgroupMetricsCollector: jest.fn(() => cgroupCollectorMock),
25+
}));

src/core/server/metrics/collectors/os.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
jest.mock('getos', () => (cb: Function) => cb(null, { dist: 'distrib', release: 'release' }));
2121

2222
import os from 'os';
23+
import { cgroupCollectorMock } from './os.test.mocks';
2324
import { OsMetricsCollector } from './os';
2425

2526
describe('OsMetricsCollector', () => {
2627
let collector: OsMetricsCollector;
2728

2829
beforeEach(() => {
2930
collector = new OsMetricsCollector();
31+
cgroupCollectorMock.collect.mockReset();
32+
cgroupCollectorMock.reset.mockReset();
3033
});
3134

3235
afterEach(() => {
@@ -96,4 +99,9 @@ describe('OsMetricsCollector', () => {
9699
'15m': fifteenMinLoad,
97100
});
98101
});
102+
103+
it('calls the cgroup sub-collector', async () => {
104+
await collector.collect();
105+
expect(cgroupCollectorMock.collect).toHaveBeenCalled();
106+
});
99107
});

src/core/server/metrics/metrics_service.mock.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ type MetricsServiceContract = PublicMethodsOf<MetricsService>;
7676

7777
const createMock = () => {
7878
const mocked: jest.Mocked<MetricsServiceContract> = {
79-
setup: jest.fn().mockReturnValue(createSetupContractMock()),
80-
start: jest.fn().mockReturnValue(createStartContractMock()),
79+
setup: jest.fn().mockReturnValue(createInternalSetupContractMock()),
80+
start: jest.fn().mockReturnValue(createInternalStartContractMock()),
8181
stop: jest.fn(),
8282
};
8383
return mocked;

0 commit comments

Comments
 (0)