5. 예외 경로의 커넥션/트랜잭션 누수 (풀 고갈 / 잠금 장기 보유)

난이도 하 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 캐릭터 저장 시 커넥션/트랜잭션 풀 반납
// ----------------------------------------------------------------------------
// 시나리오 (영속화/DB):
//   - 캐릭터 저장은 커넥션 풀에서 커넥션을 빌려, 트랜잭션 안에서 스탯·인벤토리·퀘스트를
//     여러 UPDATE 로 반영한 뒤 커밋한다.
//   - 커넥션 풀은 크기가 한정돼 있고, 동시 저장 요청이 많다(로그아웃/자동저장/존 이동).
//   - 각 UPDATE 는 제약 위반, 데드락 피해자 선정, 타임아웃 등으로 예외를 던질 수 있다.
//
// 상황:
//   - 일부 저장에서 중간 UPDATE 가 예외를 던진다(예: 일시적 DB 데드락/타임아웃).
//   - 이런 저장이 간헐적으로 반복된다.
//
// 과제:
//   중간에 예외가 났을 때 이 메서드가 커넥션과 트랜잭션을 어떻게 처리하는지 추적하라.
//   (A)에서 빌린 커넥션과 (B)에서 연 트랜잭션이 예외 경로에서 어떻게 되는지, 그리고
//   그것이 반복될 때 커넥션 풀과 DB 측 잠금에 무슨 일이 생기는지 설명하라. 수정안과
//   더 나은 설계를 제시하라.
//   (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

public interface IDbConnection
{
    IDbTransaction BeginTransaction();
    void Execute(IDbTransaction tx, string sql, object args);
    void Close();
}
public interface IDbTransaction { void Commit(); void Rollback(); }

public interface IConnectionPool
{
    IDbConnection Rent();         // 풀에서 대여(없으면 대기/타임아웃)
    void Return(IDbConnection c); // 풀로 반납
}

public sealed class CharacterRepository
{
    private readonly IConnectionPool _pool;
    public CharacterRepository(IConnectionPool pool) { _pool = pool; }

    public void SaveCharacter(Character c)
    {
        IDbConnection conn = _pool.Rent();              // (A) 커넥션 대여
        IDbTransaction tx = conn.BeginTransaction();    // (B) 트랜잭션 시작

        conn.Execute(tx, "UPDATE stats SET ... WHERE id=@id", c.Stats);        // (C) 예외 가능
        conn.Execute(tx, "UPDATE inventory SET ... WHERE id=@id", c.Inventory); // (C) 예외 가능
        conn.Execute(tx, "UPDATE quests SET ... WHERE id=@id", c.Quests);       // (C) 예외 가능

        tx.Commit();                                    // (D) 커밋
        _pool.Return(conn);                             // (E) 반납
    }
}

public sealed class Character
{
    public long Id;
    public object Stats = new();
    public object Inventory = new();
    public object Quests = new();
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.