TL;DR

  • ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘ ์‹œ์ ์ด ํด๋ผ์ด์–ธํŠธ๋งˆ๋‹ค ๋‹ฌ๋ผ autocommit ๊ธฐ๋ณธ๊ฐ’ ํ™•์ธ์ด ๋จผ์ €
  • ๊ฒฝ๊ณ„ ๊ธฐ์ค€์€ ํ•˜๋‚˜. ํ•จ๊ป˜ ์‹คํŒจํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๋งŒ ๋ฌถ๊ณ  ์™ธ๋ถ€ ํ˜ธ์ถœ์€ ๋ฐ–์œผ๋กœ ๋บŒ
  • ์‹œํ€€์Šค์™€ ์™ธ๋ถ€ ๋ถ€์ˆ˜ ํšจ๊ณผ๋Š” ๋กค๋ฐฑ์„ ๋”ฐ๋ฅด์ง€ ์•Š์œผ๋ฏ€๋กœ ์žฌ์‹œ๋„ ์„ค๊ณ„๊ฐ€ ๋ณ„๋„๋กœ ํ•„์š”

AI-assisted


1. ํŠธ๋žœ์žญ์…˜์€ ์–ธ์ œ ์‹œ์ž‘๋˜๋Š”๊ฐ€

ํŠธ๋žœ์žญ์…˜์„ ์“ธ ๋•Œ ์ฒ˜์Œ ๊ฑธ๋ฆฌ๋Š” ๊ณณ์€ ๋‚ด๊ฐ€ ์ง€๊ธˆ ํŠธ๋žœ์žญ์…˜ ์•ˆ์— ์žˆ๋Š”์ง€ ๋ชจ๋ฅธ๋‹ค๋Š” ์ ์ด๋‹ค.

BEGIN์„ ์•ˆ ์ณค๋Š”๋ฐ ์—ด๋ ค ์žˆ๊ธฐ๋„ ํ•˜๊ณ  ์ณค๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ ์•ˆ ์—ด๋ ค ์žˆ๊ธฐ๋„ ํ•˜๋‹ค. ํด๋ผ์ด์–ธํŠธ๋งˆ๋‹ค ๊ธฐ๋ณธ๊ฐ’์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

ํด๋ผ์ด์–ธํŠธautocommit ๊ธฐ๋ณธํŠธ๋žœ์žญ์…˜์ด ์—ด๋ฆฌ๋Š” ์‹œ์ 
psqlonBEGIN์ด๋‚˜ START TRANSACTION์„ ์ง์ ‘ ์น  ๋•Œ
psycopg3off์ฒซ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋‚ผ ๋•Œ ์ž๋™์œผ๋กœ. SELECT ํ•˜๋‚˜๋กœ๋„ ์—ด๋ฆฐ๋‹ค
JDBConsetAutoCommit(false) ์ดํ›„ ์ฒซ ์ฟผ๋ฆฌ
SQLAlchemy 2.0off (autobegin)์ฒซ execute() ํ˜ธ์ถœ ๋•Œ ์ž๋™์œผ๋กœ ์—ด๋ฆฐ๋‹ค
Django ORMon๊ฐ€์žฅ ๋ฐ”๊นฅ atomic() ๋ธ”๋ก์— ๋“ค์–ด๊ฐˆ ๋•Œ

autocommit์ด ์ผœ์ ธ ์žˆ์œผ๋ฉด ๋ฌธ์žฅ ํ•˜๋‚˜๊ฐ€ ๊ณง ํŠธ๋žœ์žญ์…˜ ํ•˜๋‚˜๋‹ค. UPDATE ๋‘ ์ค„์„ ๋‚˜๋ž€ํžˆ ์‹คํ–‰ํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜ ๋‘ ๊ฐœ๋กœ ๋‚˜๋‰˜์–ด ์ค‘๊ฐ„์— ๋‚จ์ด ๋ผ์–ด๋“ค ์ˆ˜ ์žˆ๋‹ค.

๋„๋ฉด ๋ฐ˜๋Œ€๊ฐ€ ๋œ๋‹ค. psycopg3์™€ SQLAlchemy 2.0์€ BEGIN์„ ์•ˆ ์จ๋„ ์ฒซ ๋ฌธ์žฅ์—์„œ ํŠธ๋žœ์žญ์…˜์„ ์—ด๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ๋‹ซ๊ธฐ ์ „๊นŒ์ง€ ์œ ์ง€ํ•œ๋‹ค. psycopg ๋ฌธ์„œ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ช…์‹œํ•˜๊ณ  ์žˆ๋‹ค.

By default even a simple SELECT will start a transaction: in long-running programs, if no further action is taken, the session will remain idle in transaction

์กฐํšŒ๋งŒ ํ•˜๋Š” ์ฝ”๋“œ๋„ ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์œผ๋ฉด idle in transaction์œผ๋กœ ๋‚จ๋Š”๋‹ค.

์ง€๊ธˆ ์ƒํƒœ๋Š” ์„œ๋ฒ„์—์„œ ํ™•์ธํ•œ๋‹ค.

SELECT pid, state, xact_start, query
FROM pg_stat_activity WHERE pid = pg_backend_pid();
  • state๊ฐ€ idle์ด๋ฉด ํŠธ๋žœ์žญ์…˜ ๋ฐ–
  • idle in transaction์ด๋ฉด ์—ด์–ด๋†“๊ณ  ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ•˜๋Š” ์ค‘
  • xact_start๊ฐ€ ์ฑ„์›Œ์ ธ ์žˆ์œผ๋ฉด ๊ทธ ์‹œ๊ฐ๋ถ€ํ„ฐ ํŠธ๋žœ์žญ์…˜์ด ์—ด๋ ค ์žˆ๋‹ค

์ฃผ์˜: ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ํ™˜๊ฒฝ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ๋ˆ๋‹ค

  • ๋กœ์ปฌ์—์„œ psql๋กœ ๊ฒ€์ฆํ•œ SQL์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์˜ฎ๊ธฐ๋ฉด ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ฐ”๊พธ๋ฉด ๊ฒฝ๊ณ„๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค. psycopg3์™€ SQLAlchemy๋Š” ์•ˆ ์—ด์–ด๋„ ์—ด๋ฆฌ๊ณ  JDBC์™€ Django๋Š” ์•ˆ ๋‹ซ์•„๋„ ๋‹ซํžŒ๋‹ค
  • ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋ฐ”๊ฟ€ ๋•Œ๋Š” ๊ธฐ๋ณธ๊ฐ’๋ถ€ํ„ฐ ํ™•์ธํ•œ๋‹ค

