diff --git a/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts b/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts index 9de1a2e5..6150fda5 100644 --- a/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts +++ b/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts @@ -512,13 +512,15 @@ const sanitizeArtifactName = (value: string): string => value.replace(/[^0-9A-Za const buildProfileFrontendOutDir = (workspaceRoot: string, profileName: string): string => path.join(workspaceRoot, '.release-dist', sanitizeArtifactName(profileName), 'game-frontend'); -const buildProfileFrontendCommands = ( +export const buildProfileFrontendCommands = ( workspaceRoot: string, profile: Pick, env?: Record ): BuildCommand[] => { + const profileFrontendBuildNodeOptions = env?.PROFILE_FRONTEND_BUILD_NODE_OPTIONS?.trim(); const buildEnv = { ...(env ?? {}), + ...(profileFrontendBuildNodeOptions ? { NODE_OPTIONS: profileFrontendBuildNodeOptions } : {}), VITE_APP_BASE_PATH: `/${profile.profile}`, VITE_GAME_API_URL: `/${profile.profile}/api/trpc`, VITE_GAME_SSE_URL: `/${profile.profile}/api/events`, diff --git a/app/gateway-api/test/orchestratorPlan.test.ts b/app/gateway-api/test/orchestratorPlan.test.ts index 8d31e88e..4c0af273 100644 --- a/app/gateway-api/test/orchestratorPlan.test.ts +++ b/app/gateway-api/test/orchestratorPlan.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest'; import path from 'node:path'; import { + buildProfileFrontendCommands, buildProfileMigrationCommand, buildProcessDefinitions, buildWorkspaceCommands, @@ -330,3 +331,30 @@ describe('buildWorkspaceCommands', () => { }); }); }); + +describe('buildProfileFrontendCommands', () => { + it('uses a profile frontend build-only Node heap without changing the shared runtime heap', () => { + const workspaceRoot = '/srv/sammo/worktrees/0123456789abcdef'; + const commands = buildProfileFrontendCommands(workspaceRoot, buildProfile(), { + NODE_OPTIONS: '--max-old-space-size=1536', + PROFILE_FRONTEND_BUILD_NODE_OPTIONS: '--max-old-space-size=2048', + }); + + expect(commands).toHaveLength(2); + expect(commands.every((command) => command.env?.NODE_OPTIONS === '--max-old-space-size=2048')).toBe(true); + expect( + commands.every( + (command) => command.env?.PROFILE_FRONTEND_BUILD_NODE_OPTIONS === '--max-old-space-size=2048' + ) + ).toBe(true); + }); + + it('keeps the shared Node heap when no frontend build override is configured', () => { + const workspaceRoot = '/srv/sammo/worktrees/0123456789abcdef'; + const commands = buildProfileFrontendCommands(workspaceRoot, buildProfile(), { + NODE_OPTIONS: '--max-old-space-size=1536', + }); + + expect(commands.every((command) => command.env?.NODE_OPTIONS === '--max-old-space-size=1536')).toBe(true); + }); +}); diff --git a/docs/release-operations.md b/docs/release-operations.md index 03dc8033..9e698705 100644 --- a/docs/release-operations.md +++ b/docs/release-operations.md @@ -56,9 +56,12 @@ profile 범위 권한과 별개인 전역 `admin.releases.manage` 권한이 필 `NODE_OPTIONS`와 `RAYON_NUM_THREADS`는 출력에는 영향을 주지 않는 resource 제한으로 build child에 전달됩니다. - `TURN_DAEMON_NODE_OPTIONS`가 설정되어 있으면 Gateway orchestrator는 그 값을 - turn-daemon PM2 process의 `NODE_OPTIONS`로만 덮어씁니다. API·frontend·worker와 - build는 공용 `NODE_OPTIONS`를 계속 사용합니다. 전용 heap을 늘릴 때는 runtime - container hard limit과 전체 process RSS를 먼저 확인합니다. + turn-daemon PM2 process의 `NODE_OPTIONS`로만 덮어씁니다. API·frontend·worker는 + 공용 `NODE_OPTIONS`를 계속 사용합니다. Profile의 `vue-tsc`와 Vite build만 더 큰 + heap이 필요하면 `PROFILE_FRONTEND_BUILD_NODE_OPTIONS`를 지정합니다. 이 값은 + profile frontend build child의 `NODE_OPTIONS`만 덮어쓰며 server package Turbo + build와 배포 후 PM2 process의 heap은 바꾸지 않습니다. 전용 heap을 늘릴 때는 + runtime container hard limit과 전체 process RSS를 먼저 확인합니다. - migration 이후 이전 애플리케이션으로 돌아갈 때 schema 하위 호환성이 유지됩니다.