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++
// 시나리오: 신규 가입 캐릭터에게 웰컴 보상을 지급하는 야간 배치(C++ 백엔드). characters
// 테이블의 id 는 DB auto-increment 다. 배치는 마지막 처리 id 이후를 청크로 나눠 순차
// 처리하고, 체크포인트(lastId)를 저장해 다음 실행이 이어가게 한다.
//
// 요구사항:
// - 신규 생성된 모든 캐릭터는 정확히 한 번 보상을 받아야 한다(누락·중복 없이).
// - 배치는 재시작해도 체크포인트 이후부터 이어서 처리해야 한다.
// - 표의 마지막 행까지 빠짐없이 훑어야 한다.
//
// 과제: processNewCharacters 를 검토하고, 어떤 상황에서 일부 캐릭터가 보상을 못 받게
// 되는지, 어떤 조건에서 드러나는지, 어떻게 고칠지 설명하라. (A)(B)(C) 를 살펴보라.
#include <cstdint>
#include <string>
#include <vector>
struct Character { int64_t id; std::string name; };
struct IDb {
// id 가 (from, to] 인 행들을 id 오름차순으로 반환
virtual std::vector<Character> queryRange(int64_t from, int64_t to) = 0;
virtual ~IDb() = default;
};
class WelcomeRewardJob {
public:
explicit WelcomeRewardJob(IDb& db) : db_(db) {}
void processNewCharacters() {
static constexpr int64_t BatchSize = 1000;
int64_t lastId = loadCheckpoint();
while (true) {
std::vector<Character> rows = db_.queryRange(lastId, lastId + BatchSize); // (A)
if (rows.empty()) break; // (B)
for (const auto& c : rows) giveWelcomeReward(c);
lastId += BatchSize; // (C)
saveCheckpoint(lastId);
}
}
private:
IDb& db_;
void giveWelcomeReward(const Character&);
int64_t loadCheckpoint();
void saveCheckpoint(int64_t);
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.