459 lines
14 KiB
TypeScript
459 lines
14 KiB
TypeScript
/**
|
||
* 🔥 버튼 제어관리 성능 검증 스크립트
|
||
*
|
||
* 실제 환경에서 성능 목표 달성 여부를 확인합니다.
|
||
*
|
||
* 사용법:
|
||
* 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 };
|