@stamhoofd/backend 2.137.2 → 2.137.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +18 -18
- package/src/boot.ts +15 -2
- package/src/endpoints/system/HealthEndpoint.ts +1 -1
- package/src/services/SSOService.ts +0 -1
- package/src/services/CpuService.ts +0 -120
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.137.
|
|
3
|
+
"version": "2.137.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -55,23 +55,23 @@
|
|
|
55
55
|
"@mollie/api-client": "4.6.0",
|
|
56
56
|
"@simonbackx/simple-database": "1.37.1",
|
|
57
57
|
"@simonbackx/simple-encoding": "2.26.11",
|
|
58
|
-
"@simonbackx/simple-endpoints": "1.
|
|
58
|
+
"@simonbackx/simple-endpoints": "1.22.0",
|
|
59
59
|
"@simonbackx/simple-errors": "1.5.0",
|
|
60
60
|
"@simonbackx/simple-logging": "1.0.1",
|
|
61
|
-
"@stamhoofd/backend-env": "2.137.
|
|
62
|
-
"@stamhoofd/backend-i18n": "2.137.
|
|
63
|
-
"@stamhoofd/backend-middleware": "2.137.
|
|
64
|
-
"@stamhoofd/crons": "2.137.
|
|
65
|
-
"@stamhoofd/email": "2.137.
|
|
66
|
-
"@stamhoofd/excel-writer": "2.137.
|
|
67
|
-
"@stamhoofd/logging": "2.137.
|
|
68
|
-
"@stamhoofd/models": "2.137.
|
|
69
|
-
"@stamhoofd/object-differ": "2.137.
|
|
70
|
-
"@stamhoofd/queues": "2.137.
|
|
71
|
-
"@stamhoofd/sql": "2.137.
|
|
72
|
-
"@stamhoofd/structures": "2.137.
|
|
73
|
-
"@stamhoofd/types": "2.137.
|
|
74
|
-
"@stamhoofd/utility": "2.137.
|
|
61
|
+
"@stamhoofd/backend-env": "2.137.3",
|
|
62
|
+
"@stamhoofd/backend-i18n": "2.137.3",
|
|
63
|
+
"@stamhoofd/backend-middleware": "2.137.3",
|
|
64
|
+
"@stamhoofd/crons": "2.137.3",
|
|
65
|
+
"@stamhoofd/email": "2.137.3",
|
|
66
|
+
"@stamhoofd/excel-writer": "2.137.3",
|
|
67
|
+
"@stamhoofd/logging": "2.137.3",
|
|
68
|
+
"@stamhoofd/models": "2.137.3",
|
|
69
|
+
"@stamhoofd/object-differ": "2.137.3",
|
|
70
|
+
"@stamhoofd/queues": "2.137.3",
|
|
71
|
+
"@stamhoofd/sql": "2.137.3",
|
|
72
|
+
"@stamhoofd/structures": "2.137.3",
|
|
73
|
+
"@stamhoofd/types": "2.137.3",
|
|
74
|
+
"@stamhoofd/utility": "2.137.3",
|
|
75
75
|
"archiver": "7.0.1",
|
|
76
76
|
"axios": "1.18.1",
|
|
77
77
|
"base-x": "3.0.11",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"uuid": "14.0.1"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
|
-
"@stamhoofd/test-utils": "2.137.
|
|
96
|
+
"@stamhoofd/test-utils": "2.137.3",
|
|
97
97
|
"@types/cookie": "0.6.0",
|
|
98
98
|
"@types/luxon": "3.7.2",
|
|
99
99
|
"@types/mailparser": "3.4.6",
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
"publishConfig": {
|
|
109
109
|
"access": "public"
|
|
110
110
|
},
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "5f2515f1eab66fa40ddb0c19620d2181ccf3d945"
|
|
112
112
|
}
|
package/src/boot.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { CORSMiddleware, LogMiddleware, VersionMiddleware } from '@stamhoofd/bac
|
|
|
4
4
|
import { checkReadOnly } from '@stamhoofd/crons';
|
|
5
5
|
import { Email } from '@stamhoofd/email';
|
|
6
6
|
import { loadLogger } from '@stamhoofd/logging';
|
|
7
|
+
import { CpuService } from '@stamhoofd/logging/CpuService';
|
|
7
8
|
import { Version } from '@stamhoofd/structures';
|
|
8
9
|
import { sleep } from '@stamhoofd/utility';
|
|
9
10
|
|
|
@@ -20,14 +21,13 @@ import { ContextMiddleware } from './middleware/ContextMiddleware.js';
|
|
|
20
21
|
import { AuditLogService } from './services/AuditLogService.js';
|
|
21
22
|
import { BalanceItemService } from './services/BalanceItemService.js';
|
|
22
23
|
import { BootChecksService } from './services/BootChecksService.js';
|
|
23
|
-
import { CpuService } from './services/CpuService.js';
|
|
24
24
|
import { DocumentService } from './services/DocumentService.js';
|
|
25
25
|
import { FileSignService } from './services/FileSignService.js';
|
|
26
26
|
import { PlatformMembershipService } from './services/PlatformMembershipService.js';
|
|
27
27
|
import { UitpasService } from './services/uitpas/UitpasService.js';
|
|
28
28
|
import { UniqueMemberNumberService } from './services/UniqueMemberNumberService.js';
|
|
29
29
|
import { UniqueUserService } from './services/UniqueUserService.js';
|
|
30
|
-
import { QueryableModel } from '@stamhoofd/sql';
|
|
30
|
+
import { QueryableModel, SQLLogger } from '@stamhoofd/sql';
|
|
31
31
|
|
|
32
32
|
process.on('unhandledRejection', (error: Error) => {
|
|
33
33
|
console.error('unhandledRejection');
|
|
@@ -176,6 +176,19 @@ export const boot = async (options: { killProcess: boolean }) => {
|
|
|
176
176
|
resumeEmails().catch(console.error);
|
|
177
177
|
|
|
178
178
|
if (STAMHOOFD.environment !== 'development' && STAMHOOFD.environment !== 'test') {
|
|
179
|
+
// Tune SQL slow-query logging based on CPU load: under heavy load timings are unreliable,
|
|
180
|
+
// and when idle we can afford to log all slow queries.
|
|
181
|
+
CpuService.addSampleListener((average5s) => {
|
|
182
|
+
if (average5s > 80) {
|
|
183
|
+
// Danger zone: don't log slow queries any longer because the timings won't be trustworthy.
|
|
184
|
+
SQLLogger.slowQueryThresholdMs = null;
|
|
185
|
+
} else if (average5s < 20) {
|
|
186
|
+
// No load, safe to log all slow queries
|
|
187
|
+
SQLLogger.slowQueryThresholdMs = 300;
|
|
188
|
+
} else {
|
|
189
|
+
SQLLogger.slowQueryThresholdMs = 500;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
179
192
|
CpuService.startMonitoring();
|
|
180
193
|
} else if (STAMHOOFD.environment === 'development') {
|
|
181
194
|
const { loadDebugFunctions } = await import('./debug.js');
|
|
@@ -2,7 +2,7 @@ import { ArrayDecoder, AutoEncoder, field, StringDecoder } from '@simonbackx/sim
|
|
|
2
2
|
import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { Endpoint, Response } from '@simonbackx/simple-endpoints';
|
|
4
4
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
5
|
-
import { CpuService } from '
|
|
5
|
+
import { CpuService } from '@stamhoofd/logging/CpuService';
|
|
6
6
|
import { StartupHealthService } from '../../services/StartupHealthService.js';
|
|
7
7
|
|
|
8
8
|
type Params = Record<string, never>;
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { SQLLogger } from '@stamhoofd/sql';
|
|
2
|
-
|
|
3
|
-
class StaticCpuService {
|
|
4
|
-
samples: number[];
|
|
5
|
-
private maxSamples: number;
|
|
6
|
-
private interval?: NodeJS.Timeout;
|
|
7
|
-
// Current index = the last saved
|
|
8
|
-
private currentIndex = 0;
|
|
9
|
-
|
|
10
|
-
constructor() {
|
|
11
|
-
this.maxSamples = 5 * 60; // 5 minutes of data
|
|
12
|
-
this.samples = new Array(this.maxSamples).fill(0);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Get a live sample of CPU usage
|
|
17
|
-
* @param samplingInterval
|
|
18
|
-
* @returns
|
|
19
|
-
*/
|
|
20
|
-
async takeSample(samplingInterval = 500): Promise<number> {
|
|
21
|
-
const startUsage = process.cpuUsage();
|
|
22
|
-
const startTime = process.hrtime.bigint();
|
|
23
|
-
|
|
24
|
-
return new Promise((resolve) => {
|
|
25
|
-
setTimeout(() => {
|
|
26
|
-
const elapsedUsage = process.cpuUsage(startUsage);
|
|
27
|
-
const elapsedTime = process.hrtime.bigint() - startTime;
|
|
28
|
-
|
|
29
|
-
// Convert to microseconds
|
|
30
|
-
const elapsedTimeMs = Number(elapsedTime) / 1000;
|
|
31
|
-
|
|
32
|
-
// CPU time in microseconds
|
|
33
|
-
const totalCPUTime = elapsedUsage.user + elapsedUsage.system;
|
|
34
|
-
|
|
35
|
-
// Calculate percentage
|
|
36
|
-
const cpuPercent = (totalCPUTime / elapsedTimeMs) * 100;
|
|
37
|
-
|
|
38
|
-
resolve(cpuPercent);
|
|
39
|
-
}, samplingInterval);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
getCpuUsage(): number | undefined {
|
|
44
|
-
if (this.currentIndex === 0) {
|
|
45
|
-
return this.samples[this.samples.length - 1];
|
|
46
|
-
}
|
|
47
|
-
return this.samples[this.currentIndex - 1];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
getAverage(size = this.maxSamples): number {
|
|
51
|
-
if (size > this.maxSamples) {
|
|
52
|
-
size = this.maxSamples;
|
|
53
|
-
}
|
|
54
|
-
if (size <= 0) {
|
|
55
|
-
return NaN;
|
|
56
|
-
}
|
|
57
|
-
if (size === this.maxSamples) {
|
|
58
|
-
return this.samples.reduce((a, b) => a + b, 0) / size;
|
|
59
|
-
} else {
|
|
60
|
-
// To read before current index
|
|
61
|
-
const toReadBeforeIndex = Math.min(size, this.currentIndex);
|
|
62
|
-
const toReadFromEnd = size - toReadBeforeIndex;
|
|
63
|
-
|
|
64
|
-
// Sum performantly
|
|
65
|
-
let sum = 0;
|
|
66
|
-
for (let i = 0; i < toReadBeforeIndex; i++) {
|
|
67
|
-
sum += this.samples[this.currentIndex - 1 - i];
|
|
68
|
-
}
|
|
69
|
-
for (let i = 0; i < toReadFromEnd; i++) {
|
|
70
|
-
sum += this.samples[this.samples.length - 1 - i];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return sum / size;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private async saveSample() {
|
|
78
|
-
const sample = await this.takeSample(1000);
|
|
79
|
-
this.samples[this.currentIndex] = sample;
|
|
80
|
-
this.currentIndex = (this.currentIndex + 1) % this.maxSamples;
|
|
81
|
-
const five = this.getAverage(5);
|
|
82
|
-
|
|
83
|
-
if (this.currentIndex % 5 === 0 || five > 80) {
|
|
84
|
-
const min = this.getAverage(60);
|
|
85
|
-
console.log(`[CPU] 5s: ${five.toFixed(2)}%\n[CPU] 1 min: ${min.toFixed(2)}%\n[CPU] 5 min: ${this.getAverage(60 * 5).toFixed(2)}%`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (five > 80) {
|
|
89
|
-
// Danger zone, in this case we don't want to log all slow queries any longer because the information won't be trustworthy.
|
|
90
|
-
SQLLogger.slowQueryThresholdMs = null;
|
|
91
|
-
} else {
|
|
92
|
-
if (five < 20) {
|
|
93
|
-
// No load, safe to log all slow queries
|
|
94
|
-
SQLLogger.slowQueryThresholdMs = 300;
|
|
95
|
-
} else {
|
|
96
|
-
SQLLogger.slowQueryThresholdMs = 500;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
startMonitoring() {
|
|
102
|
-
if (this.interval) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
this.interval = setInterval(() => {
|
|
106
|
-
this.saveSample().catch((error) => {
|
|
107
|
-
console.error('Failed to take CPU sample:', error);
|
|
108
|
-
});
|
|
109
|
-
}, 1000); // Sample every second
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
stopMonitoring() {
|
|
113
|
-
if (this.interval) {
|
|
114
|
-
clearInterval(this.interval);
|
|
115
|
-
this.interval = undefined;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export const CpuService = new StaticCpuService();
|