@@ -49,6 +49,11 @@ import {
4949 reduceDesktopUpdateStateOnUpdateAvailable ,
5050} from "./updateMachine" ;
5151import { isArm64HostRunningIntelBuild , resolveDesktopRuntimeInfo } from "./runtimeArch" ;
52+ import {
53+ decideBackendRestart ,
54+ INITIAL_BACKEND_RESTART_STATE ,
55+ noteBackendLaunch ,
56+ } from "./backendRestartPolicy" ;
5257
5358syncShellEnvironment ( ) ;
5459
@@ -89,14 +94,14 @@ let backendProcess: ChildProcess.ChildProcess | null = null;
8994let backendPort = 0 ;
9095let backendAuthToken = "" ;
9196let backendWsUrl = "" ;
92- let restartAttempt = 0 ;
9397let restartTimer : ReturnType < typeof setTimeout > | null = null ;
9498let isQuitting = false ;
9599let desktopProtocolRegistered = false ;
96100let aboutCommitHashCache : string | null | undefined ;
97101let desktopLogSink : RotatingFileSink | null = null ;
98102let backendLogSink : RotatingFileSink | null = null ;
99103let restoreStdIoCapture : ( ( ) => void ) | null = null ;
104+ let backendRestartState = INITIAL_BACKEND_RESTART_STATE ;
100105
101106let destructiveMenuIconCache : Electron . NativeImage | null | undefined ;
102107let macDeveloperIdSigned : boolean | null = null ;
@@ -1041,8 +1046,19 @@ function backendEnv(): NodeJS.ProcessEnv {
10411046function scheduleBackendRestart ( reason : string ) : void {
10421047 if ( isQuitting || restartTimer ) return ;
10431048
1044- const delayMs = Math . min ( 500 * 2 ** restartAttempt , 10_000 ) ;
1045- restartAttempt += 1 ;
1049+ const decision = decideBackendRestart ( backendRestartState , Date . now ( ) ) ;
1050+ backendRestartState = decision . nextState ;
1051+ if ( decision . type === "fatal" ) {
1052+ handleFatalStartupError (
1053+ "backend" ,
1054+ new Error (
1055+ `The background server crashed ${ decision . nextState . consecutiveFailures } times in a row within ${ decision . uptimeMs } ms. Check ${ Path . join ( LOG_DIR , "server-child.log" ) } for details.` ,
1056+ ) ,
1057+ ) ;
1058+ return ;
1059+ }
1060+
1061+ const delayMs = decision . delayMs ;
10461062 console . error ( `[desktop] backend exited unexpectedly (${ reason } ); restarting in ${ delayMs } ms` ) ;
10471063
10481064 restartTimer = setTimeout ( ( ) => {
@@ -1061,6 +1077,7 @@ function startBackend(): void {
10611077 }
10621078
10631079 const captureBackendLogs = app . isPackaged && backendLogSink !== null ;
1080+ backendRestartState = noteBackendLaunch ( backendRestartState , Date . now ( ) ) ;
10641081 const child = ChildProcess . spawn ( process . execPath , [ backendEntry ] , {
10651082 cwd : resolveBackendCwd ( ) ,
10661083 // In Electron main, process.execPath points to the Electron binary.
@@ -1084,10 +1101,6 @@ function startBackend(): void {
10841101 ) ;
10851102 captureBackendOutput ( child ) ;
10861103
1087- child . once ( "spawn" , ( ) => {
1088- restartAttempt = 0 ;
1089- } ) ;
1090-
10911104 child . on ( "error" , ( error ) => {
10921105 if ( backendProcess === child ) {
10931106 backendProcess = null ;
@@ -1115,6 +1128,8 @@ function stopBackend(): void {
11151128 restartTimer = null ;
11161129 }
11171130
1131+ backendRestartState = INITIAL_BACKEND_RESTART_STATE ;
1132+
11181133 const child = backendProcess ;
11191134 backendProcess = null ;
11201135 if ( ! child ) return ;
@@ -1135,6 +1150,8 @@ async function stopBackendAndWaitForExit(timeoutMs = 5_000): Promise<void> {
11351150 restartTimer = null ;
11361151 }
11371152
1153+ backendRestartState = INITIAL_BACKEND_RESTART_STATE ;
1154+
11381155 const child = backendProcess ;
11391156 backendProcess = null ;
11401157 if ( ! child ) return ;
0 commit comments