Skip to content

Commit

Permalink
Feat/99 reservation spike test (#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
junha-ahn authored Nov 6, 2023
1 parent 7c6574f commit 17ab2ca
Show file tree
Hide file tree
Showing 20 changed files with 325 additions and 125 deletions.
24 changes: 12 additions & 12 deletions docs/open-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -494,15 +494,15 @@ components:
format: int32
sort:
$ref: '#/components/schemas/SortObject'
first:
type: boolean
last:
type: boolean
numberOfElements:
type: integer
format: int32
pageable:
$ref: '#/components/schemas/PageableObject'
first:
type: boolean
last:
type: boolean
empty:
type: boolean
PageableObject:
Expand Down Expand Up @@ -640,15 +640,15 @@ components:
format: int32
sort:
$ref: '#/components/schemas/SortObject'
first:
type: boolean
last:
type: boolean
numberOfElements:
type: integer
format: int32
pageable:
$ref: '#/components/schemas/PageableObject'
first:
type: boolean
last:
type: boolean
empty:
type: boolean
EventResponse:
Expand Down Expand Up @@ -703,14 +703,14 @@ components:
format: int32
sort:
$ref: '#/components/schemas/SortObject'
first:
type: boolean
last:
type: boolean
numberOfElements:
type: integer
format: int32
pageable:
$ref: '#/components/schemas/PageableObject'
first:
type: boolean
last:
type: boolean
empty:
type: boolean
11 changes: 4 additions & 7 deletions src/performanceTest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ JWT_ISSUER=
# Monitoring Configuration (Optional)
GRAFANA_HOST=
K6_OUT= #experimental-prometheus-rw
K6_OUT= # experimental-prometheus-rw
K6_PROMETHEUS_RW_SERVER_URL=
K6_PROMETHEUS_RW_USERNAME=
K6_PROMETHEUS_RW_PASSWORD=
K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM= #true
K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM=true
```

### 4. Run the following commands
Expand All @@ -53,11 +53,8 @@ You can change the `ENTRYPOINT` variable to run different test files:


```shell
# To run the `a_test.js` file
ENTRYPOINT=a_test.js docker-compose up

# To run the `b_test.js` file
ENTRYPOINT=b_test.js docker-compose up
# To run the `smokeTest/healthCheck.js` file
ENTRYPOINT=smokeTest/healthCheck.js docker-compose up
```


Expand Down
2 changes: 1 addition & 1 deletion src/performanceTest/dummy-data/src/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const event = new Table('event', [
const user = new Table('user', [
new Field('id', (i) => `${i + 1}`),
new Field('email', (i) => `K6-${i + 1}@email.com`),
new Field('name', (i) => `${i + 1}`),
new Field('name', (i) => `${i + 1}-name`),
new Field('phone_number', (i) => "010-1234-1234"),
new Field('pw', (i) => userpassword[i % 1000]),
new Field('created_at', (i) => faker.date.recent()),
Expand Down
9 changes: 7 additions & 2 deletions src/performanceTest/initdb/sqls/cleanup.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ SET foreign_key_checks = 0;

TRUNCATE bookmark;
TRUNCATE reservation;
DELETE FROM event WHERE id > 10000000;

DELETE FROM user WHERE id > 1000000;
-- UPDATE event SET current_reservation_count = 0;
ALTER TABLE user AUTO_INCREMENT=1000001;

DELETE FROM event WHERE id > 10000000;
ALTER TABLE event AUTO_INCREMENT=10000001;

UPDATE event SET total_attendees = 0 WHERE id IN (98);

SET foreign_key_checks = 1;
59 changes: 59 additions & 0 deletions src/performanceTest/scripts/breakdownTest/breakdownTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { check } from "k6";
import Request from "../lib/request.js";
import { encode } from "../lib/jwt.js";
import hooks from "../lib/hooks.js";
import generator from "../lib/generator.js";
import { isSuccess, randomInt, isAlreadReservedAll } from "../lib/helpers.js";

export const setup = hooks.setup
export const handleSummary = hooks.handleSummary

export const options = {
tags: {
testid: `${__ENV.ENTRYPOINT}`
},
ext: {
loadimpact: {
apm: [
{
includeTestRunId: true,
}
]
}
},

executor: 'ramping-arrival-rate', //Assure load increase if the system slows
stages: [
{ duration: '30m', target: 10000 }, // just slowly ramp-up to a HUGE load
],

thresholds: {
http_req_failed: ['rate<0.01'], // http errors should be less than 1%
http_req_duration: ['p(95)<300'], // 95% of requests should be below 300ms
},
};


export default function () {
const req = new Request()

const ID = randomInt(1, 1000000)
req.setToken(encode(ID))

const query = {
size: 20,
page: 0,
sort: "id,asc"
}
for (let i = 0; i < 13; i++) {
check(req.getEvents(query), {"Success Get Events": isSuccess});
query.page = query.page + randomInt(1, 10)
}

const eventId = 98 // maxAttendees = 191
check(req.getEvent(eventId), {"EVENT 98 maxAttendees = 191": (r) => r.json().data.maxAttendees === 191})

const res = req.createReservation(generator.Reservation(eventId))
check(res, {"Success Reservation": isSuccess});
check(res, {"Already reserved": isAlreadReservedAll});
}
17 changes: 15 additions & 2 deletions src/performanceTest/scripts/lib/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,32 @@ const User = (ID) => {
let pw = Number(ID) % 1000
if (pw == 0) pw = 1000
return {
name: `${ID}`,
name: `${ID}-name`,
email: `K6-${ID}@email.com`,
password: `K6-${pw}-password`,
phoneNumber: '010-1234-1234',
}
} else {
return {
name: `${getPrefix()}-name`,
email: `${getPrefix()}@email.com`,
password: `${getPrefix()}-password`,
phoneNumber: '010-1234-1234',
}
}
}

const Reservation = (eventId) => {
return {
eventId,
name: "name",
phoneNumber: "010-1234-1234",
postCode: 12345,
address: "address",
}
}

export default {
User
User,
Reservation,
}
10 changes: 9 additions & 1 deletion src/performanceTest/scripts/lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ import exec from 'k6/execution';

const getRandomByRange = (max) => Math.floor(Math.random() * max);

export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export const getOneFromList = (list) => list[getRandomByRange(list.length)];

export const isSuccess = (r) => r.status >= 200 && r.status < 300;
export const isFail = (r) => !isSuccess(r);


export const isAlreadReservedAll = (r) => r.status == 409 && r.json().errorCode == 50001;
export const isRunningQueueTicket = (r) => r.status == 200 && r.json().data.isWaiting == false;

export const randomInt = (start, end) => randomIntBetween(start, end);

export const getUserIDFromExec = (VU_COUNT) => exec.vu.idInTest + (VU_COUNT * exec.vu.iterationInScenario)
export const getUserIDFromExec = (VU_COUNT) => exec.vu.idInTest + (VU_COUNT * exec.vu.iterationInScenario)

4 changes: 3 additions & 1 deletion src/performanceTest/scripts/lib/hooks.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { htmlReport } from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js";
import { textSummary } from "https://jslib.k6.io/k6-summary/0.0.1/index.js";
import config from "./config.js";
import http from "k6/http";

http.setResponseCallback(http.expectedStatuses({ min: 200, max: 499 }));

export default {
setup: function() {

console.log(`setup: ${new Date().toISOString()}`)
// https://stackoverflow.com/questions/73458542/k6-storing-data-between-setup-and-default-functions
return {
Expand Down
37 changes: 33 additions & 4 deletions src/performanceTest/scripts/lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@ import http from "k6/http";
import { sleep } from "k6";
import config from "./config.js";


function parseQuery(query) {
if (query == null) {
return ''
}
const keys = Object.keys(query)
const queryList = []
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const value = query[key]
queryList.push(`${key}=${value}`)
}
return '?' + queryList.join('&')
}
export default class Request {
constructor(baseURL = config.HOST) {
this.baseURL = baseURL
Expand Down Expand Up @@ -56,9 +68,9 @@ export default class Request {
return res
}

getEvents() {
getEvents(query) {
this.beforeHook()
const res = http.get(`${this.baseURL}/events/`);
const res = http.get(`${this.baseURL}/events` + parseQuery(query), this.getParams());
this.afterHook()
return res
}
Expand All @@ -78,7 +90,7 @@ export default class Request {
this.beforeHook()
const res = http.post(`${this.baseURL}/users/signin`, JSON.stringify({ email: body.email, password: body.password }), this.getParams());
if (res.body) {
this.setToken(res.json()['Authorization'])
this.setToken(res.json().data.Authorization)
}
this.afterHook()
return res
Expand All @@ -98,4 +110,21 @@ export default class Request {
this.afterHook()
return res
}


createQueueTicket(eventId, userId) {
this.beforeHook()
const res = http.post(`${this.baseURL}/ticket`, JSON.stringify({eventId, userId}), this.getParams());
this.afterHook()
return res
}

getQueueTicket(eventId, userId) {
this.beforeHook()
const res = http.get(`${this.baseURL}/ticket/${eventId}/${userId}`, this.getParams());
this.afterHook()
this.afterHook()
this.afterHook()
return res
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { check } from "k6";
import Request from "./lib/request.js";
import generator from "./lib/generator.js";
import hooks from "./lib/hooks.js";
import { isSuccess, getOneFromList } from "./lib/helpers.js";
import Request from "../lib/request.js";
import generator from "../lib/generator.js";
import hooks from "../lib/hooks.js";
import { isSuccess, getOneFromList } from "../lib/helpers.js";

export const setup = hooks.setup
export const handleSummary = hooks.handleSummary
Expand Down Expand Up @@ -37,15 +37,6 @@ export default function () {
const getAvaliableReservation = () => {
const events = req.getEvents()
return getOneFromList(events.json())


// for (const event of events) {
// if (event.currentReservationCount < event.maxAttendees) {
// return event.id
// }
// }

// return null
}

const user = generator.User()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { check } from "k6";
import Request from "../lib/request.js";
import hooks from "../lib/hooks.js";
import generator from "../lib/generator.js";
import { getUserIDFromExec, isSuccess } from "../lib/helpers.js";
import Request from "./lib/request.js";
import hooks from "./lib/hooks.js";
import generator from "./lib/generator.js";
import { getUserIDFromExec, isSuccess } from "./lib/helpers.js";

export const setup = hooks.setup
export const handleSummary = hooks.handleSummary

const VU_COUNT = 100
const VU_COUNT = 10
export const options = {
scenarios: {
contacts: {
executor: 'per-vu-iterations',
vus: VU_COUNT,
iterations: 20,
iterations: 10,
maxDuration: '10m',
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ export default function () {

const res = req.access_token_info()
check(res, { "status == 200": (r) => r.status == 200 });
check(res, { "res has userId key": (r) => r.json().userId > 0 });
check(res, { "res has userId key": (r) => r.json().data.userId > 0 });
}
Loading

0 comments on commit 17ab2ca

Please sign in to comment.