generated from pricelees/issue-pr-template
<!-- 제목 양식 --> <!-- [이슈번호] 작업 요약 (예시: [#10] Gitea 템플릿 생성) --> ## 📝 관련 이슈 및 PR **PR과 관련된 이슈 번호** - #61 ## ✨ 작업 내용 <!-- 어떤 작업을 했는지 알려주세요! --> - 이력 저장을 비동기 + Batch Insert로 구현하여 기존의 '로그인 완료 - 이력 저장(동기)' 로직, 특히 이력 저장을 별도의 트랜잭션으로 진행하며 발생하던 커넥션 고갈 문제를 해결 - 이벤트를 수신하면 In-Memory Queue에 저장하게 되어, OOM 발생 가능성이 있다고 판단. => 100개가 넘어가는 순간 바로 Batch Insert를 수행하도록 함. ## 🧪 테스트 <!-- 어떤 테스트를 생각했고 진행했는지 알려주세요! --> - 로컬 환경에서 Login API만 별도로 성능 테스트 => 기존 로직에서는 70VU에서 다운, 개선 후 1000VU, 초당 558번의 요청에서도 정상 동작 - 테스트 결과 메모리 사용량의 큰 변화 없이 커넥션 고갈 문제 해결 ## 📚 참고 자료 및 기타 <!-- 참고한 자료, 또는 논의할 사항이 있다면 알려주세요! --> Reviewed-on: #62 Co-authored-by: pricelees <priceelees@gmail.com> Co-committed-by: pricelees <priceelees@gmail.com>
92 lines
2.4 KiB
JavaScript
92 lines
2.4 KiB
JavaScript
import http from 'k6/http';
|
|
|
|
export const BASE_URL = __ENV.BASE_URL || 'http://localhost:8080';
|
|
|
|
export function generateRandomBase64String(length) {
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
let result = '';
|
|
for (let i = 0; i < length; i++) {
|
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function parseIdToString(response) {
|
|
try {
|
|
const safeJsonString = response.body.replace(/"(\w*Id|id)"\s*:\s*(\d{16,})/g, '"$1":"$2"');
|
|
return JSON.parse(safeJsonString);
|
|
} catch (e) {
|
|
console.error(`JSON parsing failed for VU ${__VU}: ${e}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function maxIterations() {
|
|
const maxIterationsRes = http.get(`${BASE_URL}/tests/max-iterations`)
|
|
if (maxIterationsRes.status !== 200) {
|
|
throw new Error('max-iterations 조회 실패')
|
|
}
|
|
|
|
return maxIterationsRes.json('count')
|
|
}
|
|
|
|
export function fetchUsers() {
|
|
const userCount = Math.round(maxIterations() * 0.5)
|
|
const userAccountRes = http.get(`${BASE_URL}/tests/users?count=${userCount}`)
|
|
|
|
if (userAccountRes.status !== 200) {
|
|
throw new Error('users 조회 실패')
|
|
}
|
|
|
|
return userAccountRes.json('results')
|
|
}
|
|
|
|
export function fetchStores() {
|
|
const storeIdListRes = http.get(`${BASE_URL}/tests/stores`)
|
|
|
|
if (storeIdListRes.status !== 200) {
|
|
throw new Error('stores 조회 실패')
|
|
}
|
|
|
|
return parseIdToString(storeIdListRes).results
|
|
}
|
|
|
|
export function login(account, password, principalType) {
|
|
const loginPayload = JSON.stringify({
|
|
account: account,
|
|
password: password,
|
|
principalType: principalType
|
|
})
|
|
const params = { headers: { 'Content-Type': 'application/json' } }
|
|
|
|
const loginRes = http.post(`${BASE_URL}/auth/login`, loginPayload, params)
|
|
|
|
if (loginRes.status !== 200) {
|
|
throw new Error(`로그인 실패: ${__VU}`)
|
|
}
|
|
|
|
const body = parseIdToString(loginRes).data
|
|
if (principalType === 'ADMIN') {
|
|
return {
|
|
storeId: body.storeId,
|
|
accessToken: body.accessToken
|
|
}
|
|
} else {
|
|
return {
|
|
accessToken: body.accessToken
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getHeaders(token) {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
|
|
return { headers: headers };
|
|
}
|