-
Notifications
You must be signed in to change notification settings - Fork 2
๐ ํธ๋ฌ๋ธ ์ํ
๋ฌธ์๋ฅผ ์ญ์ ํ ๋, RDB(MySQL)๊ณผ MongoDB์ ๋ถ์ฐ๋ ๋ฐ์ดํฐ๊ฐ ํจ๊ป ์ญ์ ๋์ด์ผ ํ๋ค.
ํ์ง๋ง ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ ๋จ์ผ ํธ๋์ญ์
์ ๋ฌถ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅํ๋ฉฐ(ํ๋์ @Transactional๋ก ์ฒ๋ฆฌ X), ์ฒ๋ฆฌ ์์๋ ์คํจ ๋ณต๊ตฌ๋ฅผ ์ ์ค๊ณํ์ง ์์ผ๋ฉด ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ๋ถ์ผ์น๊ฐ ๋ฐ์ํ ์ ์๋ค.
ํ์ฌ ๊ตฌ์กฐ:
| db | ์ ์ฅ ๋ด์ฉ |
|---|---|
| RDB(JPA ๊ธฐ๋ฐ) | Doc, Branch, Edge์ Save, Commit์ ๋ฉํ๋ฐ์ดํฐ (์ ๋ชฉ, ์ค๋ช , ์์ฑ/์์ ์ผ์ etc) |
| MongoDB | SaveContent, Block, CommitsBlockSequence (Save์ Commit์ ๋ณธ๋ฌธ, Commit์ ๋ธ๋ก ๋ฆฌ์คํธ) |
์ ์ฅ์ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ ์ํ์ด ๋ฐ์ํ ์ ์๋ค:
- Mongo ๋จผ์ ์ ์ฅ โ ์ดํ RDB ์คํจ: Mongo์ ์ฐ๋ ๊ธฐ ๋ฐ์ดํฐ ๋จ์
- RDB ๋จผ์ ์ ์ฅ โ ์ดํ Mongo ์คํจ: RDB์ ๋ด์ฉ์ด ์๋ ์ ์ฅ์ด ์๊น
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ์ ์ฅ ๋ก์ง์ ๊ณ ์ํ์๋ค:
โ MongoDB ์ ์ฅ ํ โ ์ฑ๊ณต ์ ID๋ฅผ ๋ฐ์ โ RDB์ ์ ์ฅ
์์ ํ๋ก์ธ์ค: RDB์ @Transactional ์์์ ๋ค์ ์ฒ๋ฆฌ ์คํ
try {
1. [RDB] MongoID๋ฅผ ์ ์ธํ Save ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ฐ์ RDB์ ์ ์ฅ
2. [MongoDB] SaveContent๋ฅผ MongoDB์ ์ ์ฅ โ saveContentId ๋ฐํ
3. [RDB] ๋ฐํ๋ฐ์ saveContentId๋ฅผ ๊ธฐ์กด Save(RDB)์ ์
๋ฐ์ดํธ
4. ์ต์ข
์ ์ผ๋ก ๋ชจ๋ ์ ์ฅ ์ฑ๊ณต โ ํธ๋์ญ์
์ปค๋ฐ
} catch (์์ธ ๋ฐ์ ์) {
- [MongoDB ์๋ ๋กค๋ฐฑ] ์คํจํ Mongo ์ ์ฅ ๋ด์ฉ ์ง์ ์ญ์ (saveContentId ๋ฑ)
- ์์ธ ์ข
๋ฅ์ ๋ฐ๋ผ ์ ์ ํ CustomException ๋ฐ์ ํ
- ์ ์ฒด ํธ๋์ญ์
๋กค๋ฐฑ
}
์ญ์ ์ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ ์ํ์ด ๋ฐ์ํ ์ ์๋ค:
Mongo ์ญ์ ์ค ์์ธ โ RDB๋ ์ด๋ฏธ ์ญ์ ์๋ฃ ์ํ โ ๋กค๋ฐฑ ๋ถ๊ฐ๋ก ๋ฐ์ดํฐ ์ ํฉ์ฑ ๊นจ์ง
๋ฐ๋๋ก Mongo๋ ์ญ์ ๋๋๋ฐ RDB ํธ๋์ญ์ ์ด ๋กค๋ฐฑ๋๋ค๋ฉด โ MongoDB์ ๊ณ ์ ๋ฐ์ดํฐ ๋ฐ์
์ญ์ ๋ ์ ์ฅ๊ณผ ๋ฌ๋ฆฌ RDB๋ง ๋จผ์ ์ญ์ ๋๊ณ MpongoDB์ ๊ณ ์ ๋ฐ์ดํฐ๊ฐ ๋จ๋๋ผ๋ UX ๊ด์ ์์ ๋ฌธ์ ๊ฐ ์์ผ๋ฏ๋ก, Mongo ์ญ์ ๋ฅผ RDB ํธ๋์ญ์ ์ดํ๋ก ๋ถ๋ฆฌํ๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ์ญ์ ๋ก์ง์ ๊ณ ์ํ์๋ค:
โ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ฒ๋ฆฌ + ํธ๋์ญ์ ๋ถ๋ฆฌ + ์ฌ์๋ + ๋น๋๊ธฐ
- RDB ์ญ์
- Doc, Branch, Commit, Save ๋ฑ(RDB)์ ํ๋์
@Transactional๋ด์์ ์ญ์ - ๋์์ ์ญ์ ํด์ผํ Mongo ๊ด๋ จ ID๋ฅผ ์์งํ์ฌ
DocDeleteMongoIdsDto์ ๋ด๊ณ -
eventPublisher.publishEvent()๋ก ์ญ์ ์ด๋ฒคํธ ๋ฐํ
- MongoDB ์ญ์
-
@TransactionalEventListener(phase = AFTER_COMMIT)๋ฅผ ํตํด RDB ํธ๋์ญ์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ปค๋ฐ๋ ํ์๋ง Mongo ์ญ์ ๋ฅผ ์๋ -
@Retryable์ AOP ๊ธฐ๋ฐ์ผ๋ก ํ๋ก์๋ฅผ ํตํด ํธ์ถ๋ผ์ผ ์๋ํ๊ธฐ ๋๋ฌธ์ ์ด๋ฒคํธ๋ฆฌ์ค๋์์ ์ง์ ์ญ์ ํ์ง ์์ (๋ฆฌ์ค๋์์ ์ง์ ํธ์ถ ์ ๋์ ์ ํจ)
- Mongo ์ญ์ : ์ฌ์๋
- MongoDB๋ Atlas ๊ธฐ์ค ๊ธฐ๋ณธ์ ์ผ๋ก Replica Set์ ์ง์ํ๋ฏ๋ก, ํธ๋์ญ์ ์ฌ์ฉ์ด ๊ฐ๋ฅํจ
-
MongoTransactionManager๋ฅผ@Bean์ผ๋ก ๋ฑ๋กํด ํธ๋์ญ์ ๋ถ๋ฆฌ
- ์ญ์ ์คํจ โ
@Recover์ง์
- ์ญ์ ๊ฐ 3ํ ๋ชจ๋ ์คํจํ๋ฉด
@Recover ๋ฉ์๋๋ก ์ด๋ - ์ด ์์ ์๋ ํธ๋์ญ์
์ปจํ
์คํธ๊ฐ ์ข
๋ฃ๋์ด ์์ผ๋ฏ๋ก, ์ง์ RDB ์ ์ฅ ์๋ ์ ์์ธ ๋ฐ์ (
no transaction is in progress) -
@Async๋ก ์์ํ์ฌ ํธ๋์ญ์ ์ ์๋ก ์์ฑํ๊ณ Mongo ์ญ์ ์คํจ ๋ด์ญ(MongoDeleteFailure)์ ์ ์ฅ
- Mongo ์ญ์ ์คํจ ๊ธฐ๋ก ์ ์ฅ
-
@Async + @Transactional์กฐํฉ์ผ๋ก ์ ํธ๋์ญ์ ์ปจํ ์คํธ๋ฅผ ์์ฑํ์ฌ RDB์ Mongo ์ญ์ ์คํจ ๋ด์ญ ์ ์ฅ
- ์คํจ ๋ด์ญ ์ฌ์๋: ์ค์ผ์ค๋ฌ ๊ธฐ๋ฐ
- ์ผ์ ์ฃผ๊ธฐ(1์๊ฐ?)๋ก
MongoDeleteFailure๋ด์ญ ์กฐํ - ์ญ์ ์ฑ๊ณต ์
resolved = true๋ก soft delete์ฒ๋ฆฌ
๋ฌธ์ ์ญ์ ์ Edgde์์ ๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
Column 'next_commit_id' cannot be null
๋ค์์ ๊ธฐ์กด์ ๊ด๋ จ ์ฝ๋์ ERD์ด๋ค.
- ๋ฌธ์(Doc) ์ญ์ ๋ฐฉ์
- MongoDB: ์ญ์ ํ ๋ฌธ์์ ๊ด๋ จ๋ ๋ฐ์ดํฐ๋ฅผ dto๋ก ๋ง๋ค์ด ์ญ์ ์ด๋ฒคํธ๋ฅผ ๋ฐํ
- RDB:
cascade = CascadeType.ALL, orphanRemoval = true๋ฅผ ํตํด Doc์ ์ญ์ ๋ก ๊ด๋ฆฌ-
์๋ฌ ์์ธ ์์ : Doc ์ญ์ ์ Edge๋ณด๋ค Commit์ด ๋จผ์ ์ ๊ฑฐ๋๋ฉฐ, Edge์ next_commit_id์์
nullable = false์ ์ฝ์กฐ๊ฑด ์๋ฐฐ
-
์๋ฌ ์์ธ ์์ : Doc ์ญ์ ์ Edge๋ณด๋ค Commit์ด ๋จผ์ ์ ๊ฑฐ๋๋ฉฐ, Edge์ next_commit_id์์
public void delete(Long docId, Long userId) {
User user = getUserOrThrow(userId);
Doc doc = getDocByIdAndUserId(docId, userId);
List<Branch> branches = doc.getBranches();
MongoIdsDto docDeleteMongoIds = mongoIdsCollector.collectFrom(branches);
user.removeDocument(doc);
eventPublisher.publishEvent(docDeleteMongoIds);- Edgde ์ํฐํฐ
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "document_id", nullable = false)
private Doc doc;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "prev_commit_id", nullable = false)
private Commit prevCommit;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "next_commit_id", nullable = false)
private Commit nextCommit;- Doc ์ํฐํฐ
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@OneToMany(mappedBy = "doc", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Branch> branches;
@OneToMany(mappedBy = "doc", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Edge> edges;- ERD
- DocService์์ doc ์ญ์ ์ ๊ฐ์ ์ ๋จผ์ ๋ช ์์ ์ผ๋ก ์ญ์ ํจ์ผ๋ก์จ ํด๊ฒฐํ์๋ค.
public void delete(Long docId, Long userId) {
User user = getUserOrThrow(userId);
Doc doc = getDocByIdAndUserId(docId, userId);
List<Edge> edges = doc.getEdges(); // ๊ฐ์ ์์ง
List<Branch> branches = doc.getBranches();
MongoIdsDto docDeleteMongoIds = mongoIdsCollector.collectFrom(branches);
edgeRepository.deleteAll(edges); //๊ฐ์ ์ญ์
user.removeDocument(doc);
eventPublisher.publishEvent(docDeleteMongoIds);์ด ์๋ฌ์ ์์ธ์ orphanRemoval ์ ์ํด ๋ฐ์ํ๋ค.
- edges ์ปฌ๋ ์ ์์ Edge ๊ฐ์ฒด๊ฐ ์ ๊ฑฐ๋จ
- Hibernate๊ฐ ์์์ฑ ์ปจํ ์คํธ์์ Edge -> Commit ์ฐธ์กฐ๋ฅผ ๋์ผ๋ฉด์(๋ฉ๋ชจ๋ฆฌ์ null ์ค์ ) UPDATE๋ฅผ ๋ ๋ฆผ
- DB์์๋ nullable = false ์ ์ฝ ์กฐ๊ฑด ๋๋ฌธ์ UPDATE ์ ์๋ฌ ๋ฐ์.
- ๊ณง DELETEํ ๊ฐ์ฒด์ธ๋ฐ FK null ์ ์ฝ์ด ๋จผ์ ๊ฑธ๋ ค์ ์คํจ.
์ฆ, DELETE ์ FK null UPDATE๊ฐ ๋ฐ์
*์์ง ๋ค ์ ์