-
Notifications
You must be signed in to change notification settings - Fork 1
๐งช MVP2: ์๋ฒ ์ต๋ ๋ถํ ์ฐพ๊ธฐ
- ์๋ณธ ์ํ
- ์คํธ๋ฆผ ๊ฐ์
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปค๋ฅ์ ํ ๊ฐ์
- ๋ฒํฌ ์ฌ์ด์ฆ ๊ฐ์
- Read uncommited database mode
- ๋ณตํฉ ์ธ๋ฑ์ค 2๊ฐ
- 50๋ง๊ฐ ๋ฐ์ดํฐ insert
- 5๋ง๊ฐ์ ๋ฐ์ดํฐ, Lambda๋ก insert (warm up)
- 10๊ฐ์ ์ธ์คํด์ค์์ vUser 50๊ฐ๋ก ๋ฌดํ ์์ฒญ(100๊ฐ ๋ก๊ทธ์ฉ)
์ต์ ํ ์ ํฐ์บฃ(100 fixed) | |
---|---|
์ต๋ RPS | 803 |
์์ ์๊ฐ | 03:15 |
RPS 0์๊ฐ | 04:25 |
diff | 1:10 |
์ต๋ RPS๊ฐ 803์์ 1:10 ์ด๋ง์ ์๋ฒ down
- ์์ฒญ ํ๋ฒ์ ์ฌ๋ฌ๊ฐ์ ์คํธ๋ฆผ์ ์ฌ์ฉํด์ ๊ฒ์ฆ๊ณผ ๋ณํ์ ํ๋ค
- ์คํธ๋ฆผ์ ์ผํ์ฉ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ฏ๋ก GC๊ฐ ๋ ์ง๋ผ๋ ๋ฉ๋ชจ๋ฆฌ ๋ถํ๊ฐ ํด๊ฒ์ด๋ค
- Controller Layer์ ๊ฒ์ฆ๊ณผ, Service Layer์ ๊ฒ์ฆ์ด ์ค๋ณต๋จ
- ์คํธ๋ฆผ ์ฌ์ฉ์ ๋ฐ๋ณต๋ฌธ์ผ๋ก ๋ณ๊ฒฝ
- ๋ถํ์ํ ์ผํ์ฉ ๊ฐ์ฒด ์์ฑ ๋ฐฉ์ง
- Contorller Layer์ ์ค๋ณต ๊ฒ์ฆ ์
stream ์ต์ ํ ํฐ์บฃ(100 fixed) | |
---|---|
์ต๋ RPS | 967 |
์์ ์๊ฐ | 55:13 |
RPS 0์๊ฐ | 56:01 |
diff | 0:48 |
48์ด๋ง์ ์ฃฝ์์ง๋ง ์ต๋ ์ฒ๋ฆฌ์จ 803 โ 967 ๋์ด์ ๋ฐ์ดํฐ๊ฐ ๋ ๋นจ๋ฆฌ ์ฐจ์ ์ฃฝ์๋ค
- ๋ฐ์ดํฐ๊ฐ ๋ ๋ง์์ ธ์ OOM์ด ์๊ฒจ ์ฃฝ๋๋ค
- ๋ฐ์ดํฐ๊ฐ ๋จ์ง ์๋๋ก ๋น ๋ฅด๊ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก Flush๋ฅผ ํด์ค๋ค
- ์ฌ์ฉํ์ง ์๋ ๋ ๋ง์ connection์ ์ฌ์ฉํ ์ ์๋๋ก ๊ฐ์
- 10๊ฐ์ 50%์์ 80%๋ก ์ฆ์ค
stream + ํฐ์บฃ + ์ค๋ ๋ํ 50% | stream + ํฐ์บฃ + ์ค๋ ๋ํ 80% | |
---|---|---|
์ต๋ RPS | 967 | 891 |
์์ ์๊ฐ | 55:13 | 13:33 |
RPS 0์๊ฐ | 56:01 | 14:36 |
diff | 0:48 | 1:03 |
์ต๋ ์ฒ๋ฆฌ์จ์ ๋์๋๋ฐ, ์ญ์ ์ฃฝ์๋ค
- ๋ ๋นจ๋ฆฌ ๋ง์ ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก์ Flush ๊ฐ ํ์ํ ๊ฒ์ด๋ค
- connections pool 10 โ20 (8 โ 16)
stream + ํฐ์บฃ + ์ค๋ ๋ํ 80% | stream + ํฐ์บฃ + ์ค๋ ๋ํ 80%+ ํ์นด๋ฆฌ 20 | |
---|---|---|
์ต๋ RPS | 891 | 1073 |
์์ ์๊ฐ | 13:33 | 20:39 |
RPS 0์๊ฐ | 14:36 | 21:37 |
diff | 1:03 | 58 |
์ฒ๋ฆฌ์จ์ ๋์๋๋ฐ ์ญ์ ์ฃฝ์๋ค
- ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก์ flush ํ๋ ๋์ญํญ์ ๋๋ ธ์ผ๋, ์์ ๋๋ ค์ผํ๋ค
- bulk insert ์ฌ์ด์ฆ๋ฅผ ๋๋ฆฌ๋ฉด ๊ฐ์ ๋ ๊ฒ์ด๋ค
- bulk size 100 โ 1000
stream + ํฐ์บฃ + ์ค๋ ๋ํ 80% + ํ์นด๋ฆฌ 20 | stream + ํฐ์บฃ + ์ค๋ ๋ํ 80% + ํ์นด๋ฆฌ 20 + bulk size 1000 | |
---|---|---|
์์ฒญ ์ | 40000 | |
์ต๋ RPS | 1073 | 716 |
์์ ์๊ฐ | 20:39 | 26:34 |
RPS 0์๊ฐ | 21:37 | 27:52(response time peak) |
diff | 58 |
์๋ฒ๊ฐ ์ฃฝ์ง๋ ์์์ผ๋ ์๋ต์๊ฐ์ด 2์ด ์ด์ ๊ฑธ๋ฆฌ๋ ๋ฌธ์ ๊ฐ ์๊น
- ๋ ๋ง์ ์์ฉ flushํด์ผํ๋ค
- bulk size 1000 โ 2000
stream + ํฐ์บฃ + ์ค๋ ๋ํ 80% + ํ์นด๋ฆฌ 20 + bulk size 1000 | stream + ํฐ์บฃ + ์ค๋ ๋ํ 80% + ํ์นด๋ฆฌ 20 + bulk size 2000 | |
---|---|---|
์์ฒญ ์ | 40000 | 37167 |
์ต๋ RPS | 716 | 655 |
์์ ์๊ฐ | 26:34 | 48:15 |
RPS 0์๊ฐ | 27:52(response time peak) | 49:45 |
๊ฐ์ ์ ์ด ์๋ค
โ ๋ก์ง ์ ๊ฒ
์ฌ์ค์ ๋ก์ง์ ๋ฌธ์ ๊ฐ ์์์ ํ์ธ
log๋ฅผ flush ํ ์กฐ๊ฑด
- queue.size() > defailtBulkSize
- ํ์ ์ฌ์ด์ฆ๊ฐ flushํด์ผ ํ bullksize๋ณด๋ค ํฌ๋ค
- currentTime > lastflushTime + defaultTimeout
- ๋ง์ง๋ง flush ์๊ฐ๋ณด๋ค timeout ์๊ฐ์ด ์ง๋๋ค
๊ธฐ์กด ์ฝ๋
void leaderTask(Consumer<List<Log>> saveFunction) {
while (!Thread.currentThread().isInterrupted()) {
try {
final Log log = logQueue.poll(defaultTimeout, TimeUnit.MILLISECONDS);
/*
* Log๊ฐ ์ฒ์ฒํ ๋ค์ด์ค๋ ๊ฒฝ์ฐ Timeout์ ํ ๋ฒ์ฉ ์ ์ฅ
*
* Log๊ฐ ๋์ ๋ถํ๋ก ๋ค์ด์ค๋ ๊ฒฝ์ฐ Bulk Size๋งํผ ํ ๋ฒ์ ์ ์ฅ
*
* Timeout๋์ ๋ค์ด์จ Log๊ฐ ์๋ ๊ฒฝ์ฐ ๋ค์ ๋ฐ๋ณต๋ฌธ cycle ์ํ
*/
if (log == null) {
continue;
}
List<Log> logs = new ArrayList<>();
logs.add(log);
//drainTo๋ Queue์ ์๋ ์์๋ฅผ maxElements๋งํผ ๊บผ๋ด์ Collection์ ๋ด์์ค๋ค.
logQueue.drainTo(logs, defaultBulkSize - 1);
// Follower Thread Pool์ ์ ์ฅ ์์ฒญ
followerExecutor.execute(() -> saveFunction.accept(logs));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Leader thread was interrupted. Exiting.", e);
break;
} catch (Exception e) {
log.error("Unexpected error in leader thread", e);
}
}
}
logQueue.drainTo(logs, defaultBulkSize - 1);
์์ log๊ฐ ํ๋๋ผ๋ ๋์ค๋ฉด defaultBulkSize
๋งํผ ๋ค ์ฐจ์ง ์๋๋ผ๋ ๋ชจ๋ ๋ก๊ทธ๋ฅผ ์ถ์ถํด์ flush๋ฅผ ํ๋ค.
void leaderTask(Consumer<List<Log>> saveFunction) {
long lastFlushTime = System.currentTimeMillis();
while (!Thread.currentThread().isInterrupted()) {
try {
int size = logQueue.size();
// ํ์ฌ ์๊ฐ ํ์ธ
long currentTime = System.currentTimeMillis();
// 2์ด๊ฐ ์ง๋ฌ๊ฑฐ๋, ํ์ ํฌ๊ธฐ๊ฐ defaultBulkSize ์ด์์ธ ๊ฒฝ์ฐ flush
if ((currentTime - lastFlushTime > defaultTimeout) || size >= defaultBulkSize) {
List<Log> logs = new ArrayList<>();
// ๋ก๊ทธ๋ฅผ ์ต๋ defaultBulkSize ๊ฐ์๋งํผ ํ์์ ๊ฐ์ ธ์ด
logQueue.drainTo(logs, defaultBulkSize);
if (!logs.isEmpty()) {
followerExecutor.execute(() -> saveFunction.accept(logs));
}
lastFlushTime = currentTime;
}
} catch (Exception e) {
log.error("Unexpected error in leader thread", e);
}
}
}
lastFlushTime
์ ๊ธฐ๋กํด์ ์กฐ๊ฑด์ ๋ช
ํํ๊ฒ ์ค์ ํด์ ์ ๋๋ก ๋ก๊ทธ๊ฐ ์์์๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ๊ฐ ์ ์๋๋ก ๋ณ๊ฒฝํ์ต๋๋ค.
150 TPS๋ก ๊ณ ์ ์์ ํ ๋จ์ ํ์ธ
- ๐ก How To? Chat-GPT ๋ฆฌ๋ทฐ์ด ๋์ ํ๊ธฐ
- ๐ก How To? ๋๋ฉ์ธ ์ค์ ํ๊ธฐ
- ๐ก How To? NGINX๋ก CORS ์ค์ ํ๊ธฐ
- ๐ก How To? JavaScript SDK ๊ฐ๋ฐํ๊ธฐ
- ๐ก How To? Java SDK ๊ฐ๋ฐํ๊ธฐ
- ๐ก How To? AWS Lambda๋ก ๋ถํ ํ ์คํธ ์งํํ๊ธฐ
- ๐ก How To? Terraform + Locust๋ก ๋ถํ ํ ์คํธ ์งํํ๊ธฐ
- โ๏ธ Refactoring: ๋ก๊ทธ ์ ์ฅ์ ํ๋ฒ์ ํ์!
- ๐จ ์๋ฒฝํ์ง ์์ ์๋๋ฐ์ค ํ ์คํธ๊ฐ ๋ถ๋ฌ์จ ํญํ
- ๐ก How To? ๋๋ฒ๊น ์ฉ ๊ฒฝ์์ด ๋ฐ์ํ์ง ์๋ Long ์นด์ดํฐ ๋ง๋ค๊ธฐ!
- ๐ก How To? Queue์ poll๊ณผ push๋ ์ ํ ์ค๋ ๋์์ ๋ด๋นํ๊ฒ ํ์๊น?