2. ์–ด๋””์„œ ๋‹ซ์„ ๊ฒƒ์ธ๊ฐ€

๊ฒฝ๊ณ„ ๊ธฐ์ค€์€ ํ•˜๋‚˜๋‹ค. ํ•จ๊ป˜ ์‹คํŒจํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๋งŒ ๋ฌถ๋Š”๋‹ค.

์‹ค์ „์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ๋ฐ”๊ฟ” ๋ฌผ์œผ๋ฉด ํŒ๋‹จ์ด ๋น ๋ฅด๋‹ค. โ€œ์ด๊ฒŒ ์‹คํŒจํ•˜๋ฉด ์•ž์˜ ๊ฒƒ๋„ ์ทจ์†Œ๋ผ์•ผ ํ•˜๋‚˜?โ€ ์•„๋‹ˆ๋ผ๋ฉด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋‚˜๋ˆˆ๋‹ค.

ํ›„๋ณด๋Š” ๋Œ€์ฒด๋กœ ์…‹์ด๋‹ค.

๋ฒ”์œ„์–ธ์ œ ๋งž๋‚˜์œ„ํ—˜
๋ฌธ์žฅ ๋‹จ์œ„๋‹จ์ˆœ ์กฐํšŒ, ๋…๋ฆฝ์ ์ธ ๋‹จ๊ฑด ๊ฐฑ์‹ ์—ฌ๋Ÿฌ ๋ฌธ์žฅ์˜ ์ •ํ•ฉ์„ฑ์„ ๋ชป ์ง€ํ‚ด
์ž‘์—… ๋‹จ์œ„๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์—†์Œ. ๊ธฐ๋ณธ์œผ๋กœ ์‚ผ๋Š”๋‹ค
์š”์ฒญ ๋‹จ์œ„์งง๊ณ  DB๋งŒ ๊ฑด๋“œ๋ฆฌ๋Š” API์™ธ๋ถ€ ํ˜ธ์ถœ์ด ๋ผ๋ฉด ์˜ค๋ž˜ ์—ด๋ฆผ

๋„“๊ฒŒ ์žก์„ ๋•Œ์™€ ์ข๊ฒŒ ์žก์„ ๋•Œ ์žƒ๋Š” ๊ฒƒ์ด ๋‹ค๋ฅด๋‹ค.

  • ๋„“๊ฒŒ ์žก์œผ๋ฉด: ๋ฝ ์œ ์ง€ ์‹œ๊ฐ„ ์ฆ๊ฐ€, ์ปค๋„ฅ์…˜ ์ ์œ , MVCC ์ฃฝ์€ ๋ฒ„์ „ ํšŒ์ˆ˜ ์ง€์—ฐ
  • ์ข๊ฒŒ ์žก์œผ๋ฉด: ์ค‘๊ฐ„ ์ƒํƒœ๊ฐ€ ๋ฐ–์—์„œ ๋ณด์ž„, ์ผ๋ถ€๋งŒ ๋ฐ˜์˜๋˜๊ณ  ๋๋‚จ

์ฃผ๋ฌธ ์ฒ˜๋ฆฌ๋ฅผ ์˜ˆ๋กœ ๋“ค๋ฉด ์ด๋ ‡๊ฒŒ ๊ฐˆ๋ฆฐ๋‹ค.

# ํ•˜๋‚˜๋กœ ๋ฌถ๋Š”๋‹ค: ์žฌ๊ณ ์™€ ์ฃผ๋ฌธ์€ ํ•จ๊ป˜ ์‹คํŒจํ•ด์•ผ ํ•œ๋‹ค
with conn.transaction():
    decrease_stock(item_id, qty)
    create_order(user_id, item_id, qty)
 
# ๋ฐ–์œผ๋กœ ๋บ€๋‹ค: ์•Œ๋ฆผ์ด ์‹คํŒจํ•ด๋„ ์ฃผ๋ฌธ์€ ์‚ด์•„ ์žˆ์–ด์•ผ ํ•œ๋‹ค
send_notification(user_id)

๊ฒฝ๊ณ„๋ฅผ ๋ชป ์ •ํ•˜๊ฒ ์„ ๋•Œ

  • ๋˜๋Œ๋ฆฌ๋ฉด ์•ˆ ๋˜๋Š” ๊ฒƒ์ด ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ๊ทธ ์ง€์ ์ด ๊ฒฝ๊ณ„๋‹ค
  • ์• ๋งคํ•˜๋ฉด ์ข๊ฒŒ ์‹œ์ž‘ํ•œ๋‹ค. ๋‚˜์ค‘์— ๋„“ํžˆ๋Š” ํŽธ์ด ๋ฐ˜๋Œ€๋ณด๋‹ค ์‰ฝ๋‹ค

3. ํŠธ๋žœ์žญ์…˜ ์•ˆ์— ๋„ฃ์œผ๋ฉด ์•ˆ ๋˜๋Š” ๊ฒƒ

ํ›„์† ์ฒ˜๋ฆฌ๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์€ ๋น„๋™๊ธฐ๋กœ ๋„˜๊ฒจ ๋™๊ธฐ ํŠธ๋žœ์žญ์…˜์˜ ์“ฐ๊ธฐ ๋ฒ”์œ„๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

