19. auto-increment ID 를 연속이라 가정한 구간 배치의 누락 (C#)

난이도 하 해설 보기 →

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

결함 코드 · C#
// 시나리오: 신규 가입 캐릭터에게 웰컴 보상을 지급하는 야간 배치. characters 테이블의
// id 는 DB auto-increment(IDENTITY) 다. 배치는 마지막으로 처리한 id 이후를 청크로 나눠
// 조회하며 순차 처리하고, 체크포인트(lastId)를 저장해 다음 실행이 이어가게 한다.
//
// 요구사항:
//  - 신규로 생성된 모든 캐릭터는 정확히 한 번 보상을 받아야 한다(누락·중복 없이).
//  - 배치는 재시작해도 체크포인트 이후부터 이어서 처리해야 한다.
//  - 표의 마지막 행까지 빠짐없이 훑어야 한다.
//
// 과제: ProcessNewCharacters 를 검토하고, 어떤 상황에서 일부 캐릭터가 보상을 못 받게
//       되는지, 어떤 조건에서 드러나는지, 어떻게 고칠지 설명하라. (A)(B)(C) 를 살펴보라.

using System.Threading.Tasks;

public sealed class WelcomeRewardJob
{
    private const int BatchSize = 1000;
    private readonly IDb _db;
    public WelcomeRewardJob(IDb db) { _db = db; }

    public async Task ProcessNewCharacters()
    {
        long lastId = await LoadCheckpoint();
        while (true)
        {
            var rows = await _db.QueryAsync<Character>(
                "SELECT id, name FROM characters WHERE id > @from AND id <= @to ORDER BY id",
                new { from = lastId, to = lastId + BatchSize });   // (A)

            if (rows.Count == 0) break;                            // (B)

            foreach (var c in rows) await GiveWelcomeReward(c);
            lastId += BatchSize;                                   // (C)
            await SaveCheckpoint(lastId);
        }
    }

    private Task GiveWelcomeReward(Character c) => Task.CompletedTask;
    private Task<long> LoadCheckpoint() => Task.FromResult(0L);
    private Task SaveCheckpoint(long id) => Task.CompletedTask;
}

public sealed class Character { public long Id; public string Name; }
내 리뷰 · C#
내 답안 · 자동 저장

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