fix(maintenance): prevent CLI hanging on occassion

fix(maintenance): always ack messages
fix(maintenance): ensure Redis is connected first
This commit is contained in:
izzy
2025-12-19 13:52:57 +00:00
parent 125de91c71
commit 8ca692bfb0
4 changed files with 18 additions and 39 deletions

View File

@@ -37,7 +37,10 @@ export class MaintenanceWebsocketRepository implements OnGatewayConnection, OnGa
afterInit(websocketServer: Server) {
this.logger.log('Initialized websocket server');
websocketServer.on('AppRestart', () => this.appRepository.exitApp());
websocketServer.on('AppRestart', (_, ack) => {
ack('ok');
this.appRepository.exitApp();
});
}
clientBroadcast<T extends keyof ClientEventMap>(event: T, ...data: ClientEventMap[T]) {

View File

@@ -56,7 +56,7 @@ export class CliService extends BaseService {
const state = { isMaintenanceMode: false as const };
await this.systemMetadataRepository.set(SystemMetadataKey.MaintenanceMode, state);
sendOneShotAppRestart(state);
await sendOneShotAppRestart(state);
return {
alreadyDisabled: false,
@@ -89,7 +89,7 @@ export class CliService extends BaseService {
secret,
});
sendOneShotAppRestart({
await sendOneShotAppRestart({
isMaintenanceMode: true,
});

View File

@@ -31,7 +31,8 @@ export class MaintenanceService extends BaseService {
}
@OnEvent({ name: 'AppRestart', server: true })
onRestart(): void {
onRestart(_: undefined, ack: (ok: 'ok') => void): void {
ack('ok');
this.appRepository.exitApp();
}

View File

@@ -7,47 +7,22 @@ import { MaintenanceAuthDto } from 'src/dtos/maintenance.dto';
import { ConfigRepository } from 'src/repositories/config.repository';
import { AppRestartEvent } from 'src/repositories/event.repository';
export function sendOneShotAppRestart(state: AppRestartEvent): void {
export async function sendOneShotAppRestart(state: AppRestartEvent): Promise<void> {
const server = new SocketIO();
const { redis } = new ConfigRepository().getEnv();
const pubClient = new Redis(redis);
const pubClient = new Redis({ ...redis, lazyConnect: true });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
server.adapter(createAdapter(pubClient, subClient));
/**
* Keep trying until we manage to stop Immich
*
* Sometimes there appear to be communication
* issues between to the other servers.
*
* This issue only occurs with this method.
*/
async function tryTerminate() {
while (true) {
try {
const responses = await server.serverSideEmitWithAck('AppRestart', state);
if (responses.length > 0) {
return;
}
} catch (error) {
console.error(error);
console.error('Encountered an error while telling Immich to stop.');
}
console.info(
"\nIt doesn't appear that Immich stopped, trying again in a moment.\nIf Immich is already not running, you can ignore this error.",
);
await new Promise((r) => setTimeout(r, 1e3));
}
}
// => corresponds to notification.service.ts#onAppRestart
server.emit('AppRestartV1', state, () => {
void tryTerminate().finally(() => {
pubClient.disconnect();
subClient.disconnect();
});
server.emit('AppRestartV1', state, async () => {
await server.serverSideEmitWithAck('AppRestart', state);
pubClient.disconnect();
subClient.disconnect();
});
}