[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-10 22:08:15 +09:00
parent 21ac3ed255
commit 504fd5fb42
3011 changed files with 380280 additions and 206977 deletions
@@ -0,0 +1,365 @@
---
id: db-transaction-patterns-deep
title: DB Transaction Deep — isolation / SAVEPOINT / advisory lock
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [database, transaction, vibe-coding]
tech_stack: { language: "SQL", applicable_to: ["Database"] }
applied_in: []
aliases: [transaction, isolation, savepoint, advisory lock, nested transaction, explicit lock]
---
# DB Transaction Deep
> ACID 의 정밀 control. **Isolation, SAVEPOINT, advisory lock, FOR UPDATE**.
## 📖 핵심 개념
- ACID: Atomic, Consistent, Isolated, Durable.
- Isolation level (4 가지).
- SAVEPOINT = nested transaction.
- Advisory lock = app-level.
## 💻 코드 패턴
### Basic transaction
```sql
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 또는 ROLLBACK;
```
### Isolation levels
```
READ UNCOMMITTED: dirty read 가능 (Postgres 가 안 implement).
READ COMMITTED: dirty read 안 (Postgres default).
REPEATABLE READ: phantom 안 (range query OK).
SERIALIZABLE: 가장 strict (slow).
```
```sql
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
-- ...
COMMIT;
```
### SAVEPOINT (nested)
```sql
BEGIN;
INSERT INTO orders (...) VALUES (...);
SAVEPOINT after_order;
INSERT INTO items (...) VALUES (...);
-- Error
ROLLBACK TO after_order;
-- 매 order 보존, items 만 rollback.
COMMIT;
```
→ Partial rollback.
### Explicit lock (FOR UPDATE)
```sql
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- Other transaction 가 wait.
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
```
→ Exclusive row lock.
### FOR UPDATE SKIP LOCKED (queue)
```sql
SELECT * FROM jobs
WHERE status = 'pending'
ORDER BY created_at
LIMIT 1
FOR UPDATE SKIP LOCKED;
-- → Lock 된 row 가 skip. Multi-worker queue.
```
### FOR SHARE
```sql
SELECT * FROM users WHERE id = 1 FOR SHARE;
-- → Shared lock. Read 가 OK, write 가 wait.
```
### Advisory lock (app-level)
```sql
-- Acquire
SELECT pg_advisory_lock(12345);
-- Critical section
-- ...
-- Release
SELECT pg_advisory_unlock(12345);
```
→ Cross-transaction. Cron / migration 친화.
### Transaction-scoped advisory
```sql
SELECT pg_advisory_xact_lock(12345);
-- → Transaction 끝 시 자동 release.
```
### Distributed lock (Postgres)
```ts
async function withDistributedLock<T>(key: number, fn: () => Promise<T>): Promise<T> {
const client = await pool.connect();
try {
await client.query('SELECT pg_advisory_lock($1)', [key]);
return await fn();
} finally {
await client.query('SELECT pg_advisory_unlock($1)', [key]);
client.release();
}
}
```
→ Single Postgres = 작은 system 의 distributed lock.
### Optimistic locking (version)
```sql
-- Version column
UPDATE users
SET name = 'Bob', version = version + 1
WHERE id = 1 AND version = 5;
-- Affected rows = 0 → conflict.
```
→ Lock 없음. Retry on conflict.
### Read-write split (transaction)
```ts
// Read replica
const reader = pool.replica;
const writer = pool.primary;
// Write transaction
await writer.transaction(async (tx) => {
await tx.query('UPDATE ...');
});
// Read after write 의 함정:
// Write tx commit → replica 에 lag.
// → Read 가 stale.
```
→ Sticky reads.
### Long transaction 의 함정
```sql
BEGIN;
SELECT * FROM users LIMIT 1;
-- 1 hour 후
COMMIT;
```
→ Vacuum 가 freeze X. Bloat 누적.
```sql
-- Auto kill
SET statement_timeout = '30s';
SET idle_in_transaction_session_timeout = '60s';
```
### Deadlock
```
Tx A: lock row 1, wait row 2.
Tx B: lock row 2, wait row 1.
→ Postgres detect + abort 1.
```
```ts
try {
await tx.query(...);
} catch (e) {
if (e.code === '40P01') {
// Deadlock — retry.
}
}
```
### Deadlock 방지
```
- 모든 transaction 가 같은 order 로 lock.
- Short transaction.
- 최소 lock.
```
### Two-phase commit (XA, 2PC)
```sql
PREPARE TRANSACTION 'tx-1';
-- 다른 system 도 PREPARE.
-- Coordinator 가 모두 OK.
COMMIT PREPARED 'tx-1';
-- 또는
ROLLBACK PREPARED 'tx-1';
```
→ Cross-DB. 매우 느린. 거의 안 사용.
→ [[Backend_Saga_Choreography_vs_Orchestration]] 가 modern.
### Idempotent transaction
```sql
INSERT INTO transactions (id, ...) VALUES ($idempKey, ...)
ON CONFLICT (id) DO NOTHING;
-- Retry-safe.
```
### Outbox pattern
```sql
BEGIN;
INSERT INTO orders (...);
INSERT INTO outbox (event_type, payload) VALUES ('OrderCreated', ...);
COMMIT;
-- Background process: outbox → publish.
```
→ DB write + event 가 atomic.
→ [[Backend_Outbox_Pattern]].
### MVCC (Multi-Version Concurrency Control)
```
Postgres / MySQL InnoDB:
- 매 row 가 version 가 가짐.
- Read 가 lock X (snapshot).
- Write 가 새 version.
→ "Reader 가 writer 가 막지 않음".
```
→ [[CS_MVCC_Concurrency]].
### Connection pool + transaction
```ts
// 매 transaction 가 dedicated connection.
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query('UPDATE ...');
await client.query('COMMIT');
} finally {
client.release();
}
// 또는 helper
await pool.transaction(async (tx) => {
await tx.query('UPDATE ...');
});
```
### PgBouncer 함정 (transaction mode)
```
PgBouncer transaction mode:
- 매 transaction = 다른 connection.
- Session-bound feature 안 됨 (LISTEN, prepared, advisory lock).
→ Session mode 또는 raw query.
```
→ [[DB_Connection_Pooling_Patterns]].
### Phantom read (REPEATABLE READ 가 막음)
```
Tx A: SELECT count(*) FROM users WHERE age > 18 → 100.
Tx B: INSERT new user (age 20).
Tx A: SELECT count(*) → 101 (REPEATABLE READ X) / 100 (REPEATABLE READ).
```
### Serializable conflict
```sql
-- Postgres detect + abort.
ERROR: could not serialize access due to concurrent update
SQLSTATE: 40001
```
→ Retry on 40001.
### Performance
```
SERIALIZABLE: 가장 slow.
REPEATABLE READ: 약간 slow.
READ COMMITTED: default + 빠름.
→ Use case 의 적절.
```
### Real-world tip
```
1. Default = READ COMMITTED.
2. Transfer / financial = SERIALIZABLE 또는 explicit lock.
3. Long read = SET TRANSACTION READ ONLY.
4. Idempotency key 매 critical write.
5. Optimistic lock + version (web app).
6. Advisory lock (distributed lock).
```
### Distributed transaction (saga)
```
Cross-DB / cross-service:
- 2PC 안 사용 (slow, brittle).
- Saga + compensation.
- Idempotent step.
```
→ [[Backend_Saga_Choreography_vs_Orchestration]].
### 함정
```
- Long transaction: bloat / lock contention.
- Implicit transaction: ORM 의 auto-commit 무시.
- Lock order 다름: deadlock.
- Retry 무한: 내려.
- 2PC: 느린, 거의 안 사용.
```
## 🤔 의사결정 기준
| 작업 | 추천 |
|---|---|
| Read | Default isolation |
| Critical write | SERIALIZABLE / FOR UPDATE |
| Queue | FOR UPDATE SKIP LOCKED |
| Web app | Optimistic + version |
| Distributed lock | pg_advisory_lock |
| Cross-service | Saga (NOT 2PC) |
| Idempotent | Idempotency key |
## ❌ 안티패턴
- **Long transaction**: bloat.
- **2PC**: brittle.
- **No retry on serialize fail**: lost.
- **모든 거 SERIALIZABLE**: slow.
- **Lock order inconsistent**: deadlock.
- **No timeout**: hang.
## 🤖 LLM 활용 힌트
- READ COMMITTED 가 default.
- FOR UPDATE SKIP LOCKED 가 queue 의 답.
- Advisory lock 가 app-level.
- Saga > 2PC.
## 🔗 관련 문서
- [[DB_Transaction_Isolation]]
- [[DB_Lock_Analysis]]
- [[CS_MVCC_Concurrency]]