본문 바로가기

프로젝트

AWS SQS 메시지 소비할 때 Visibility Timeout 문제

배경

회사에서 앱으로 단체 알림을 보내는 서비스를 만들 때 순차 실행을 위해 메시지 큐 서비스인 SQS를 사용했다. SQS에 발행된 메시지를 서버 리스너가 일정 주기마다 체크를 해서 받아왔다. 받은 메시지를 기반으로 정해진 로직을 수행을 하였다. 이 때, 모든 로직이 완료가 되었을 때 SQS 메시지가 삭제가 되어야 했는데 삭제가 되지 않고 SQS에 메시지가 계속 남아있는 현상이 발생하였다.

원인

SQS에서 가져온 메시지를 소비할 수 있는 시간이 정해져 있었다. 소비 시간이 지나면 메시지는 반환이 되고 다른 컨슈머가 사용할 수 있는 상태가 된다. 메시지 소비 시간은 별도로 설정하지 않아 30초로 설정이 되었다. 단체 알림 로직은 대상이 되는 유저에 따라 처리 시간이 30초가 넘는 경우가 생겨 처리 완료 이전에 메시지가 반환이 된 것이다.

The default visibility timeout for a message is 30 seconds. The minimum is 0 seconds. The maximum is 12 hours.

해결 방법

메시지 소비 시간을 처음부터 설정을 길게 하는 방법을 생각했었다. 그런데 이 방법의 경우 서버에서 단체 알림 동작 중에 문제가 발생하면 메시지 반환이 바로 되지 않으며, 다른 컨슈머가 다시 단체 알림 동작을 수행할 수 없게 된다. 

메시지 소비 시간를 기본 값으로 설정을 하고, 동작하는 중에 필요한만큼 소비 시간을 늘려가며 사용하는 방식을 사용하기로 했다. 처리 중에 계속 시간 체크를 해야하겠지만 많은 리소스가 드는 동작은 아니었고 처음 생각했던 방식보다 더 안전하다고 판단했다.

 

int initialVisibilityTimeout = 30;
int extensionTime = 60; 
Instant messageReceiptTime = Instant.now(); 

for (String endpoint : endpoints) {
    Instant now = Instant.now();
    long elapsedSeconds = Duration.between(messageReceiptTime, now).getSeconds();
    long remainingVisibilityTimeout = initialVisibilityTimeout - elapsedSeconds;

    if (remainingVisibilityTimeout <= 10) {
        initialVisibilityTimeout += extensionTime;
        sqs.changeMessageVisibility(ChangeMessageVisibilityRequest.builder()
                .queueUrl(SQS_URL)
                .receiptHandle(receiptHandle)
                .visibilityTimeout(initialVisibilityTimeout)
                .build());
    }
}

 참고 문서

AWS Docs

 

Amazon SQS visibility timeout - Amazon Simple Queue Service

Amazon SQS visibility timeout When a consumer receives and processes a message from a queue, the message remains in the queue. Amazon SQS doesn't automatically delete the message. Because Amazon SQS is a distributed system, there's no guarantee that the co

docs.aws.amazon.com