Skip to content

Commit 5c22611

Browse files
committed
feat(core): auto skipping scheduled restarts after server close
This will auto skip any scheduled restart for the next two hours. Also migrated FxScheduler to typescript.
1 parent f57046f commit 5c22611

2 files changed

Lines changed: 77 additions & 32 deletions

File tree

core/modules/FxRunner/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ export default class FxRunner {
323323
this.proc = null;
324324

325325
//Cleanup
326+
txCore.fxScheduler.handleServerClose();
326327
txCore.fxResources.handleServerClose();
327328
txCore.fxPlayerlist.handleServerClose(debugInfo.mutex);
328329
txCore.metrics.svRuntime.logServerClose(reasonString);
Lines changed: 76 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,32 @@ const modulename = 'FxScheduler';
22
import { parseSchedule } from '@lib/misc';
33
import consoleFactory from '@lib/console';
44
import { SYM_SYSTEM_AUTHOR } from '@lib/symbols';
5+
import type { UpdateConfigKeySet } from './ConfigStore/utils';
56
const console = consoleFactory(modulename);
67

78

9+
//Types
10+
type RestartInfo = {
11+
string: string;
12+
minuteFloorTs: number;
13+
}
14+
type ParsedTime = {
15+
string: string;
16+
hours: number;
17+
minutes: number;
18+
}
19+
20+
821
//Consts
922
const scheduleWarnings = [30, 15, 10, 5, 4, 3, 2, 1];
1023

24+
1125
/**
1226
* Processes an array of HH:MM, gets the next timestamp (sorted by closest).
13-
* When time matches, it will be dist: 0, distMins: 0, and nextTs likely in the past due to seconds and milliseconds being 0.
14-
* @param {Array} schedule
15-
* @returns {Object} {string, minuteFloorTs}
27+
* When time matches, it will be dist: 0, distMins: 0, and nextTs likely in the
28+
* past due to seconds and milliseconds being 0.
1629
*/
17-
const getNextScheduled = (parsedSchedule) => {
30+
const getNextScheduled = (parsedSchedule: ParsedTime[]): RestartInfo => {
1831
const thisMinuteTs = new Date().setSeconds(0, 0);
1932
const processed = parsedSchedule.map((t) => {
2033
const nextDate = new Date();
@@ -37,11 +50,15 @@ const getNextScheduled = (parsedSchedule) => {
3750
*/
3851
export default class FxScheduler {
3952
static configKeysWatched = ['restarter.schedule']; //FIXME: add readonly prop when moving to typescript
53+
private nextTempSchedule: RestartInfo | false = false;
54+
private calculatedNextRestartMinuteFloorTs: number | false = false;
55+
private nextSkip: number | false = false;
4056

4157
constructor() {
42-
this.nextSkip = false;
43-
this.nextTempSchedule = false;
44-
this.calculatedNextRestartMinuteFloorTs = false;
58+
//Initial check to update status
59+
setImmediate(() => {
60+
this.checkSchedule();
61+
});
4562

4663
//Cron Function
4764
setInterval(() => {
@@ -54,13 +71,36 @@ export default class FxScheduler {
5471
/**
5572
* Refresh configs, resets skip and temp scheduled, runs checkSchedule.
5673
*/
57-
handleConfigUpdate(updatedConfigs) {
74+
handleConfigUpdate(updatedConfigs: UpdateConfigKeySet) {
5875
this.nextSkip = false;
5976
this.nextTempSchedule = false;
6077
this.checkSchedule();
6178
txCore.webServer.webSocket.pushRefresh('status');
6279
}
6380

81+
82+
/**
83+
* Updates state when server closes.
84+
* Clear temp skips and skips next scheduled if it's in less than 2 hours.
85+
*/
86+
handleServerClose() {
87+
//Clear temp schedule, recalculates next restart
88+
if (this.nextTempSchedule) this.nextTempSchedule = false;
89+
this.checkSchedule(true);
90+
91+
//Check if next scheduled restart is in less than 2 hours
92+
const inTwoHours = Date.now() + 2 * 60 * 60 * 1000;
93+
if (this.calculatedNextRestartMinuteFloorTs && this.calculatedNextRestartMinuteFloorTs < inTwoHours) {
94+
console.warn('Server closed, skipping next scheduled restart because it\'s in less than 2 hours.');
95+
this.nextSkip = this.calculatedNextRestartMinuteFloorTs;
96+
}
97+
this.checkSchedule(true);
98+
99+
//Push UI update
100+
txCore.webServer.webSocket.pushRefresh('status');
101+
}
102+
103+
64104
/**
65105
* Returns the current status of scheduler
66106
* NOTE: sending relative because server might have clock skew
@@ -87,10 +127,8 @@ export default class FxScheduler {
87127
* Sets this.nextSkip.
88128
* Cancel scheduled button -> setNextSkip(true)
89129
* Enable scheduled button -> setNextSkip(false)
90-
* @param {boolean} enabled
91-
* @param {string} author
92130
*/
93-
setNextSkip(enabled, author) {
131+
setNextSkip(enabled: boolean, author?: string) {
94132
if (enabled) {
95133
let prevMinuteFloorTs, temporary;
96134
if (this.nextTempSchedule) {
@@ -103,18 +141,20 @@ export default class FxScheduler {
103141
this.nextSkip = this.calculatedNextRestartMinuteFloorTs;
104142
}
105143

106-
//Dispatch `txAdmin:events:scheduledRestartSkipped`
107-
txCore.fxRunner.sendEvent('scheduledRestartSkipped', {
108-
secondsRemaining: Math.floor((prevMinuteFloorTs - Date.now()) / 1000),
109-
temporary,
110-
author,
111-
});
112-
113-
//FIXME: deprecate
114-
txCore.fxRunner.sendEvent('skippedNextScheduledRestart', {
115-
secondsRemaining: Math.floor((prevMinuteFloorTs - Date.now()) / 1000),
116-
temporary
117-
});
144+
if (prevMinuteFloorTs) {
145+
//Dispatch `txAdmin:events:scheduledRestartSkipped`
146+
txCore.fxRunner.sendEvent('scheduledRestartSkipped', {
147+
secondsRemaining: Math.floor((prevMinuteFloorTs - Date.now()) / 1000),
148+
temporary,
149+
author,
150+
});
151+
152+
//FIXME: deprecate
153+
txCore.fxRunner.sendEvent('skippedNextScheduledRestart', {
154+
secondsRemaining: Math.floor((prevMinuteFloorTs - Date.now()) / 1000),
155+
temporary
156+
});
157+
}
118158
} else {
119159
this.nextSkip = false;
120160
}
@@ -130,9 +170,8 @@ export default class FxScheduler {
130170
/**
131171
* Sets this.nextTempSchedule.
132172
* The value MUST be before the next setting scheduled time.
133-
* @param {String} timeString
134173
*/
135-
setNextTempSchedule(timeString) {
174+
setNextTempSchedule(timeString: string) {
136175
//Process input
137176
if (typeof timeString !== 'string') throw new Error('expected string');
138177
const thisMinuteTs = new Date().setSeconds(0, 0);
@@ -188,10 +227,9 @@ export default class FxScheduler {
188227
/**
189228
* Checks the schedule to see if it's time to announce or restart the server
190229
*/
191-
async checkSchedule() {
192-
//FIXME: if fxchild === null || span less than 1 minute, return
230+
async checkSchedule(calculateOnly = false) {
193231
//Check settings and temp scheduled restart
194-
let nextRestart;
232+
let nextRestart: RestartInfo;
195233
if (this.nextTempSchedule) {
196234
nextRestart = this.nextTempSchedule;
197235
} else if (Array.isArray(txConfig.restarter.schedule) && txConfig.restarter.schedule.length) {
@@ -203,6 +241,7 @@ export default class FxScheduler {
203241
return;
204242
}
205243
this.calculatedNextRestartMinuteFloorTs = nextRestart.minuteFloorTs;
244+
if (calculateOnly) return;
206245

207246
//Checking if skipped
208247
if (this.nextSkip === this.calculatedNextRestartMinuteFloorTs) {
@@ -223,6 +262,13 @@ export default class FxScheduler {
223262
txCore.translator.t('restarter.schedule_reason', { time: nextRestart.string }),
224263
);
225264

265+
//Check if server is in boot cooldown
266+
const processUptime = Math.floor((txCore.fxRunner.child?.uptime ?? 0) / 1000);
267+
if (processUptime < txConfig.restarter.bootCooldown) {
268+
console.verbose.log(`Server is in boot cooldown, skipping scheduled restart.`);
269+
return;
270+
}
271+
226272
//reset next scheduled
227273
this.nextTempSchedule = false;
228274

@@ -252,12 +298,10 @@ export default class FxScheduler {
252298

253299
/**
254300
* Triggers FXServer restart and logs the reason.
255-
* @param {string} reasonInternal
256-
* @param {string} reasonTranslated
257301
*/
258-
async triggerServerRestart(reasonInternal, reasonTranslated) {
302+
async triggerServerRestart(reasonInternal: string, reasonTranslated: string) {
259303
//Sanity check
260-
if (txCore.fxRunner.isIdle) {
304+
if (txCore.fxRunner.isIdle || !txCore.fxRunner.child?.isAlive) {
261305
console.verbose.warn('Server not running, skipping scheduled restart.');
262306
return false;
263307
}

0 commit comments

Comments
 (0)