mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-22 01:35:31 -05:00
FS: Fix hotpatching and add more throttling (#7878)
- `writeUpdate` state is now stored in a global variable, so hotpatching doesn't crash it - throttling now writes on the tail (so two throttled `writeUpdate` calls will write one update, not two) - room settings, punishments, and helptickets are now throttled
This commit is contained in:
parent
d5e7f3f778
commit
ca94dea20f
35
lib/fs.ts
35
lib/fs.ts
|
|
@ -35,7 +35,13 @@ interface PendingUpdate {
|
|||
throttleTimer: NodeJS.Timer | null;
|
||||
}
|
||||
|
||||
const pendingUpdates = new Map<string, PendingUpdate>();
|
||||
declare const __fsState: {pendingUpdates: Map<string, PendingUpdate>};
|
||||
declare const global: {__fsState: typeof __fsState};
|
||||
if (!global.__fsState) {
|
||||
global.__fsState = {
|
||||
pendingUpdates: new Map(),
|
||||
};
|
||||
}
|
||||
|
||||
export class FSPath {
|
||||
path: string;
|
||||
|
|
@ -152,9 +158,8 @@ export class FSPath {
|
|||
*/
|
||||
writeUpdate(dataFetcher: () => string | Buffer, options: AnyObject = {}) {
|
||||
if (Config.nofswriting) return;
|
||||
const pendingUpdate: PendingUpdate | undefined = pendingUpdates.get(this.path);
|
||||
const pendingUpdate: PendingUpdate | undefined = __fsState.pendingUpdates.get(this.path);
|
||||
|
||||
// @ts-ignore
|
||||
const throttleTime = options.throttle ? Date.now() + options.throttle : 0;
|
||||
|
||||
if (pendingUpdate) {
|
||||
|
|
@ -168,11 +173,22 @@ export class FSPath {
|
|||
return;
|
||||
}
|
||||
|
||||
this.writeUpdateNow(dataFetcher, options);
|
||||
if (!throttleTime) {
|
||||
this.writeUpdateNow(dataFetcher, options);
|
||||
return;
|
||||
}
|
||||
|
||||
const update: PendingUpdate = {
|
||||
isWriting: false,
|
||||
pendingDataFetcher: dataFetcher,
|
||||
pendingOptions: options,
|
||||
throttleTime,
|
||||
throttleTimer: setTimeout(() => this.checkNextUpdate(), throttleTime - Date.now()),
|
||||
};
|
||||
__fsState.pendingUpdates.set(this.path, update);
|
||||
}
|
||||
|
||||
writeUpdateNow(dataFetcher: () => string | Buffer, options: AnyObject) {
|
||||
// @ts-ignore
|
||||
const throttleTime = options.throttle ? Date.now() + options.throttle : 0;
|
||||
const update = {
|
||||
isWriting: true,
|
||||
|
|
@ -181,25 +197,25 @@ export class FSPath {
|
|||
throttleTime,
|
||||
throttleTimer: null,
|
||||
};
|
||||
pendingUpdates.set(this.path, update);
|
||||
__fsState.pendingUpdates.set(this.path, update);
|
||||
void this.safeWrite(dataFetcher(), options).then(() => this.finishUpdate());
|
||||
}
|
||||
checkNextUpdate() {
|
||||
const pendingUpdate = pendingUpdates.get(this.path);
|
||||
const pendingUpdate = __fsState.pendingUpdates.get(this.path);
|
||||
if (!pendingUpdate) throw new Error(`FS: Pending update not found`);
|
||||
if (pendingUpdate.isWriting) throw new Error(`FS: Conflicting update`);
|
||||
|
||||
const {pendingDataFetcher: dataFetcher, pendingOptions: options} = pendingUpdate;
|
||||
if (!dataFetcher || !options) {
|
||||
// no pending update
|
||||
pendingUpdates.delete(this.path);
|
||||
__fsState.pendingUpdates.delete(this.path);
|
||||
return;
|
||||
}
|
||||
|
||||
this.writeUpdateNow(dataFetcher, options);
|
||||
}
|
||||
finishUpdate() {
|
||||
const pendingUpdate = pendingUpdates.get(this.path);
|
||||
const pendingUpdate = __fsState.pendingUpdates.get(this.path);
|
||||
if (!pendingUpdate) throw new Error(`FS: Pending update not found`);
|
||||
if (!pendingUpdate.isWriting) throw new Error(`FS: Conflicting update`);
|
||||
|
||||
|
|
@ -491,4 +507,3 @@ function getFs(path: string) {
|
|||
export const FS = Object.assign(getFs, {
|
||||
FileReadStream,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ try {
|
|||
|
||||
export function writeTickets() {
|
||||
FS(TICKET_FILE).writeUpdate(
|
||||
() => JSON.stringify(tickets)
|
||||
() => JSON.stringify(tickets), {throttle: 5000}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ export const Punishments = new class {
|
|||
buf += Punishments.renderEntry(entry, id);
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
}, {throttle: 5000});
|
||||
}
|
||||
|
||||
saveRoomPunishments() {
|
||||
|
|
@ -302,7 +302,7 @@ export const Punishments = new class {
|
|||
buf += Punishments.renderEntry(entry, id);
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
}, {throttle: 5000});
|
||||
}
|
||||
|
||||
getEntry(entryId: string) {
|
||||
|
|
|
|||
|
|
@ -1143,7 +1143,7 @@ export class GlobalRoomState {
|
|||
JSON.stringify(this.settingsList)
|
||||
.replace(/\{"title":/g, '\n{"title":')
|
||||
.replace(/\]$/, '\n]')
|
||||
));
|
||||
), {throttle: 5000});
|
||||
}
|
||||
|
||||
writeNumRooms() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user