๊ฒฝ๊ณ„๋ฅผ ๋„“๊ฒŒ ์žก๋Š” ๊ฒƒ ์ž์ฒด๋ณด๋‹ค ์•ˆ์— ๋ฌด์—‡์ด ๋“ค์–ด๊ฐ€๋А๋ƒ๊ฐ€ ๋ฌธ์ œ์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

  • ์™ธ๋ถ€ API ํ˜ธ์ถœ: ์ƒ๋Œ€๊ฐ€ ๋А๋ฆฌ๋ฉด ๊ทธ ์‹œ๊ฐ„๋งŒํผ ํŠธ๋žœ์žญ์…˜์ด ์—ด๋ ค ์žˆ๋‹ค. ํƒ€์ž„์•„์›ƒ์ด 30์ดˆ๋ฉด 30์ดˆ ๋™์•ˆ ๋ฝ๊ณผ ์ปค๋„ฅ์…˜์„ ์ฅ”๋‹ค
  • ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋Œ€๊ธฐ: ํ™”๋ฉด์„ ๋„์›Œ๋†“๊ณ  ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ตฌ์กฐ๋Š” ํŠธ๋žœ์žญ์…˜ ์•ˆ์— ๋“ค์–ด๊ฐ€๋ฉด ์•ˆ ๋œ๋‹ค
  • ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ: DB์™€ ๋ฌด๊ด€ํ•œ ์—ฐ์‚ฐ์€ ํŠธ๋žœ์žญ์…˜ ๋ฐ–์—์„œ ๋๋‚ด๊ณ  ๊ฒฐ๊ณผ๋งŒ ๋“ค๊ณ  ๋“ค์–ด๊ฐ„๋‹ค
  • ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์ฒ˜๋ฆฌ: ์—…๋กœ๋“œ์™€ ํŒŒ์‹ฑ์€ ๋ฐ–์—์„œ ํ•œ๋‹ค

ํŠธ๋žœ์žญ์…˜์ด ์—ด๋ ค ์žˆ๋Š” ๋™์•ˆ ๋ถ™์žก๊ณ  ๋†“์ง€ ์•Š๋Š” ์ž์›์ด ์…‹์ด๋‹ค.

  • ์ปค๋„ฅ์…˜: ํ’€์—์„œ ํ•˜๋‚˜๊ฐ€ ๊ณ„์† ๋น ์ ธ ์žˆ๋‹ค
  • ๋ฝ: ๊ฐ™์€ ํ–‰์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ํŠธ๋žœ์žญ์…˜์ด ์ค„์„ ์„ ๋‹ค
  • ์Šค๋ƒ…์ƒท: ์ด ํŠธ๋žœ์žญ์…˜์ด ๋ณผ์ง€ ๋ชจ๋ฅด๋Š” ์˜› ๋ฒ„์ „์„ ๋ชป ์ง€์šด๋‹ค

์ฃผ์˜: idle in transaction์ด ๋‚จ๊ธฐ๋Š” ๊ฒƒ

  • ํŠธ๋žœ์žญ์…˜์„ ์—ด๊ณ  ์•„๋ฌด ์ผ๋„ ์•ˆ ํ•˜๋Š” ์ƒํƒœ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์™ธ๋ถ€ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ค‘์ด๊ฑฐ๋‚˜ ์ปค๋ฐ‹์„ ์žŠ์—ˆ์„ ๋•Œ ์ƒ๊ธด๋‹ค
  • PostgreSQL์—์„œ๋Š” `VACUUM`์ด ์ฃฝ์€ ํ–‰์„ ํšŒ์ˆ˜ํ•˜์ง€ ๋ชปํ•œ๋‹ค. ์ด ์„ธ์…˜์ด ์˜› ๋ฒ„์ „์„ ๋ณผ์ง€๋„ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค
  • ์„ธ์…˜ ํ•˜๋‚˜๊ฐ€ ํ…Œ์ด๋ธ” ์ „์ฒด์˜ ํšŒ์ˆ˜๋ฅผ ๋ง‰๋Š”๋‹ค. ๊ฐฑ์‹ ์ด ์žฆ์€ ํ…Œ์ด๋ธ”์ด๋ฉด ๋ฉฐ์น  ๋งŒ์— ํฌ๊ธฐ๊ฐ€ ๋ช‡ ๋ฐฐ๊ฐ€ ๋œ๋‹ค
  • ์ปค๋„ฅ์…˜ ํ’€๋„ ํ•จ๊ป˜ ๋งˆ๋ฅธ๋‹ค. ํ’€ ํฌ๊ธฐ๊ฐ€ 20์ธ๋ฐ 5๊ฐœ๊ฐ€ ์ด ์ƒํƒœ๋ฉด ์‹ค์งˆ ๋™์‹œ์„ฑ์ด 15๋‹ค
  • idle_in_transaction_session_timeout์œผ๋กœ ๊ฐ•์ œ ์ข…๋ฃŒ๋ฅผ ๊ฑธ์–ด๋‘”๋‹ค

4. ์‹คํŒจํ•œ ๋’ค์— ๋ฒŒ์–ด์ง€๋Š” ์ผ

ํŠธ๋žœ์žญ์…˜ ์ค‘๊ฐ„์— ์—๋Ÿฌ๊ฐ€ ๋‚˜๋ฉด ๊ทธ๋‹ค์Œ ๋™์ž‘์ด DB๋งˆ๋‹ค ๋‹ค๋ฅด๋‹ค.

PostgreSQL์€ ํŠธ๋žœ์žญ์…˜ ์ „์ฒด๋ฅผ ์‹คํŒจ ์ƒํƒœ๋กœ ๋Œ๋ฆฐ๋‹ค.

ERROR:  duplicate key value violates unique constraint "users_email_key"
 
-- ์ดํ›„ ์–ด๋–ค ๋ฌธ์žฅ์„ ๋ณด๋‚ด๋„
ERROR:  current transaction is aborted,
        commands ignored until end of transaction block

์ฒซ ์—๋Ÿฌ ์ดํ›„ ๋ชจ๋“  ๋ฌธ์žฅ์ด ๊ฑฐ๋ถ€๋œ๋‹ค. ROLLBACK์ด๋‚˜ COMMIT์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ๋‹ซ๊ธฐ ์ „๊นŒ์ง€ ์•„๋ฌด๊ฒƒ๋„ ๋ชป ํ•œ๋‹ค. ๋กœ๊ทธ์— ๋‘ ๋ฒˆ์งธ ๋ฉ”์‹œ์ง€๋งŒ ์ž”๋œฉ ์Œ“์—ฌ ์›์ธ์„ ๋ชป ์ฐพ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์—ฌ๊ธฐ์„œ ์ƒ๊ธด๋‹ค.

