Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/dev/ide/profile-workspace.js
2492 views
1
/**
2
* Run node ./dev/ide/capture-ws-top.js to profile cpu and memory usage of the workspace.
3
*
4
*/
5
//@ts-check
6
7
const { execSync } = require("child_process");
8
const { promises } = require("fs");
9
const fetch = require("node-fetch");
10
const path = require("path");
11
12
/**
13
* @param {number[]} arr
14
* @returns {number}
15
*/
16
const q90 = (arr) => {
17
const sorted = arr.sort((a, b) => a - b);
18
const pos = (sorted.length - 1) * 0.9;
19
const base = Math.floor(pos);
20
const rest = pos - base;
21
if (sorted[base + 1] !== undefined) {
22
return sorted[base] + rest * (sorted[base + 1] - sorted[base]);
23
} else {
24
return sorted[base];
25
}
26
};
27
28
(async () => {
29
let wsPodName;
30
let creationTimestamp;
31
while (!wsPodName) {
32
const segments = execSync(
33
"kubectl get pod -l component=workspace -o=custom-columns=:metadata.name,:metadata.creationTimestamp",
34
{
35
encoding: "utf8",
36
},
37
)
38
.trim()
39
.split(/\s+/);
40
wsPodName = segments[0];
41
creationTimestamp = new Date(segments[1]);
42
await new Promise((r) => setTimeout(r, 1000));
43
}
44
console.log(wsPodName);
45
console.log(creationTimestamp);
46
47
const query = async (prefix) => {
48
const age = ((new Date().getTime() - creationTimestamp.getTime()) / 1000).toFixed(0);
49
const response = await fetch.default(
50
encodeURI(`http://localhost:9090/api/v1/query?query=${prefix}{pod="${wsPodName}"}[${age}s])/(1024*1024)`),
51
{
52
method: "GET",
53
},
54
);
55
if (!response.ok) {
56
console.error(`${prefix}: ${response.statusText} (${response.status})`);
57
return "N/A";
58
}
59
/**
60
* @type {{data: {result: { value: [number, string] }[]}}}
61
*/
62
const body = await response.json();
63
return Number(body.data.result[0].value[1]).toFixed(2);
64
};
65
66
const perfLogPath = path.resolve(__dirname, "perf.log");
67
console.log(perfLogPath);
68
const start = new Date().getTime();
69
await promises.writeFile(perfLogPath, wsPodName + "\n", { encoding: "utf8" });
70
let measurements = 0;
71
const cores = [];
72
const mems = [];
73
let avgCores = 0;
74
let maxCores = 0;
75
let sumCores = 0;
76
let avgMemory = 0;
77
let maxMemory = 0;
78
let sumMemory = 0;
79
while (true) {
80
measurements++;
81
82
/**
83
* @type {{memory: {used: number, limit: number}, cpu: {used: number, limit: number}} | undefined}
84
*/
85
let top;
86
try {
87
const content = execSync(`kubectl exec -t ${wsPodName} -- /.supervisor/supervisor top -sj`, {
88
encoding: "utf8",
89
}).trim();
90
const value = JSON.parse(content);
91
if ("memory" in value && "cpu" in value) {
92
top = value;
93
}
94
} catch (e) {
95
console.error(e);
96
}
97
98
if (top) {
99
cores.push(top.cpu.used);
100
sumCores += top.cpu.used;
101
avgCores = sumCores / measurements;
102
maxCores = Math.max(maxCores, top.cpu.used);
103
104
const mem = top.memory.used / (1024 * 1024);
105
mems.push(mem);
106
sumMemory += mem;
107
avgMemory = sumMemory / measurements;
108
maxMemory = Math.max(maxMemory, mem);
109
110
const [avgMax, maxMax, q90Max, avgUsed, maxUsed, q90user] = (
111
await Promise.allSettled([
112
query("avg_over_time(gitpod_jb_backend_memory_max_bytes"),
113
query("max_over_time(gitpod_jb_backend_memory_max_bytes"),
114
query("quantile_over_time(0.9, gitpod_jb_backend_memory_max_bytes"),
115
query("avg_over_time(gitpod_jb_backend_memory_used_bytes"),
116
query("max_over_time(gitpod_jb_backend_memory_used_bytes"),
117
query("quantile_over_time(0.9, gitpod_jb_backend_memory_used_bytes"),
118
])
119
).map((v) => {
120
if (v.status === "fulfilled") {
121
return v.value;
122
}
123
console.error(v.reason);
124
return "N/A";
125
});
126
127
await promises.appendFile(
128
perfLogPath,
129
`${((new Date().getTime() - start) / 1000).toFixed(2)}s, cpu(m) ${top.cpu.used.toFixed(
130
2,
131
)}/${avgCores.toFixed(2)}/${maxCores.toFixed(2)}/${q90(cores).toFixed(2)}, memory(Mi) ${mem.toFixed(
132
2,
133
)}/${avgMemory.toFixed(2)}/${maxMemory.toFixed(2)}/${q90(mems).toFixed(
134
2,
135
)}, allocated(M) ${avgMax}/${maxMax}/${q90Max}, used(M) ${avgUsed}/${maxUsed}/${q90user}\n`,
136
);
137
}
138
await new Promise((r) => setTimeout(r, 1000));
139
}
140
})();
141
142