환경 및 전제 조건
- Pub/Sub
- RDB
- Java
배경
- 리스트 데이터를 병렬로 처리하여 Pub/Sub 이벤트를 발행하던 중, Database의 최종 상태값이 의도와 다르게 저장되는 현상이 발생함.
- 모든 DB 쓰기 작업 로그가 정상임에도 불구하고 데이터 정합성이 깨지는 현상이 발생.
이슈
parallelStream()을 이용해 대량의 메시지를 전송한 뒤 해당 항목들의 상태를 일괄 업데이트하는 로직에서 문제 발생.- GCP Pub/Sub으로부터 “처리 성공(SUCCESS)” 응답을 받아 DB 업데이트까지 마쳤으나 최종 결과는 “전송 완료(PUBLISHED)” 상태로 남아있음.
- 해당 코드는 아니지만 대략적으로 아래와 같은 형식의 코드.
// 1. 병렬 이벤트 발행 (Main Thread)
jobList.parallelStream().forEach(job -> {
// Pub/Sub 메시지 전송
publishEvent(job);
});
// ---------------------------------------------------------
// [핵심 지점] 이 사이에 Pub/Sub 구독에서 SUCCESS 응답이 먼저 도착!
// 별도의 구독 스레드(Sub Thread)가 DB를 이미 SUCCESS로 업데이트함
// ---------------------------------------------------------
// 2. 발행 완료 후 상태 업데이트 (Main Thread)
// 뒤늦게 실행되어 DB를 "PUBLISHED"로 덮어버림
jobRepository.findAllById(ids).forEach(job -> {
// AS-IS: 조건문 없이 무조건 덮어쓰기
job.setStatus(PUBLISHED);
jobRepository.save(job); // SUCCESS가 PUBLISHED로 변하는 정합성 오류 발생
});
이유
Pub/Sub 구독 메시지 응답이 DB 쓰기 보다 빠른 경우
- 정상 흐름: Pub/Sub 게시 -> 게시 완료 변경(DB: PUBLISHED) -> 구독 메시지 수신(DB: SUCCESS)
- 비정상 흐름: Pub/Sub 게시 -> 구독 메시지 수신(DB: SUCCESS) -> 게시 완료 변경(DB: PUBLISHED)
의견
병렬 또는 비동기로 외부와 통신하고 상태를 업데이트 하는 부분들은 이전에도 비슷한 이슈([Javascript] forEach에서 async await가 순차적으로 수행되지 않는 경우)가 있었는데 매번 주의해서 개발해야하는 거 같다.