Skip to content

Commit a57ef18

Browse files
authored
respond to adapter webserver upgrade message and optimize downtime (#2399)
* respoond to webserver upgrade message * unlike controller ws, this is not a separate process, thus do not call process exit * destroy all sockets, to prevent server beeing kept alive from new requests * typo
1 parent e3f3f7d commit a57ef18

File tree

3 files changed

+75
-1
lines changed

3 files changed

+75
-1
lines changed

packages/controller/src/lib/adapterUpgradeManager.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { setTimeout as wait } from 'timers/promises';
77
import type { Logger } from 'winston';
88
import { Upgrade } from '@iobroker/js-controller-cli';
99
import type { ProcessExitCallback } from '@iobroker/js-controller-cli/build/lib/_Types';
10+
import type { Socket } from 'node:net';
11+
import type { Duplex } from 'node:stream';
1012

1113
interface Certificates {
1214
/** Public certificate */
@@ -80,6 +82,8 @@ export class AdapterUpgradeManager {
8082

8183
/** The server used for communicating upgrade status */
8284
private server?: https.Server | http.Server;
85+
/** All socket connections of the webserver */
86+
private sockets = new Set<Socket | Duplex>();
8387
/** Name of the host for logging purposes */
8488
private readonly hostname = tools.getHostName();
8589
/** The objects DB client */
@@ -202,15 +206,27 @@ export class AdapterUpgradeManager {
202206
}
203207

204208
if (!this.server) {
205-
process.exit();
209+
return;
206210
}
207211

212+
this.destroySockets();
213+
208214
this.server.close(async () => {
209215
await this.startAdapter();
210216
this.log('Successfully started adapter');
211217
});
212218
}
213219

220+
/**
221+
* Destroy all sockets, to prevent requests from keeping server alive
222+
*/
223+
destroySockets(): void {
224+
for (const socket of this.sockets) {
225+
socket.destroy();
226+
this.sockets.delete(socket);
227+
}
228+
}
229+
214230
/**
215231
* This function is called when the webserver receives a message
216232
*
@@ -276,6 +292,8 @@ export class AdapterUpgradeManager {
276292
this.webServerCallback(req, res);
277293
});
278294

295+
this.monitorSockets(this.server);
296+
279297
this.server.listen(port, () => {
280298
this.log(`Server is running on http://localhost:${port}`);
281299
});
@@ -295,11 +313,28 @@ export class AdapterUpgradeManager {
295313
this.webServerCallback(req, res);
296314
});
297315

316+
this.monitorSockets(this.server);
317+
298318
this.server.listen(port, () => {
299319
this.log(`Server is running on http://localhost:${port}`);
300320
});
301321
}
302322

323+
/**
324+
* Keep track of all existing sockets
325+
*
326+
* @param server the webserver
327+
*/
328+
monitorSockets(server: http.Server | https.Server): void {
329+
server.on('connection', socket => {
330+
this.sockets.add(socket);
331+
332+
server.once('close', () => {
333+
this.sockets.delete(socket);
334+
});
335+
});
336+
}
337+
303338
/**
304339
* Get certificates from the DB
305340
*

packages/controller/src/lib/upgradeManager.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import type { Client as ObjectsClient } from '@iobroker/db-objects-redis';
88
import { setTimeout as wait } from 'timers/promises';
99
import type { Logger } from 'winston';
1010
import fs from 'fs-extra';
11+
import type { Socket } from 'node:net';
12+
import type { Duplex } from 'node:stream';
1113

1214
export interface UpgradeArguments {
1315
/** Version of controller to upgrade too */
@@ -73,6 +75,8 @@ class UpgradeManager {
7375

7476
/** The server used for communicating upgrade status */
7577
private server?: https.Server | http.Server;
78+
/** All socket connections of the webserver */
79+
private sockets = new Set<Socket | Duplex>();
7680
/** Name of the host for logging purposes */
7781
private readonly hostname = tools.getHostName();
7882

@@ -206,6 +210,8 @@ class UpgradeManager {
206210
process.exit();
207211
}
208212

213+
this.destroySockets();
214+
209215
this.server.close(async () => {
210216
await this.startController();
211217
this.log('Successfully started js-controller');
@@ -214,6 +220,16 @@ class UpgradeManager {
214220
});
215221
}
216222

223+
/**
224+
* Destroy all sockets, to prevent requests from keeping server alive
225+
*/
226+
destroySockets(): void {
227+
for (const socket of this.sockets) {
228+
socket.destroy();
229+
this.sockets.delete(socket);
230+
}
231+
}
232+
217233
/**
218234
* This function is called when the webserver receives a message
219235
*
@@ -245,6 +261,8 @@ class UpgradeManager {
245261
this.webServerCallback(req, res);
246262
});
247263

264+
this.monitorSockets(this.server);
265+
248266
this.server.listen(port, () => {
249267
this.log(`Server is running on http://localhost:${port}`);
250268
});
@@ -262,11 +280,28 @@ class UpgradeManager {
262280
this.webServerCallback(req, res);
263281
});
264282

283+
this.monitorSockets(this.server);
284+
265285
this.server.listen(port, () => {
266286
this.log(`Server is running on https://localhost:${port}`);
267287
});
268288
}
269289

290+
/**
291+
* Keep track of all existing sockets
292+
*
293+
* @param server the webserver
294+
*/
295+
monitorSockets(server: http.Server | https.Server): void {
296+
server.on('connection', socket => {
297+
this.sockets.add(socket);
298+
299+
server.once('close', () => {
300+
this.sockets.delete(socket);
301+
});
302+
});
303+
}
304+
270305
/**
271306
* Get certificates from the DB
272307
*

packages/controller/src/main.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,6 +3199,10 @@ async function processMessage(msg: ioBroker.SendableMessage): Promise<null | voi
31993199
certPublicName
32003200
});
32013201

3202+
if (msg.callback) {
3203+
sendTo(msg.from, msg.command, { result: true }, msg.callback);
3204+
}
3205+
32023206
await upgradeManager.stopAdapter();
32033207
await upgradeManager.startWebServer();
32043208
await upgradeManager.performUpgrade();

0 commit comments

Comments
 (0)