MySQL์€ ๋‹ค๋ฅด๋‹ค. ๋ฌธ์žฅ ํ•˜๋‚˜๋งŒ ์‹คํŒจํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜์€ ์‚ด์•„ ์žˆ์–ด ๋‹ค์Œ ๋ฌธ์žฅ์ด ๊ทธ๋Œ€๋กœ ์‹คํ–‰๋œ๋‹ค.

DB์—๋Ÿฌ 1๊ฑด์ด ๋‚ฌ์„ ๋•Œ
PostgreSQLํŠธ๋žœ์žญ์…˜ ์ „์ฒด๊ฐ€ aborted. ๋‚˜๋จธ์ง€ ์ „๋ถ€ ๊ฑฐ๋ถ€
MySQL๊ทธ ๋ฌธ์žฅ๋งŒ ์‹คํŒจ. ๋‚˜๋จธ์ง€๋Š” ๊ณ„์† ์‹คํ–‰

MySQL์—์„œ ๋Œ๋˜ ๋ฐฐ์น˜๋ฅผ PostgreSQL๋กœ ์˜ฎ๊ธฐ๋ฉด ์—ฌ๊ธฐ์„œ ๊นจ์ง„๋‹ค. โ€œ์‹คํŒจํ•œ ๊ฑด์€ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๊ณ„์†โ€์ด ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค.

SAVEPOINT๋กœ ํ•œ ๊ฑด๋งŒ ๋ฒ„๋ฆฌ๊ธฐ

๋ฐ˜๋ณต ์ ์žฌ์—์„œ ์ผ๋ถ€ ์‹คํŒจ๋ฅผ ํก์ˆ˜ํ•˜๋ ค๋ฉด SAVEPOINT๋ฅผ ์“ด๋‹ค.

BEGIN;
SAVEPOINT sp;
INSERT INTO users VALUES (...);       -- ์ค‘๋ณต ํ‚ค๋กœ ์‹คํŒจ
ROLLBACK TO SAVEPOINT sp;             -- ์ด ๊ฑด๋งŒ ์ทจ์†Œ. ํŠธ๋žœ์žญ์…˜์€ ์‚ด์•„๋‚œ๋‹ค
RELEASE SAVEPOINT sp;
 
SAVEPOINT sp;
INSERT INTO users VALUES (...);       -- ๋‹ค์Œ ๊ฑด์€ ์ •์ƒ ์ง„ํ–‰
RELEASE SAVEPOINT sp;
COMMIT;

ROLLBACK TO SAVEPOINT๊ฐ€ aborted ์ƒํƒœ๋ฅผ ๊ทธ ์ง€์ ๊นŒ์ง€ ๋˜๊ฐ์•„์ค€๋‹ค. PostgreSQL์—์„œ โ€œ์‹คํŒจํ•œ ๊ฑด๋งŒ ๊ฑด๋„ˆ๋›ฐ๊ธฐโ€๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

SAVEPOINT: ์ค‘์ฒฉ ํŠธ๋žœ์žญ์…˜์ด ์•„๋‹ˆ๋‹ค

  • ํŠธ๋žœ์žญ์…˜ ์•ˆ์— ํŠธ๋žœ์žญ์…˜์„ ๋งŒ๋“œ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋˜๊ฐ์„ ์ง€์ ์„ ํ‘œ์‹œํ•ด๋‘๋Š” ๊ฒƒ์ด๋‹ค
  • RELEASEํ•ด๋„ ์ปค๋ฐ‹๋˜์ง€ ์•Š๋Š”๋‹ค. ํ™•์ •์€ ๋ฐ”๊นฅ COMMIT ์‹œ์ ์— ํ•œ๊บผ๋ฒˆ์— ์ผ์–ด๋‚œ๋‹ค
  • ๋ฐ”๊นฅ์ด ๋กค๋ฐฑ๋˜๋ฉด ์•ˆ์—์„œ ์„ฑ๊ณตํ•œ ๊ฒƒ๋„ ์ „๋ถ€ ์‚ฌ๋ผ์ง„๋‹ค
  • ORM์˜ ์ค‘์ฒฉ atomic() ๋ธ”๋ก์ด ๋‚ด๋ถ€์ ์œผ๋กœ ์ด๊ฑธ ์“ด๋‹ค

์ฃผ์˜: SAVEPOINT๋ฅผ ๊ฑด๋งˆ๋‹ค ์žก์œผ๋ฉด

  • ๋ฐ˜๋ณต ํšŸ์ˆ˜๋งŒํผ ์„œ๋ฒ„ ์ž์›์ด ์Œ“์ธ๋‹ค. ์ˆ˜๋งŒ ๊ฑด ๋ฃจํ”„์—์„œ๋Š” ๋ถ€๋‹ด์ด ๋ˆˆ์— ๋ˆ๋‹ค
  • ์‹คํŒจ๊ฐ€ ๋“œ๋ฌผ๋ฉด SAVEPOINT ์—†์ด ๊ฐ€๋‹ค๊ฐ€ ์—๋Ÿฌ๊ฐ€ ๋‚ฌ์„ ๋•Œ๋งŒ ํŠธ๋žœ์žญ์…˜์„ ๋‹ค์‹œ ์—ฌ๋Š” ํŽธ์ด ์‹ธ๋‹ค
  • ๋Œ€๋Ÿ‰ ์ ์žฌ๋ผ๋ฉด INSERT ... ON CONFLICT DO NOTHING์œผ๋กœ ์• ์ดˆ์— ์‹คํŒจ๋ฅผ ์•ˆ ๋งŒ๋“œ๋Š” ์ชฝ์„ ๋จผ์ € ๋ณธ๋‹ค

5. ๋กค๋ฐฑํ•ด๋„ ๋Œ์•„์˜ค์ง€ ์•Š๋Š” ๊ฒƒ

ROLLBACK์€ ํŠธ๋žœ์žญ์…˜ ์•ˆ์˜ ๋ณ€๊ฒฝ๋งŒ ๋˜๋Œ๋ฆฐ๋‹ค. ๋ฐ–์— ์žˆ๋Š” ๊ฒƒ์€ ๊ทธ๋Œ€๋กœ ๋‚จ๋Š”๋‹ค.

์‹œํ€€์Šค

