Skip to content

Commit 0fdbf6f

Browse files
committed
feat: enhance Socket.IO transport failure handling in cron logs
- Added `onSocketIoTransportFailure` function to manage transport errors and cleanup listeners in the cron logs component. - Integrated transport failure handling into the cron logs snapshot process, ensuring robust error management during socket communication. - Updated the cron.vue component to utilize the new transport failure handling, improving overall stability and user experience.
1 parent 45aade9 commit 0fdbf6f

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

apps/web/src/composables/useSocketIoClient.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,48 @@
1-
import type { ManagerOptions, SocketOptions } from 'socket.io-client'
1+
import type { ManagerOptions, Socket, SocketOptions } from 'socket.io-client'
22
import { tryUseNuxtApp, useRuntimeConfig } from '#app'
33

4+
const STALE_SOCKET_IO_DISCONNECT_REASONS = new Set([
5+
'transport error',
6+
'transport close',
7+
'ping timeout',
8+
'io server disconnect',
9+
])
10+
11+
/** Session Engine.IO invalide (ex. redémarrage API) alors que le namespace semble encore connecté. */
12+
export function isStaleSocketIoDisconnectReason(reason: string): boolean {
13+
return STALE_SOCKET_IO_DISCONNECT_REASONS.has(reason)
14+
}
15+
16+
/** Appelle `handler` sur erreur transport (ex. POST polling 400) puis retire les écouteurs. */
17+
export function onSocketIoTransportFailure(socket: Socket, handler: () => void): () => void {
18+
let handled = false
19+
const runOnce = () => {
20+
if (handled) {
21+
return
22+
}
23+
handled = true
24+
cleanup()
25+
handler()
26+
}
27+
28+
const onDisconnect = (reason: string) => {
29+
if (isStaleSocketIoDisconnectReason(reason)) {
30+
runOnce()
31+
}
32+
}
33+
const onError = () => runOnce()
34+
35+
socket.on('disconnect', onDisconnect)
36+
socket.io.on('error', onError)
37+
38+
const cleanup = () => {
39+
socket.off('disconnect', onDisconnect)
40+
socket.io.off('error', onError)
41+
}
42+
43+
return cleanup
44+
}
45+
446
/** Même préfixe que le proxy `/api/**` → API (cf. `nuxt.config.ts`). */
547
export const SOCKET_IO_PATH = '/api/socket.io'
648

apps/web/src/pages/settings/cron.vue

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@
264264
import type { LocationQueryValue } from 'vue-router'
265265
import { reactive, ref } from 'vue'
266266
import { attachSocketIoDebug } from '~/composables/useSocketIoDebug'
267-
import { buildSocketIoClientOptions } from '~/composables/useSocketIoClient'
267+
import { buildSocketIoClientOptions, onSocketIoTransportFailure } from '~/composables/useSocketIoClient'
268268
import { io, type Socket } from 'socket.io-client'
269269
import { NewTargetId } from '~/constants/variables'
270270
@@ -350,6 +350,7 @@ export default defineNuxtComponent({
350350
logsLastScrollTop: 0,
351351
logsSnapshotTimeout: null as ReturnType<typeof setTimeout> | null,
352352
logsSnapshotResolver: null as (() => void) | null,
353+
logsSnapshotTransportCleanup: null as (() => void) | null,
353354
logsHasScrolledDown: false,
354355
logsFollowTail: true,
355356
}
@@ -609,8 +610,13 @@ export default defineNuxtComponent({
609610
this.logsSnapshotTimeout = null
610611
}
611612
},
613+
clearLogsSnapshotTransportWatch(): void {
614+
this.logsSnapshotTransportCleanup?.()
615+
this.logsSnapshotTransportCleanup = null
616+
},
612617
finishCronLogsSnapshotRequest(): void {
613618
this.clearLogsSnapshotTimeout()
619+
this.clearLogsSnapshotTransportWatch()
614620
this.logsLoading = false
615621
this.logsLoadingMore = false
616622
this.logsLoadInFlight = false
@@ -662,6 +668,12 @@ export default defineNuxtComponent({
662668
663669
this.logsSocket.on('connect_error', () => {
664670
this.logsSocketConnected = false
671+
if (this.logsLoadInFlight) {
672+
void this.loadCronLogsViaHttp(true).finally(() => {
673+
this.finishCronLogsSnapshotRequest()
674+
})
675+
return
676+
}
665677
this.logsLoading = false
666678
})
667679
@@ -670,8 +682,11 @@ export default defineNuxtComponent({
670682
})
671683
},
672684
disconnectCronLogsSocket(): void {
685+
this.clearLogsSnapshotTransportWatch()
673686
if (this.logsSocket) {
674-
this.logsSocket.emit('unsubscribe')
687+
if (this.logsSocket.connected) {
688+
this.logsSocket.emit('unsubscribe')
689+
}
675690
this.logsSocket.removeAllListeners()
676691
this.logsSocket.disconnect()
677692
this.logsSocket = null
@@ -723,6 +738,7 @@ export default defineNuxtComponent({
723738
this.logsHasScrolledDown = false
724739
this.logsFollowTail = true
725740
this.clearLogsSnapshotTimeout()
741+
this.clearLogsSnapshotTransportWatch()
726742
const resolver = this.logsSnapshotResolver
727743
this.logsSnapshotResolver = null
728744
resolver?.()
@@ -1127,6 +1143,12 @@ export default defineNuxtComponent({
11271143
this.logsLoadInFlight = true
11281144
this.logsSnapshotResolver = resolve
11291145
this.logsLoading = true
1146+
this.clearLogsSnapshotTransportWatch()
1147+
this.logsSnapshotTransportCleanup = onSocketIoTransportFailure(socket, () => {
1148+
void this.loadCronLogsViaHttp(true).finally(() => {
1149+
this.finishCronLogsSnapshotRequest()
1150+
})
1151+
})
11301152
11311153
socket.emit('resync', {
11321154
taskName: this.selectedCronName,

0 commit comments

Comments
 (0)