From 50c48746dfa751bfa4acf22cf2df499e54f5afcd Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Mon, 3 Mar 2025 11:00:55 +0100 Subject: [PATCH] mission map management with defered delete --- src/helpers/missionDocHelper.ts | 1 + src/index.ts | 2 ++ src/storage/missionMap.ts | 58 ++++++++++++++++++++++++++++++++- src/websocket/index.ts | 11 ++++--- 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/helpers/missionDocHelper.ts b/src/helpers/missionDocHelper.ts index 851823b..f93c772 100644 --- a/src/helpers/missionDocHelper.ts +++ b/src/helpers/missionDocHelper.ts @@ -24,6 +24,7 @@ export default abstract class MissionDocHelper { awareness.setLocalState(null); MissionMap.write(missionId, { missionId, doc, awareness, timestamp: 0 }); + console.log(`created local doc ${missionId}`); } public static async saveDoc(missionId: string) { diff --git a/src/index.ts b/src/index.ts index fb2c50d..45edc90 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,8 +41,10 @@ httpServer.listen(process.env.NODE_ENV ? SERVER_PORT : 5000, () => { import schedule from "node-schedule"; import RefreshCommandHandler from "./command/refreshCommandHandler"; +import { MissionMap } from "./storage/missionMap"; const job = schedule.scheduleJob("0 0 * * *", async () => { console.log(`${new Date().toISOString()}: running Cron`); await RefreshCommandHandler.deleteExpired(); await BackupHelper.createBackupOnInterval(); + MissionMap.deleteExpired(); }); diff --git a/src/storage/missionMap.ts b/src/storage/missionMap.ts index 28c336d..a84bf35 100644 --- a/src/storage/missionMap.ts +++ b/src/storage/missionMap.ts @@ -1,5 +1,6 @@ import * as Y from "yjs"; import * as AwarenessProtocol from "y-protocols/dist/awareness.cjs"; +import ms from "ms"; export interface missionStoreModel { missionId: string; @@ -13,38 +14,93 @@ export interface missionStoreModel { */ export abstract class MissionMap { private static store = new Map(); + private static timer = new Map(); + private static usage = new Map(); public static write(identifier: string, data: missionStoreModel, overwrite: boolean = false): void { - if (!this.exists(identifier) || overwrite) this.store.set(identifier, data); + this.monitorAccess(identifier); + + if (!this.exists(identifier) || overwrite) { + this.store.set(identifier, data); + } } public static updateState(identifier: string, data: Uint8Array): void { + this.monitorAccess(identifier); + let mission = this.read(identifier); Y.applyUpdate(mission.doc, data); this.write(identifier, mission, true); } public static updateAwareness(identifier: string, data: Uint8Array, socketId: string): void { + this.monitorAccess(identifier); + let mission = this.read(identifier); AwarenessProtocol.applyAwarenessUpdate(mission.awareness, data, socketId); this.write(identifier, mission, true); } public static updateTimestamp(identifier: string, data: number): void { + this.monitorAccess(identifier); + let mission = this.read(identifier); mission.timestamp = data; this.write(identifier, mission, true); } public static read(identifier: string): missionStoreModel { + this.monitorAccess(identifier); + return this.store.get(identifier); } public static exists(identifier: string): boolean { + this.monitorAccess(identifier); + return this.store.has(identifier); } public static delete(identifier: string): void { + this.abortDelete(identifier); + + const copyIdentifier = identifier; + const timer = setTimeout(() => { + this.finalDelete(copyIdentifier); + }, ms("10s")); + + this.timer.set(identifier, timer); + } + + public static deleteExpired(): void { + this.usage.forEach((val, key) => { + if (Date.now() - val > ms("10s")) { + this.finalDelete(key); + } + }); + } + + private static monitorAccess(identifier: string): void { + this.usage.set(identifier, Date.now()); + this.abortDelete(identifier); + } + + private static abortDelete(identifier: string): void { + const timer = this.timer.get(identifier); + if (timer) { + clearTimeout(timer); + this.timer.delete(identifier); + } + } + + private static finalDelete(identifier: string): void { + const time = this.usage.get(identifier); + if (Date.now() - time < ms("10s")) return; + this.store.delete(identifier); + this.timer.delete(identifier); + this.usage.delete(identifier); + + console.log(`cleared local doc ${identifier}`); } } diff --git a/src/websocket/index.ts b/src/websocket/index.ts index bc1c6e7..0b2650f 100644 --- a/src/websocket/index.ts +++ b/src/websocket/index.ts @@ -18,10 +18,12 @@ export default abstract class SocketServer { credentials: true, }, }); - instrument(this.io, { - auth: false, - mode: "development", - }); + if (process.env.NODE_ENV) { + instrument(this.io, { + auth: false, + mode: "development", + }); + } this.io.engine.use(helmet()); @@ -29,6 +31,7 @@ export default abstract class SocketServer { .of("/") .use(authenticateSocket) .on("connection", (socket) => { + console.log("socket connection: ", socket.id); socket.use((packet, next) => authenticateSocket(socket, next)); socket.use((packet, next) => perRequestCheck(socket, packet, next));