์‹œํ€€์Šค: ๋ฒˆํ˜ธ๋ฅผ ํ•˜๋‚˜์”ฉ ๋ฐœ๊ธ‰ํ•˜๋Š” ์นด์šดํ„ฐ

  • ํ…Œ์ด๋ธ”ยท์ธ๋ฑ์Šค์™€ ๊ฐ™์€ ๊ธ‰์˜ DB ๊ฐ์ฒด๋‹ค. ์•ˆ์— ๋“  ๊ฒƒ์€ โ€œ๋ช‡ ๋ฒˆ๊นŒ์ง€ ๋‚˜๊ฐ”๋Š”์ง€โ€ ํ•˜๋‚˜๋ฟ์ด๋‹ค
  • nextval()์ด ๋‹ค์Œ ๋ฒˆํ˜ธ๋ฅผ ๊บผ๋‚ด๋ฉด์„œ ์นด์šดํ„ฐ๋ฅผ ์˜ฌ๋ฆฐ๋‹ค. currval()์€ ์ด ์„ธ์…˜์ด ๋งˆ์ง€๋ง‰์— ๋ฐ›์€ ๊ฐ’์„ ๋ณด์—ฌ์ค„ ๋ฟ ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค
  • bigserial์ด๋‚˜ GENERATED ALWAYS AS IDENTITY๋กœ ์ปฌ๋Ÿผ์„ ๋งŒ๋“ค๋ฉด ์‹œํ€€์Šค๊ฐ€ ๋”ธ๋ ค ๋ถ™๋Š”๋‹ค. orders ํ…Œ์ด๋ธ”์˜ id๋ฉด ์ด๋ฆ„์ด orders_id_seq๋‹ค
  • INSERT์—์„œ id๋ฅผ ๋„˜๊ธฐ์ง€ ์•Š์œผ๋ฉด DB๊ฐ€ ๋Œ€์‹  nextval์„ ๋ถ€๋ฅธ๋‹ค. ์ง์ ‘ ์น  ์ผ์€ ๊ฑฐ์˜ ์—†๋‹ค
BEGIN;
SELECT nextval('orders_id_seq');   -- 42
ROLLBACK;
 
SELECT nextval('orders_id_seq');   -- 43. 42๋Š” ์˜์˜ ์‚ฌ๋ผ์กŒ๋‹ค

PostgreSQL ์‹œํ€€์Šค ํ•จ์ˆ˜ ๋ฌธ์„œ๊ฐ€ ์ด์œ ๋ฅผ ์ ์–ด๋†“์•˜๋‹ค.

the value obtained by nextval is not reclaimed for re-use if the calling transaction later aborts

๋˜๋Œ๋ฆฌ๋ ค๋ฉด ์‹œํ€€์Šค์— ๋ฝ์„ ๊ฑธ์–ด์•ผ ํ•˜๋Š”๋ฐ ๊ทธ๋Ÿฌ๋ฉด ๋ฒˆํ˜ธ๋ฅผ ๋ฐ›์œผ๋ ค๋Š” ํŠธ๋žœ์žญ์…˜์ด ์ „๋ถ€ ์ค„์„ ์„ ๋‹ค. ๋™์‹œ์„ฑ์„ ์œ„ํ•ด ์ผ๋ถ€๋Ÿฌ ํŠธ๋žœ์žญ์…˜ ๋ฐ–์— ๋’€๋‹ค.

MySQL์˜ AUTO_INCREMENT๋„ ๊ฐ™๋‹ค.

์ฃผ์˜: ์—ฐ์†๋œ ๋ฒˆํ˜ธ๋ฅผ ์š”๊ตฌํ•˜๋Š” ์š”๊ฑด

  • ๋ฌธ์„œ์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด PostgreSQL ์‹œํ€€์Šค๋กœ๋Š” gapless ๋ฒˆํ˜ธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†๋‹ค
  • ๋กค๋ฐฑ๋ฟ ์•„๋‹ˆ๋ผ INSERT ... ON CONFLICT์—์„œ๋„ ๊ตฌ๋ฉ์ด ์ƒ๊ธด๋‹ค. ์ถฉ๋Œ์„ ๊ฐ์ง€ํ•˜๊ธฐ ์ „์— nextval์„ ์ด๋ฏธ ๋ถ€๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค
  • ์„ธ๊ธˆ๊ณ„์‚ฐ์„œ ๋ฒˆํ˜ธ์ฒ˜๋Ÿผ ๋น ์ง์—†์ด ์ด์–ด์ ธ์•ผ ํ•˜๋Š” ๊ฐ’์€ ๋ณ„๋„ ํ…Œ์ด๋ธ”์— ๋ฝ์„ ๊ฑธ๊ณ  ๋ฐœ๊ธ‰ํ•˜๊ฑฐ๋‚˜ ์ปค๋ฐ‹ ์ดํ›„ ํ›„์ฒ˜๋ฆฌ๋กœ ๋งค๊ธด๋‹ค
  • PK์— ๊ตฌ๋ฉ์ด ์ƒ๊ธฐ๋Š” ๊ฒƒ ์ž์ฒด๋Š” ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋‹ค. ๊ทธ๊ฑธ ์—…๋ฌด ๋ฒˆํ˜ธ๋กœ ๋…ธ์ถœํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋œ๋‹ค

์™ธ๋ถ€ ๋ถ€์ˆ˜ ํšจ๊ณผ

ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฒ„ํ•˜๋Š” ๋ฒ”์œ„๋Š” ๊ทธ DB ์•ˆ๊นŒ์ง€๋‹ค.

๋™์ž‘๋กค๋ฐฑ๋˜๋‚˜
INSERT ยท UPDATE ยท DELETE๋œ๋‹ค
์‹œํ€€์Šค ์†Œ๋น„์•ˆ ๋œ๋‹ค
์™ธ๋ถ€ API ํ˜ธ์ถœ์•ˆ ๋œ๋‹ค
ํ ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰์•ˆ ๋œ๋‹ค
๋ฉ”์ผ ๋ฐœ์†ก์•ˆ ๋œ๋‹ค
ํŒŒ์ผ ์“ฐ๊ธฐ์•ˆ ๋œ๋‹ค

