From 2adc56de5966983ca1bf7b773657f0bfc3a3cf9a Mon Sep 17 00:00:00 2001 From: ponderingdemocritus Date: Thu, 19 Dec 2024 18:27:46 +1100 Subject: [PATCH] debounce --- client/src/dojo/debouncedQueries.ts | 114 ++++++++++++++++++ client/src/dojo/queries.ts | 10 +- .../resources/InventoryResources.tsx | 4 +- client/src/ui/layouts/World.tsx | 16 ++- 4 files changed, 131 insertions(+), 13 deletions(-) create mode 100644 client/src/dojo/debouncedQueries.ts diff --git a/client/src/dojo/debouncedQueries.ts b/client/src/dojo/debouncedQueries.ts new file mode 100644 index 000000000..83121766d --- /dev/null +++ b/client/src/dojo/debouncedQueries.ts @@ -0,0 +1,114 @@ +import { Component, Metadata, Schema } from "@dojoengine/recs"; +import { ToriiClient } from "@dojoengine/torii-client"; +import debounce from "lodash/debounce"; +import { + addMarketSubscription, + addToSubscription, + addToSubscriptionOneKeyModelbyRealmEntityId, + addToSubscriptionTwoKeyModelbyRealmEntityId, + syncPosition, +} from "./queries"; + +// Queue class to manage requests +class RequestQueue { + private queue: Array<() => Promise> = []; + private processing = false; + private batchSize = 3; // Number of concurrent requests + private batchDelayMs = 100; // Delay between batches + + async add(request: () => Promise) { + this.queue.push(request); + if (!this.processing) { + this.processing = true; + this.processQueue(); + } + } + + private async processQueue() { + while (this.queue.length > 0) { + const batch = this.queue.splice(0, this.batchSize); + + try { + await Promise.all(batch.map((request) => request())); + } catch (error) { + console.error("Error processing request batch:", error); + } + + if (this.queue.length > 0) { + // Add delay between batches to prevent overwhelming the server + await new Promise((resolve) => setTimeout(resolve, this.batchDelayMs)); + } + } + this.processing = false; + } + + clear() { + this.queue = []; + } +} + +// Create separate queues for different types of requests +const positionQueue = new RequestQueue(); +const subscriptionQueue = new RequestQueue(); +const marketQueue = new RequestQueue(); + +// Debounced functions that add to queues +export const debouncedSyncPosition = debounce( + async (client: ToriiClient, components: Component[], entityID: string) => { + await positionQueue.add(() => syncPosition(client, components, entityID)); + }, + 100, + { leading: true }, // Add leading: true to execute immediately on first call +); + +export const debouncedAddToSubscriptionTwoKey = debounce( + async ( + client: ToriiClient, + components: Component[], + entityID: string[], + ) => { + await subscriptionQueue.add(() => addToSubscriptionTwoKeyModelbyRealmEntityId(client, components, entityID)); + }, + 250, + { leading: true }, +); + +export const debouncedAddToSubscriptionOneKey = debounce( + async ( + client: ToriiClient, + components: Component[], + entityID: string[], + ) => { + await subscriptionQueue.add(() => addToSubscriptionOneKeyModelbyRealmEntityId(client, components, entityID)); + }, + 250, + { leading: true }, +); + +export const debouncedAddToSubscription = debounce( + async ( + client: ToriiClient, + components: Component[], + entityID: string[], + position?: { x: number; y: number }[], + ) => { + await subscriptionQueue.add(() => addToSubscription(client, components, entityID, position)); + }, + 250, + { leading: true }, +); + +export const debouncedAddMarketSubscription = debounce( + async (client: ToriiClient, components: Component[]) => { + await marketQueue.add(() => addMarketSubscription(client, components)); + }, + 500, + { leading: true }, +); + +// Utility function to clear all queues if needed +export const clearAllQueues = () => { + positionQueue.clear(); + subscriptionQueue.clear(); + marketQueue.clear(); +}; diff --git a/client/src/dojo/queries.ts b/client/src/dojo/queries.ts index c1620c90e..9dff82a6d 100644 --- a/client/src/dojo/queries.ts +++ b/client/src/dojo/queries.ts @@ -25,7 +25,7 @@ export const syncPosition = async ( components, [], [], - 30_000, + 5_000, ); }; @@ -53,7 +53,7 @@ export const addToSubscriptionTwoKeyModelbyRealmEntityId = async ( components as any, [], [], - 30_000, + 5_000, ); const end = performance.now(); console.log("AddToSubscriptionEnd", end - start); @@ -145,7 +145,7 @@ export const addMarketSubscription = async ( components, [], [], - 30_000, + 5_000, false, ); const end = performance.now(); diff --git a/client/src/ui/components/resources/InventoryResources.tsx b/client/src/ui/components/resources/InventoryResources.tsx index 0b4b7cd2f..235d28010 100644 --- a/client/src/ui/components/resources/InventoryResources.tsx +++ b/client/src/ui/components/resources/InventoryResources.tsx @@ -1,4 +1,4 @@ -import { addToSubscription } from "@/dojo/queries"; +import { debouncedAddToSubscription } from "@/dojo/debouncedQueries"; import { useDojo } from "@/hooks/context/DojoContext"; import { useResourceBalance, useResourcesUtils } from "@/hooks/helpers/useResources"; import { ResourceCost } from "@/ui/elements/ResourceCost"; @@ -52,7 +52,7 @@ export const InventoryResources = ({ setIsSyncing(true); try { console.log("AddToSubscriptionStart - 4"); - await addToSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, [ + await debouncedAddToSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, [ entityId.toString(), ]); localStorage.setItem(cacheKey, now.toString()); diff --git a/client/src/ui/layouts/World.tsx b/client/src/ui/layouts/World.tsx index 3a2132791..22a06988e 100644 --- a/client/src/ui/layouts/World.tsx +++ b/client/src/ui/layouts/World.tsx @@ -3,7 +3,11 @@ import { lazy, Suspense, useEffect, useMemo, useState } from "react"; import { Redirect } from "wouter"; import useUIStore from "../../hooks/store/useUIStore"; -import { addMarketSubscription, addToSubscription, addToSubscriptionOneKeyModelbyRealmEntityId } from "@/dojo/queries"; +import { + debouncedAddMarketSubscription, + debouncedAddToSubscription, + debouncedAddToSubscriptionOneKey, +} from "@/dojo/debouncedQueries"; import { useDojo } from "@/hooks/context/DojoContext"; import { PlayerStructure, useEntities } from "@/hooks/helpers/useEntities"; import { useStructureEntityId } from "@/hooks/helpers/useStructureEntityId"; @@ -143,7 +147,7 @@ export const World = ({ backgroundImage }: { backgroundImage: string }) => { console.log("AddToSubscriptionStart - 1"); try { await Promise.all([ - addToSubscription( + debouncedAddToSubscription( dojo.network.toriiClient, dojo.network.contractComponents as any, [structureEntityId.toString()], @@ -164,13 +168,13 @@ export const World = ({ backgroundImage }: { backgroundImage: string }) => { try { setWorldLoading(true); console.log("AddToSubscriptionStart - 2"); - addToSubscription( + debouncedAddToSubscription( dojo.network.toriiClient, dojo.network.contractComponents as any, [...filteredStructures.map((structure) => structure.entity_id.toString())], [...filteredStructures.map((structure) => ({ x: structure.position.x, y: structure.position.y }))], ); - addToSubscriptionOneKeyModelbyRealmEntityId(dojo.network.toriiClient, dojo.network.contractComponents as any, [ + debouncedAddToSubscriptionOneKey(dojo.network.toriiClient, dojo.network.contractComponents as any, [ ...filteredStructures.map((structure) => structure.entity_id.toString()), ]); } catch (error) { @@ -184,10 +188,10 @@ export const World = ({ backgroundImage }: { backgroundImage: string }) => { try { setMarketLoading(true); console.log("AddToSubscriptionStart - 3"); - addToSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, [ + debouncedAddToSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, [ ADMIN_BANK_ENTITY_ID.toString(), ]); - addMarketSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any); + debouncedAddMarketSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any); } catch (error) { console.error("Fetch failed", error); } finally {