SQS 처리 시간 연장과 receiptHandle 확인
SQS로 대용량 데이터 내보내기를 처리할 때 visibility timeout을 늘리는 코드가 기대대로 동작하지 않는 문제를 수정했습니다. 처리 중인 메시지가 다시 전달되면 같은 작업이 겹칠 수 있어, 작업에 걸리는 시간과 메시지를 수신한 정보를 함께 살펴봐야 했습니다.
당시 수정 기록을 계기로 visibility timeout과 receiptHandle의 역할을 정리했습니다.
메시지 처리와 visibility timeout
처리 프로그램이 메시지를 받아도 메시지는 큐에서 바로 삭제되지 않습니다. 일정 시간 다른 처리 프로그램에 전달되지 않고, 처리 후 DeleteMessage를 호출해야 삭제됩니다. 삭제 전에 visibility timeout이 끝나면 다시 수신할 수 있습니다.
Standard 큐에서는 이 시간 안에도 중복 전달이 가능하므로, timeout 설정만으로 중복 실행을 막는다고 볼 수는 없습니다. AWS의 visibility timeout 설명 도 처리 시간에 맞는 설정과 주기적인 연장을 안내합니다.
메시지 ID와 수신 핸들의 차이
메시지 ID는 메시지를 구분하는 값이고, receiptHandle은 해당 메시지를 수신했을 때 받는 값입니다. 같은 메시지가 다시 전달되면 새로운 핸들이 발급됩니다.
시간이 지났다는 이유만으로 SQS가 처리 중인 핸들을 주기적으로 갱신하는 구조는 아닙니다. 수신 한 번에 대응하는 핸들과 그 작업의 수명을 함께 관리해야 합니다. 오래된 작업의 타이머에 다른 수신 작업의 핸들을 넣는 방식은 작업 간 관계를 더 모호하게 만들 수 있습니다.
처리 시간 연장
ChangeMessageVisibility는 현재 수신한 메시지의 visibility timeout을 바꿉니다. 지정한 시간은 호출 시점부터 계산되며, 남은 시간에 값을 더하는 방식이 아닙니다. 다음은 AWS SDK for Java v2를 사용한 호출 예시입니다.
sqsClient.changeMessageVisibility(ChangeMessageVisibilityRequest.builder()
.queueUrl(queueUrl)
.receiptHandle(receivedMessage.receiptHandle())
.visibilityTimeout(120)
.build());실제 heartbeat를 구성할 때는 이 수신 작업이 끝나면 연장 작업도 중단해야 합니다. 처리 완료 후에도 타이머가 남아 있거나, timeout이 끝난 뒤까지 같은 작업이 실행되는 경우를 함께 봐야 합니다.
또한 연장한다고 무한히 처리할 수 있는 것은 아닙니다. 최초 수신부터 최대 12시간이라는 한도 안에서만 연장할 수 있습니다. 자세한 동작은 ChangeMessageVisibility API 에서 확인할 수 있습니다.
API별 응답 의미 확인
오래된 핸들의 동작은 API별로 확인해야 합니다. DeleteMessage 문서 는 오래된 핸들로 요청하면 성공하더라도 메시지가 삭제되지 않을 수 있다고 설명합니다. 반면 ChangeMessageVisibility에는 유효하지 않은 핸들에 대한 오류가 정의돼 있습니다.
따라서 삭제 API의 설명을 근거로 시간 연장 API도 항상 같은 방식으로 동작한다고 일반화하면 안 됩니다. 당시 현상을 다시 분석할 때도 실제 사용한 핸들과 수신 시각, 연장 호출과 응답, 재수신 로그를 맞춰 봐야 합니다.
중복 실행과 실패 처리
시간 연장이 있어도 중복 실행을 고려해야 합니다. 요청 ID가 같으면 같은 결과를 재사용하거나, 동시에 같은 작업을 수행하지 못하도록 상태를 원자적으로 관리하는 방법이 필요합니다. 완료 여부를 조회한 뒤 실행하는 것만으로는 두 작업이 동시에 시작되는 경우를 막기 어렵습니다.
삭제 시점도 중요합니다. 예외가 발생했는데 finally에서 무조건 메시지를 삭제하면 재시도할 기회를 없앨 수 있습니다. 처리 성공 후 삭제할지, 실패 시 다시 전달되도록 둘지, 반복 실패를 DLQ로 보낼지를 처리 규칙에 맞춰 정해야 합니다.
긴 작업에서는 연장 API가 호출됐다는 로그만 볼 것이 아니라, 어떤 수신 작업이 언제 끝났고 삭제됐는지까지 이어서 확인해야 했습니다.