๋Œ€์‘์€ ๋‘ ๊ฐˆ๋ž˜๋‹ค.

  • ์ปค๋ฐ‹ ์ดํ›„๋กœ ๋ฏธ๋ฃฌ๋‹ค: ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๋‹ค. ๋‹ค๋งŒ ์ปค๋ฐ‹๊ณผ ํ›„์† ๋™์ž‘ ์‚ฌ์ด์—์„œ ์ฃฝ์œผ๋ฉด ์œ ์‹ค๋œ๋‹ค
  • outbox๋ฅผ ์“ด๋‹ค: ๋ณด๋‚ผ ๋‚ด์šฉ์„ ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ํ…Œ์ด๋ธ”์— ์ ์–ด๋‘๊ณ  ๋ณ„๋„ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฝ์–ด ๋ฐœ์†กํ•œ๋‹ค. ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋˜๋ฉด ๋ฐœ์†ก ๋Œ€์ƒ๋„ ํ•จ๊ป˜ ์‚ฌ๋ผ์ง„๋‹ค
# ์ปค๋ฐ‹ ์•ˆ์—์„œ: ๋ณด๋‚ผ ๋‚ด์šฉ๋งŒ ๊ธฐ๋ก
with conn.transaction():
    create_order(...)
    insert_outbox(topic="order.created", payload=...)
 
# ๋ณ„๋„ ์›Œ์ปค๊ฐ€ outbox๋ฅผ ์ฝ์–ด ์‹ค์ œ๋กœ ๋ฐœ์†ก

Transactional Outbox: ํ…Œ์ด๋ธ” ํ•˜๋‚˜๋กœ ๋งŒ๋“œ๋Š” ์„ค๊ณ„ ํŒจํ„ด

  • ๊ตฌํ˜„์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ชซ์ด๋‹ค. ๋ฐœ์†ก ๋Œ€๊ธฐ ํ…Œ์ด๋ธ”๊ณผ ๊ทธ๊ฒƒ์„ ์ฝ์–ด ๋ณด๋‚ด๋Š” ์›Œ์ปค๋ฅผ ์ง์ ‘ ๋งŒ๋“ ๋‹ค
  • ํ•„์š”ํ•œ ๊ฒƒ์ด ํ…Œ์ด๋ธ”๊ณผ ํŠธ๋žœ์žญ์…˜๋ฟ์ด๋ผ PostgreSQLยทMySQLยทOracle ์–ด๋””์„œ๋‚˜ ๊ฐ™๊ฒŒ ๋งŒ๋“ ๋‹ค
  • ์›Œ์ปค๋ฅผ ์—ฌ๋Ÿฌ ๋Œ€ ๋„์šฐ๋ ค๋ฉด FOR UPDATE SKIP LOCKED๋กœ ์ง‘์–ด๊ฐ„๋‹ค. ๊ฐ™์€ ํ–‰์„ ๋‘ ๋ฒˆ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค
  • ๋ฐœ์†ก์— ์„ฑ๊ณตํ•˜๊ณ  ์™„๋ฃŒ ํ‘œ์‹œ๋ฅผ ๋‚จ๊ธฐ๊ธฐ ์ „์— ์ฃฝ์œผ๋ฉด ๋‹ค์‹œ ๋ณด๋‚ธ๋‹ค. ๋ฐ›๋Š” ์ชฝ์—์„œ ์ค‘๋ณต์„ ๊ฑธ๋Ÿฌ์•ผ ํ•œ๋‹ค

6. DDL๊ณผ ํŠธ๋žœ์žญ์…˜

์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์„ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์„ ์ˆ˜ ์žˆ๋Š”์ง€๊ฐ€ DB๋งˆ๋‹ค ๊ฐˆ๋ฆฐ๋‹ค.

PostgreSQL์€ DDL๋„ ๋กค๋ฐฑ๋œ๋‹ค.

BEGIN;
ALTER TABLE users ADD COLUMN phone text;
UPDATE users SET phone = '';
-- ์—ฌ๊ธฐ์„œ ์‹คํŒจํ•˜๋ฉด
ROLLBACK;   -- ์ปฌ๋Ÿผ ์ถ”๊ฐ€๊นŒ์ง€ ์—†๋˜ ์ผ์ด ๋œ๋‹ค

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „์ฒด๋ฅผ ํ•œ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์œผ๋ฉด ์ค‘๊ฐ„์— ์‹คํŒจํ•ด๋„ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค. ๋Š๊ธด ์Šคํ‚ค๋งˆ๊ฐ€ ๋‚จ์ง€ ์•Š๋Š”๋‹ค.

MySQL์€ DDL์ด ์•”๋ฌต์  ์ปค๋ฐ‹์„ ์ผ์œผํ‚จ๋‹ค.

MySQL ๋ฌธ์„œ ํ‘œํ˜„์€ ์ด๋ ‡๋‹ค.

implicitly end any transaction active in the current session, as if you had done a COMMIT before executing the statement

START TRANSACTION;
INSERT INTO users VALUES (...);     -- ์•„์ง ์ปค๋ฐ‹ ์•ˆ ๋จ
ALTER TABLE users ADD COLUMN phone text;
-- ์ด ์‹œ์ ์— ์•ž์˜ INSERT๊นŒ์ง€ ํ•จ๊ป˜ ํ™•์ •๋œ๋‹ค
ROLLBACK;                            -- ๋˜๋Œ๋ฆด ๊ฒƒ์ด ์—†๋‹ค

CREATEยทALTERยทDROPยทTRUNCATE ๊ณ„์—ด ์ „๋ถ€๊ฐ€ ํ•ด๋‹นํ•œ๋‹ค. GRANTยทREVOKE ๊ฐ™์€ ๊ถŒํ•œ ๋ฌธ์žฅ๊ณผ LOCK TABLES๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค.

