perf: further reduce memory (#7015)

* feat: server source maps

* perf: strip unused fields from search results

* perf: reduce game version memory usage
This commit is contained in:
Michael H.
2026-08-06 15:39:06 +00:00
committed by GitHub
parent c671ca52f6
commit b8d97268cb
5 changed files with 67 additions and 24 deletions
+30 -5
View File
@@ -68,11 +68,16 @@ export interface GeneratedState extends GloballyUsedState {
} }
/** /**
* Composable for accessing the globally used generated state. * Built once per module load rather than via `useState`, because every `useState` value is
* This includes both fetched data and runtime-defined constants. * serialized into the SSR payload of every page. This is build-time constant data that the
* client already has via the static import above, so putting it in the payload would ship a
* second copy for no benefit.
*
* Consequence: on the server this object is shared by every request in the isolate, so it must
* stay immutable there. Runtime refreshes are client-only and go through `setGameVersions`.
*/ */
export const useGeneratedState = () => const generatedState = shallowRef<GeneratedState>(
useState<GeneratedState>('generatedState', () => ({ Object.freeze({
// Cast JSON data to typed API responses // Cast JSON data to typed API responses
categories: (categories ?? []) as Labrinth.Tags.v2.Category[], categories: (categories ?? []) as Labrinth.Tags.v2.Category[],
loaders: (loaders ?? []) as Labrinth.Tags.v2.Loader[], loaders: (loaders ?? []) as Labrinth.Tags.v2.Loader[],
@@ -147,4 +152,24 @@ export const useGeneratedState = () =>
errors, errors,
buildYear: new Date().getFullYear(), buildYear: new Date().getFullYear(),
})) }) as GeneratedState,
)
/**
* Composable for accessing the globally used generated state.
* This includes both fetched data and runtime-defined constants.
*/
export const useGeneratedState = () => generatedState
/**
* Replaces the build-time game versions with a freshly fetched list. Client-only: mutating this
* on the server would leak across every request sharing the isolate.
*/
export function setGameVersions(versions: Labrinth.Tags.v2.GameVersion[]) {
if (import.meta.server) return
generatedState.value = Object.freeze({
...generatedState.value,
gameVersions: versions,
}) as GeneratedState
}
@@ -228,6 +228,19 @@ function parseSearchParams(requestParams: string): Labrinth.Search.SearchParams
} }
} }
// Search returns expanded dependency data that no card renders and that isn't part of
// ResultSearchProject. On modpacks it is ~90% of the response, and everything cached here
// is serialized into the SSR payload, so drop it before it reaches the query cache.
function stripUnrenderedFields(
hits: Labrinth.Search.v3.ResultSearchProject[],
): Labrinth.Search.v3.ResultSearchProject[] {
return hits.map((hit) => {
const { dependencies, dependency_project_ids, compatible_dependency_project_ids, ...rendered } =
hit as Labrinth.Search.v3.ResultSearchProject & Record<string, unknown>
return rendered as Labrinth.Search.v3.ResultSearchProject
})
}
async function fetchSearch(requestParams: string) { async function fetchSearch(requestParams: string) {
debug('search() called', { debug('search() called', {
requestParams: requestParams.substring(0, 100), requestParams: requestParams.substring(0, 100),
@@ -241,17 +254,19 @@ async function fetchSearch(requestParams: string) {
debug('search() response', { total_hits: raw.total_hits, hitCount: raw.hits?.length }) debug('search() response', { total_hits: raw.total_hits, hitCount: raw.hits?.length })
const hits = stripUnrenderedFields(raw.hits ?? [])
if (isServerType.value) { if (isServerType.value) {
return { return {
projectHits: [], projectHits: [],
serverHits: raw.hits, serverHits: hits,
total_hits: raw.total_hits, total_hits: raw.total_hits,
per_page: raw.hits_per_page, per_page: raw.hits_per_page,
} }
} }
return { return {
projectHits: raw.hits, projectHits: hits,
serverHits: [], serverHits: [],
total_hits: raw.total_hits, total_hits: raw.total_hits,
per_page: raw.hits_per_page, per_page: raw.hits_per_page,
@@ -0,0 +1,19 @@
import type { Labrinth } from '@modrinth/api-client'
import { setGameVersions } from '~/composables/generated'
export default defineNuxtPlugin((nuxtApp) => {
// Deferred until after hydration: the server renders with the build-time game versions, so
// swapping them in during setup would make the first client render disagree with the markup.
nuxtApp.hook('app:suspense:resolve', async () => {
try {
const gameVersions = await $fetch<Labrinth.Tags.v2.GameVersion[]>('/api/tags/game-versions')
if (gameVersions && gameVersions.length > 0) {
setGameVersions(gameVersions)
}
} catch (error) {
console.error('[Game Version Updater] Failed to fetch:', error)
}
})
})
@@ -1,17 +0,0 @@
import type { Labrinth } from '@modrinth/api-client'
export default defineNuxtPlugin(async () => {
try {
const gameVersions = await $fetch<Labrinth.Tags.v2.GameVersion[]>('/api/tags/game-versions')
if (gameVersions && gameVersions.length > 0) {
const state = useState<{ gameVersions: Labrinth.Tags.v2.GameVersion[] }>('generatedState')
if (state.value) {
state.value.gameVersions = gameVersions
}
}
} catch (error) {
console.error('[Game Version Updater] Failed to fetch:', error)
}
})
+1
View File
@@ -10,6 +10,7 @@
"routes": ["modrinth.com/*"], "routes": ["modrinth.com/*"],
"preview_urls": true, "preview_urls": true,
"workers_dev": true, "workers_dev": true,
"upload_source_maps": true,
"limits": { "limits": {
"cpu_ms": 5000 "cpu_ms": 5000
}, },