From a2cbcf05191f98c501c6cf4c8a6636f8222115dc Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 16 Aug 2025 12:21:41 +0100 Subject: [PATCH] SLow down requests for development --- .../src/debugging/flaky-websocket-factory.ts | 71 +++++++++++++++++++ .../src/debugging/slow-fetch-factory.ts | 14 ++++ frontend/obsidian-plugin/src/utils/sleep.ts | 3 + .../obsidian-plugin/src/vault-link-plugin.ts | 11 +++ 4 files changed, 99 insertions(+) create mode 100644 frontend/obsidian-plugin/src/debugging/flaky-websocket-factory.ts create mode 100644 frontend/obsidian-plugin/src/debugging/slow-fetch-factory.ts create mode 100644 frontend/obsidian-plugin/src/utils/sleep.ts diff --git a/frontend/obsidian-plugin/src/debugging/flaky-websocket-factory.ts b/frontend/obsidian-plugin/src/debugging/flaky-websocket-factory.ts new file mode 100644 index 00000000..f2f3db0a --- /dev/null +++ b/frontend/obsidian-plugin/src/debugging/flaky-websocket-factory.ts @@ -0,0 +1,71 @@ +import { helpers, Logger } from "sync-client"; + +export function flakyWebSocketFactory( + jitterScaleInSeconds: number, + logger: Logger +): typeof WebSocket { + // eslint-disable-next-line + return class FlakyWebSocket extends WebSocket { + private static readonly RECEIVE_KEY = "websocket-receive"; + private static readonly SEND_KEY = "websocket-send"; + + private readonly locks = new helpers.Locks(logger); + + public set onopen(callback: (event: Event) => void) { + super.onopen = async (event: Event): Promise => { + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + + callback(event); + }; + } + + public set onmessage(callback: (event: MessageEvent) => void) { + super.onmessage = async (event: MessageEvent): Promise => { + await this.locks.waitForLock(FlakyWebSocket.RECEIVE_KEY); + + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + + callback(event); + + this.locks.unlock(FlakyWebSocket.RECEIVE_KEY); + }; + } + + public set onclose(callback: (event: CloseEvent) => void) { + super.onclose = async (event: CloseEvent): Promise => { + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + callback(event); + }; + } + + public set onerror(callback: (event: Event) => void) { + super.onerror = async (event: Event): Promise => { + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + callback(event); + }; + } + + public async send( + data: string | ArrayBufferLike | Blob | ArrayBufferView + ): Promise { + // maintain message order + await this.locks.waitForLock(FlakyWebSocket.SEND_KEY); + + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + + super.send(data); + + this.locks.unlock(FlakyWebSocket.SEND_KEY); + } + } as unknown as typeof WebSocket; +} diff --git a/frontend/obsidian-plugin/src/debugging/slow-fetch-factory.ts b/frontend/obsidian-plugin/src/debugging/slow-fetch-factory.ts new file mode 100644 index 00000000..5fe6c3ef --- /dev/null +++ b/frontend/obsidian-plugin/src/debugging/slow-fetch-factory.ts @@ -0,0 +1,14 @@ +export const slowFetchFactory = + (jitterScaleInSeconds: number) => + async ( + input: string | URL | globalThis.Request, + init?: RequestInit + ): Promise => { + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + + const response = await fetch(input, init); + + return response; + }; diff --git a/frontend/obsidian-plugin/src/utils/sleep.ts b/frontend/obsidian-plugin/src/utils/sleep.ts new file mode 100644 index 00000000..638fc019 --- /dev/null +++ b/frontend/obsidian-plugin/src/utils/sleep.ts @@ -0,0 +1,3 @@ +export async function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/frontend/obsidian-plugin/src/vault-link-plugin.ts b/frontend/obsidian-plugin/src/vault-link-plugin.ts index 6e8e4cf7..0f3e5fb8 100644 --- a/frontend/obsidian-plugin/src/vault-link-plugin.ts +++ b/frontend/obsidian-plugin/src/vault-link-plugin.ts @@ -22,6 +22,8 @@ import { setCursors } from "./views/cursors/remote-cursors-plugin"; import { LocalCursorUpdateListener } from "./views/cursors/local-cursor-update-listener"; +import { slowFetchFactory } from "./debugging/slow-fetch-factory"; +import { flakyWebSocketFactory } from "./debugging/flaky-websocket-factory"; const MIN_WAIT_BETWEEN_UPDATES_IN_MS = 250; export default class VaultLinkPlugin extends Plugin { @@ -41,6 +43,15 @@ export default class VaultLinkPlugin extends Plugin { ".trash/**" ); + const isDebugBuild = process.env.NODE_ENV === "development"; + + const debugOptions = isDebugBuild + ? { + fetch: slowFetchFactory(1), + webSocket: flakyWebSocketFactory(1, new Logger()) + } + : {}; + this.client = await SyncClient.create({ fs: new ObsidianFileSystemOperations( this.app.vault,