์ฃผ์˜: MySQL์—์„œ ๋ฐ˜์ฏค ์ ์šฉ๋œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

  • ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์ค‘๊ฐ„์— ์‹คํŒจํ•˜๋ฉด ์•ž ๋‹จ๊ณ„๋Š” ํ™•์ •๋˜๊ณ  ๋’ค๋Š” ์•ˆ ๋œ ์ƒํƒœ๋กœ ๋ฉˆ์ถ˜๋‹ค
  • ๋˜๋Œ๋ฆฌ๋ ค๋ฉด ์—ญ๋ฐฉํ–ฅ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค. ์ž๋™ ๋กค๋ฐฑ์ด ์—†๋‹ค
  • ๊ทธ๋ž˜์„œ MySQL ์ชฝ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋„๊ตฌ๋Š” ๋‹จ๊ณ„๋ฅผ ์ž˜๊ฒŒ ์ชผ๊ฐœ๊ณ  ๊ฐ ๋‹จ๊ณ„๋ฅผ ๋ฉฑ๋“ฑํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ„๋‹ค
  • START TRANSACTION๊ณผ SET autocommit = 1 ์ž์ฒด๋„ ์•”๋ฌต์  ์ปค๋ฐ‹์„ ์ผ์œผํ‚จ๋‹ค. ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ๋‹ค์‹œ START TRANSACTION์„ ์น˜๋ฉด ์•ž์˜ ๊ฒƒ์ด ์ปค๋ฐ‹๋œ๋‹ค

DDL์ด ์žก๋Š” ๋ฝ์€ ์„ฑ๊ฒฉ์ด ๋˜ ๋‹ฌ๋ผ์„œ PostgreSQL์—์„œ๋„ ALTER TABLE ํ•œ ์ค„์ด ์„œ๋น„์Šค๋ฅผ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ๋‹ค.


7. ORM์—์„œ์˜ ๊ฒฝ๊ณ„

ORM์€ ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„๋ฅผ ์ˆจ๊ธด๋‹ค. ํŽธํ•˜์ง€๋งŒ ์–ด๋””์„œ ์—ด๋ฆฌ๊ณ  ๋‹ซํžˆ๋Š”์ง€ ๋ชจ๋ฅด๋ฉด ์•ž์˜ ํ•จ์ •์„ ๊ทธ๋Œ€๋กœ ๋ฐŸ๋Š”๋‹ค.

flush์™€ commit์€ ๋‹ค๋ฅด๋‹ค

session.add(order)
session.flush()    # SQL์ด ์‹คํ–‰๋๋‹ค. ํŠธ๋žœ์žญ์…˜์€ ์•„์ง ์—ด๋ ค ์žˆ๋‹ค
                   # order.id๋Š” ์ฑ„์›Œ์ง„๋‹ค. ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ๋Š” ์•ˆ ๋ณด์ธ๋‹ค
session.commit()   # ์—ฌ๊ธฐ์„œ ํ™•์ •๋œ๋‹ค

flush๋Š” ํŒŒ์ด์ฌ ๊ฐ์ฒด ์ƒํƒœ๋ฅผ SQL๋กœ ๋งŒ๋“ค์–ด ์‹คํ–‰ํ•˜๋Š” ๋™์ž‘์ด๊ณ  commit์€ ํŠธ๋žœ์žญ์…˜์„ ๋‹ซ๋Š” ๋™์ž‘์ด๋‹ค. ์ž๋™ ์ƒ์„ฑ๋œ ID๊ฐ€ ํ•„์š”ํ•ด์„œ flush๋ฅผ ๋ถ€๋ฅด๊ณ ๋Š” ์ปค๋ฐ‹ํ–ˆ๋‹ค๊ณ  ์ฐฉ๊ฐํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

๊ฒฝ๊ณ„๋Š” ๋ธ”๋ก์œผ๋กœ ์ง€์ •ํ•œ๋‹ค

ํ”„๋ ˆ์ž„์›Œํฌ์ง€์ • ๋ฐฉ์‹
SQLAlchemywith session.begin(): ๋ธ”๋ก
Djangowith transaction.atomic(): ๋ธ”๋ก
Django (์š”์ฒญ ๋‹จ์œ„)ATOMIC_REQUESTS = True ์„ค์ •

ATOMIC_REQUESTS๋ฅผ ์ผœ๋ฉด ์š”์ฒญ ํ•˜๋‚˜๊ฐ€ ํŠธ๋žœ์žญ์…˜ ํ•˜๋‚˜๊ฐ€ ๋œ๋‹ค. ํŽธํ•˜์ง€๋งŒ ํŠธ๋žœ์žญ์…˜ ์•ˆ์— ์™ธ๋ถ€ ํ˜ธ์ถœ์ด ๋“ค์–ด๊ฐ€๋Š” ๋ฌธ์ œ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ฐœ์ƒํ•œ๋‹ค.

์ฃผ์˜: ์š”์ฒญ ๋‹จ์œ„ ํŠธ๋žœ์žญ์…˜๊ณผ ์™ธ๋ถ€ ํ˜ธ์ถœ

  • ATOMIC_REQUESTS๊ฐ€ ์ผœ์ง„ ์ƒํƒœ์—์„œ ๋ทฐ ์•ˆ์— ๊ฒฐ์ œ API ํ˜ธ์ถœ์ด ์žˆ์œผ๋ฉด ๊ทธ ์™•๋ณต ์‹œ๊ฐ„ ๋‚ด๋‚ด ํŠธ๋žœ์žญ์…˜์ด ์—ด๋ ค ์žˆ๋‹ค
  • ์™ธ๋ถ€ ํ˜ธ์ถœ์ด ์žˆ๋Š” ๋ทฐ๋Š” non_atomic_requests๋กœ ๋นผ๊ฑฐ๋‚˜ ํ˜ธ์ถœ์„ ์ปค๋ฐ‹ ์ดํ›„๋กœ ์˜ฎ๊ธด๋‹ค
  • ์„ธ์…˜์„ ์š”์ฒญ๋ณด๋‹ค ์˜ค๋ž˜ ์‚ด๋ ค๋‘๋ฉด ์ปค๋„ฅ์…˜์ด ํ’€๋กœ ๋Œ์•„๊ฐ€์ง€ ์•Š๋Š”๋‹ค

8. ์žฌ์‹œ๋„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ

ํŠธ๋žœ์žญ์…˜์ด ์‹คํŒจํ•˜๋Š” ๊ฒƒ ์ž์ฒด๋Š” ์ •์ƒ์ด๋‹ค. ๋ฌธ์ œ๋Š” ๋‹ค์‹œ ๋Œ๋ ธ์„ ๋•Œ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๋А๋ƒ๋‹ค.

