Files
vexplor/frontend/scripts/performance-test.ts

459 lines
14 KiB
TypeScript
Raw Normal View History

2025-09-18 10:05:50 +09:00
/**
* 🔥
*
* .
*
* :
* npm run performance-test
*/
import { optimizedButtonDataflowService } from "../lib/services/optimizedButtonDataflowService";
import { dataflowConfigCache } from "../lib/services/dataflowCache";
import { dataflowJobQueue } from "../lib/services/dataflowJobQueue";
import { PerformanceBenchmark } from "../lib/services/__tests__/buttonDataflowPerformance.test";
import { ButtonActionType, ButtonTypeConfig } from "../types/screen";
// 🔥 성능 목표 상수
const PERFORMANCE_TARGETS = {
IMMEDIATE_RESPONSE: 200, // ms
CACHE_HIT: 10, // ms
SIMPLE_VALIDATION: 50, // ms
QUEUE_ENQUEUE: 5, // ms
CACHE_HIT_RATE: 80, // %
} as const;
/**
* 🔥
*/
async function runPerformanceTests() {
console.log("🔥 Button Dataflow Performance Verification");
console.log("==========================================\n");
const benchmark = new PerformanceBenchmark();
let totalTests = 0;
let passedTests = 0;
try {
// 1. 캐시 성능 테스트
console.log("📊 Testing Cache Performance...");
const cacheResults = await testCachePerformance(benchmark);
totalTests += cacheResults.total;
passedTests += cacheResults.passed;
// 2. 버튼 실행 성능 테스트
console.log("\n⚡ Testing Button Execution Performance...");
const buttonResults = await testButtonExecutionPerformance(benchmark);
totalTests += buttonResults.total;
passedTests += buttonResults.passed;
// 3. 큐 성능 테스트
console.log("\n🚀 Testing Job Queue Performance...");
const queueResults = await testJobQueuePerformance(benchmark);
totalTests += queueResults.total;
passedTests += queueResults.passed;
// 4. 통합 성능 테스트
console.log("\n🔧 Testing Integration Performance...");
const integrationResults = await testIntegrationPerformance(benchmark);
totalTests += integrationResults.total;
passedTests += integrationResults.passed;
// 최종 결과 출력
console.log("\n" + "=".repeat(50));
console.log("🎯 PERFORMANCE TEST SUMMARY");
console.log("=".repeat(50));
console.log(`Total Tests: ${totalTests}`);
console.log(`Passed: ${passedTests} (${((passedTests / totalTests) * 100).toFixed(1)}%)`);
console.log(`Failed: ${totalTests - passedTests}`);
// 벤치마크 리포트
benchmark.printReport();
// 성공/실패 판정
const successRate = (passedTests / totalTests) * 100;
if (successRate >= 90) {
console.log("\n🎉 PERFORMANCE VERIFICATION PASSED!");
console.log("All performance targets have been met.");
process.exit(0);
} else {
console.log("\n⚠ PERFORMANCE VERIFICATION FAILED!");
console.log("Some performance targets were not met.");
process.exit(1);
}
} catch (error) {
console.error("\n❌ Performance test failed:", error);
process.exit(1);
}
}
/**
*
*/
async function testCachePerformance(benchmark: PerformanceBenchmark) {
let total = 0;
let passed = 0;
// 캐시 초기화
dataflowConfigCache.clearAllCache();
// 1. 첫 번째 로드 성능 (서버 호출)
total++;
try {
const time = await benchmark.measure("Cache First Load", async () => {
return await dataflowConfigCache.getConfig("perf-test-1");
});
// 첫 로드는 1초 이내면 통과
if (benchmark.getResults().details.slice(-1)[0].time < 1000) {
passed++;
console.log(" ✅ First load performance: PASSED");
} else {
console.log(" ❌ First load performance: FAILED");
}
} catch (error) {
console.log(" ❌ First load test: ERROR -", error.message);
}
// 2. 캐시 히트 성능
total++;
try {
await benchmark.measure("Cache Hit Performance", async () => {
return await dataflowConfigCache.getConfig("perf-test-1");
});
const hitTime = benchmark.getResults().details.slice(-1)[0].time;
if (hitTime < PERFORMANCE_TARGETS.CACHE_HIT) {
passed++;
console.log(` ✅ Cache hit performance: PASSED (${hitTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.CACHE_HIT}ms)`);
} else {
console.log(` ❌ Cache hit performance: FAILED (${hitTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.CACHE_HIT}ms)`);
}
} catch (error) {
console.log(" ❌ Cache hit test: ERROR -", error.message);
}
// 3. 캐시 히트율 테스트
total++;
try {
// 여러 버튼에 대해 캐시 로드 및 히트 테스트
const buttonIds = Array.from({ length: 10 }, (_, i) => `perf-test-${i}`);
// 첫 번째 로드 (캐시 채우기)
await Promise.all(buttonIds.map((id) => dataflowConfigCache.getConfig(id)));
// 두 번째 로드 (캐시 히트)
await Promise.all(buttonIds.map((id) => dataflowConfigCache.getConfig(id)));
const metrics = dataflowConfigCache.getMetrics();
if (metrics.hitRate >= PERFORMANCE_TARGETS.CACHE_HIT_RATE) {
passed++;
console.log(
` ✅ Cache hit rate: PASSED (${metrics.hitRate.toFixed(1)}% >= ${PERFORMANCE_TARGETS.CACHE_HIT_RATE}%)`,
);
} else {
console.log(
` ❌ Cache hit rate: FAILED (${metrics.hitRate.toFixed(1)}% < ${PERFORMANCE_TARGETS.CACHE_HIT_RATE}%)`,
);
}
} catch (error) {
console.log(" ❌ Cache hit rate test: ERROR -", error.message);
}
return { total, passed };
}
/**
*
*/
async function testButtonExecutionPerformance(benchmark: PerformanceBenchmark) {
let total = 0;
let passed = 0;
const mockConfig: ButtonTypeConfig = {
actionType: "save" as ButtonActionType,
enableDataflowControl: true,
dataflowTiming: "after",
dataflowConfig: {
controlMode: "simple",
selectedDiagramId: 1,
selectedRelationshipId: "rel-123",
},
};
// 1. After 타이밍 성능 테스트
total++;
try {
await benchmark.measure("Button Execution (After)", async () => {
return await optimizedButtonDataflowService.executeButtonWithDataflow(
"perf-button-1",
"save",
mockConfig,
{ testData: "value" },
"DEFAULT",
);
});
const execTime = benchmark.getResults().details.slice(-1)[0].time;
if (execTime < PERFORMANCE_TARGETS.IMMEDIATE_RESPONSE) {
passed++;
console.log(
` ✅ After timing execution: PASSED (${execTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.IMMEDIATE_RESPONSE}ms)`,
);
} else {
console.log(
` ❌ After timing execution: FAILED (${execTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.IMMEDIATE_RESPONSE}ms)`,
);
}
} catch (error) {
console.log(" ❌ After timing test: ERROR -", error.message);
}
// 2. Before 타이밍 (간단한 검증) 성능 테스트
total++;
try {
const beforeConfig = {
...mockConfig,
dataflowTiming: "before" as const,
dataflowConfig: {
controlMode: "advanced" as const,
directControl: {
sourceTable: "test_table",
triggerType: "insert" as const,
conditions: [
{
id: "cond1",
type: "condition" as const,
field: "status",
operator: "=" as const,
value: "active",
},
],
actions: [],
},
},
};
await benchmark.measure("Button Execution (Before Simple)", async () => {
return await optimizedButtonDataflowService.executeButtonWithDataflow(
"perf-button-2",
"save",
beforeConfig,
{ status: "active" },
"DEFAULT",
);
});
const execTime = benchmark.getResults().details.slice(-1)[0].time;
if (execTime < PERFORMANCE_TARGETS.SIMPLE_VALIDATION) {
passed++;
console.log(
` ✅ Before simple validation: PASSED (${execTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.SIMPLE_VALIDATION}ms)`,
);
} else {
console.log(
` ❌ Before simple validation: FAILED (${execTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.SIMPLE_VALIDATION}ms)`,
);
}
} catch (error) {
console.log(" ❌ Before timing test: ERROR -", error.message);
}
// 3. 제어관리 없는 실행 성능
total++;
try {
const noDataflowConfig = {
...mockConfig,
enableDataflowControl: false,
};
await benchmark.measure("Button Execution (No Dataflow)", async () => {
return await optimizedButtonDataflowService.executeButtonWithDataflow(
"perf-button-3",
"save",
noDataflowConfig,
{ testData: "value" },
"DEFAULT",
);
});
const execTime = benchmark.getResults().details.slice(-1)[0].time;
if (execTime < 100) {
// 제어관리 없으면 더 빨라야 함
passed++;
console.log(` ✅ No dataflow execution: PASSED (${execTime.toFixed(2)}ms < 100ms)`);
} else {
console.log(` ❌ No dataflow execution: FAILED (${execTime.toFixed(2)}ms >= 100ms)`);
}
} catch (error) {
console.log(" ❌ No dataflow test: ERROR -", error.message);
}
return { total, passed };
}
/**
*
*/
async function testJobQueuePerformance(benchmark: PerformanceBenchmark) {
let total = 0;
let passed = 0;
const mockConfig: ButtonTypeConfig = {
actionType: "save" as ButtonActionType,
enableDataflowControl: true,
dataflowTiming: "after",
dataflowConfig: {
controlMode: "simple",
selectedDiagramId: 1,
selectedRelationshipId: "rel-123",
},
};
// 큐 초기화
dataflowJobQueue.clearQueue();
// 1. 단일 작업 큐잉 성능
total++;
try {
await benchmark.measure("Job Queue Enqueue (Single)", async () => {
return dataflowJobQueue.enqueue("queue-perf-1", "save", mockConfig, {}, "DEFAULT", "normal");
});
const queueTime = benchmark.getResults().details.slice(-1)[0].time;
if (queueTime < PERFORMANCE_TARGETS.QUEUE_ENQUEUE) {
passed++;
console.log(
` ✅ Single job enqueue: PASSED (${queueTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.QUEUE_ENQUEUE}ms)`,
);
} else {
console.log(
` ❌ Single job enqueue: FAILED (${queueTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.QUEUE_ENQUEUE}ms)`,
);
}
} catch (error) {
console.log(" ❌ Single enqueue test: ERROR -", error.message);
}
// 2. 대량 작업 큐잉 성능
total++;
try {
const jobCount = 50;
await benchmark.measure("Job Queue Enqueue (Batch)", async () => {
const promises = Array.from({ length: jobCount }, (_, i) =>
dataflowJobQueue.enqueue(`queue-perf-batch-${i}`, "save", mockConfig, {}, "DEFAULT", "normal"),
);
return Promise.resolve(promises);
});
const batchTime = benchmark.getResults().details.slice(-1)[0].time;
const averageTime = batchTime / jobCount;
if (averageTime < PERFORMANCE_TARGETS.QUEUE_ENQUEUE) {
passed++;
console.log(
` ✅ Batch job enqueue: PASSED (avg ${averageTime.toFixed(2)}ms < ${PERFORMANCE_TARGETS.QUEUE_ENQUEUE}ms)`,
);
} else {
console.log(
` ❌ Batch job enqueue: FAILED (avg ${averageTime.toFixed(2)}ms >= ${PERFORMANCE_TARGETS.QUEUE_ENQUEUE}ms)`,
);
}
} catch (error) {
console.log(" ❌ Batch enqueue test: ERROR -", error.message);
}
// 3. 우선순위 처리 확인
total++;
try {
// 일반 우선순위 작업들
const normalJobs = Array.from({ length: 5 }, (_, i) =>
dataflowJobQueue.enqueue(`normal-${i}`, "save", mockConfig, {}, "DEFAULT", "normal"),
);
// 높은 우선순위 작업
const highJob = dataflowJobQueue.enqueue("high-priority", "save", mockConfig, {}, "DEFAULT", "high");
const queueInfo = dataflowJobQueue.getQueueInfo();
// 높은 우선순위 작업이 맨 앞에 있는지 확인
if (queueInfo.pending[0].id === highJob && queueInfo.pending[0].priority === "high") {
passed++;
console.log(" ✅ Priority handling: PASSED");
} else {
console.log(" ❌ Priority handling: FAILED");
}
} catch (error) {
console.log(" ❌ Priority test: ERROR -", error.message);
}
return { total, passed };
}
/**
*
*/
async function testIntegrationPerformance(benchmark: PerformanceBenchmark) {
let total = 0;
let passed = 0;
// 실제 사용 시나리오 시뮬레이션
total++;
try {
const scenarios = [
{ timing: "after", count: 10, actionType: "save" },
{ timing: "before", count: 5, actionType: "delete" },
{ timing: "replace", count: 3, actionType: "submit" },
];
await benchmark.measure("Integration Load Test", async () => {
for (const scenario of scenarios) {
const promises = Array.from({ length: scenario.count }, async (_, i) => {
const config: ButtonTypeConfig = {
actionType: scenario.actionType as ButtonActionType,
enableDataflowControl: true,
dataflowTiming: scenario.timing as any,
dataflowConfig: {
controlMode: "simple",
selectedDiagramId: 1,
selectedRelationshipId: `rel-${i}`,
},
};
return await optimizedButtonDataflowService.executeButtonWithDataflow(
`integration-${scenario.timing}-${i}`,
scenario.actionType as ButtonActionType,
config,
{ testData: `value-${i}` },
"DEFAULT",
);
});
await Promise.all(promises);
}
});
const totalTime = benchmark.getResults().details.slice(-1)[0].time;
const totalRequests = scenarios.reduce((sum, s) => sum + s.count, 0);
const averageTime = totalTime / totalRequests;
// 통합 테스트에서는 평균 300ms 이내면 통과
if (averageTime < 300) {
passed++;
console.log(` ✅ Integration load test: PASSED (avg ${averageTime.toFixed(2)}ms < 300ms)`);
} else {
console.log(` ❌ Integration load test: FAILED (avg ${averageTime.toFixed(2)}ms >= 300ms)`);
}
} catch (error) {
console.log(" ❌ Integration test: ERROR -", error.message);
}
return { total, passed };
}
// 스크립트가 직접 실행될 때만 테스트 실행
if (require.main === module) {
runPerformanceTests();
}
export { runPerformanceTests };