์žฌ์‹œ๋„ํ•ด๋„ ๋˜๋Š” ์—๋Ÿฌ๋งŒ ์žฌ์‹œ๋„ํ•œ๋‹ค

SQLSTATE์˜๋ฏธ์žฌ์‹œ๋„
40001์ง๋ ฌํ™” ์‹คํŒจํ•œ๋‹ค
40P01๋ฐ๋“œ๋ฝํ•œ๋‹ค
23505์œ ๋‹ˆํฌ ์ œ์•ฝ ์œ„๋ฐ˜๋Œ€๊ฐœ ๋ฌด์˜๋ฏธ
23503์™ธ๋ž˜ ํ‚ค ์œ„๋ฐ˜๋ฌด์˜๋ฏธ
22P02ํƒ€์ž… ๋ณ€ํ™˜ ์‹คํŒจ๋ฌด์˜๋ฏธ

์•ž์˜ ๋‘˜์€ ๋‹ค์‹œ ํ•˜๋ฉด ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ์‹คํŒจ๋‹ค. ๋’ค์˜ ์…‹์€ ์ž…๋ ฅ์ด ์ž˜๋ชป๋œ ๊ฒƒ์ด๋ผ ๋ช‡ ๋ฒˆ์„ ๋Œ๋ ค๋„ ๊ฐ™๋‹ค. ๊ตฌ๋ถ„ ์—†์ด ์žฌ์‹œ๋„ ๋ฃจํ”„๋ฅผ ๋Œ๋ฆฌ๋ฉด ์ž˜๋ชป๋œ ์š”์ฒญ์— ๋ถ€ํ•˜๋งŒ ์ค€๋‹ค.

RETRYABLE = {"40001", "40P01"}
 
for _ in range(3):
    try:
        with conn.transaction():
            do_work()
        break
    except psycopg.Error as e:
        if e.sqlstate not in RETRYABLE:
            raise          # ์žฌ์‹œ๋„ํ•ด๋„ ๊ฐ™์€ ๊ฒฐ๊ณผ์ธ ์—๋Ÿฌ๋Š” ๋ฐ”๋กœ ์˜ฌ๋ฆฐ๋‹ค
else:
    raise RuntimeError("์žฌ์‹œ๋„ ํ•œ๋„ ์ดˆ๊ณผ")

์žฌ์‹œ๋„ ๋ฒ”์œ„๋Š” ํŠธ๋žœ์žญ์…˜ ์ „์ฒด๋‹ค

์ปค๋ฐ‹๋งŒ ๋‹ค์‹œ ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์€ ์˜๋ฏธ๊ฐ€ ์—†๋‹ค. ์ฝ์€ ๊ฐ’ ์ž์ฒด๊ฐ€ ๋ฌดํšจ๋ผ์„œ ์ฝ๊ธฐ๋ถ€ํ„ฐ ๋‹ค์‹œ ํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์žฌ์‹œ๋„ ๋ฃจํ”„๊ฐ€ ํŠธ๋žœ์žญ์…˜ ๋ธ”๋ก ๋ฐ”๊นฅ์— ์žˆ์–ด์•ผ ํ•œ๋‹ค.

๋ถ€์ˆ˜ ํšจ๊ณผ๊ฐ€ ์žˆ์œผ๋ฉด ์žฌ์‹œ๋„๊ฐ€ ์œ„ํ—˜ํ•ด์ง„๋‹ค

์•ž์„œ ๋ณธ ๋ถ€์ˆ˜ ํšจ๊ณผ ๋ฌธ์ œ์™€ ์ด์–ด์ง„๋‹ค. ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ–ˆ๋‹ค๋ฉด ์žฌ์‹œ๋„ํ•  ๋•Œ๋งˆ๋‹ค ๊ทธ ํ˜ธ์ถœ์ด ๋ฐ˜๋ณต๋œ๋‹ค.

# ์œ„ํ—˜: ์žฌ์‹œ๋„ํ•  ๋•Œ๋งˆ๋‹ค ๊ฒฐ์ œ๊ฐ€ ๋‹ค์‹œ ์ผ์–ด๋‚œ๋‹ค
with conn.transaction():
    charge_payment(...)      # ์™ธ๋ถ€ ํ˜ธ์ถœ
    create_order(...)
 
# ์•ˆ์ „: ์™ธ๋ถ€ ํ˜ธ์ถœ์€ ๋ฐ–์—์„œ ํ•œ ๋ฒˆ๋งŒ
payment_id = charge_payment(...)      # ๋ฉฑ๋“ฑ ํ‚ค๋ฅผ ํ•จ๊ป˜ ๋ณด๋‚ธ๋‹ค
 
for _ in range(3):                    # ์žฌ์‹œ๋„ํ•ด๋„ ๊ฒฐ์ œ๋Š” ๋ฐ˜๋ณต๋˜์ง€ ์•Š๋Š”๋‹ค
    try:
        with conn.transaction():
            create_order(payment_id=payment_id)
        break
    except psycopg.Error as e:
        if e.sqlstate not in RETRYABLE:
            raise

์žฌ์‹œ๋„๋ฅผ ์ „์ œ๋กœ ์„ค๊ณ„ํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜ ์•ˆ์€ DB ์ž‘์—…๋งŒ ๋‚จ๋Š”๋‹ค. ์™ธ๋ถ€ ํ˜ธ์ถœ์„ ํŠธ๋žœ์žญ์…˜ ๋ฐ–์œผ๋กœ ๋นผ์•ผ ํ•˜๋Š” ์ด์œ ๊ฐ€ ํ•˜๋‚˜ ๋” ๋Š˜์–ด๋‚œ๋‹ค.

Serializable์„ ์“ฐ๋ฉด ์žฌ์‹œ๋„๊ฐ€ ์„ ํƒ์ด ์•„๋‹ˆ๋ผ ํ•„์ˆ˜๊ฐ€ ๋œ๋‹ค. ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ์˜ฌ๋ฆฌ๊ธฐ ์ „์— ์ด ๊ตฌ์กฐ๋ถ€ํ„ฐ ๊ฐ–์ถฐ๋‘”๋‹ค.