결제가 두 번 처리될 수 있는 상황

upstream 하나가 timeout 나면 다른 server로 자동 재시도하도록 설정한 뒤 간헐적으로 POST side effect가 중복됩니다. client retry만 확인하면 proxy가 같은 요청을 한 번 더 보낸 경로를 놓칠 수 있습니다.

Nginx는 request가 upstream에 전송된 뒤에는 POST·LOCK·PATCH를 기본적으로 다음 server에 다시 보내지 않으며, non_idempotent option을 켜야만 이러한 재시도를 명시적으로 허용합니다.

실제 적용 설정 분석

기본 proxy_next_upstream 조건은 error timeout입니다. 연결 자체가 되지 않아 request가 전송되지 않은 경우에는 다음 upstream을 시도할 수 있습니다. 반면 request가 이미 전송된 POST·LOCK·PATCH를 다시 보내려면 설정에 non_idempotent를 명시해야 합니다. 먼저 nginx -T 결과와 access log의 $upstream_addr, $upstream_status, $upstream_response_time을 한 request ID로 확인합니다.

log_format upstream '$request_id $request_method $uri $upstream_addr $upstream_status $upstream_response_time';

location /api/payments {
    proxy_next_upstream error timeout http_502 http_503;
    proxy_next_upstream_tries 2;
    proxy_next_upstream_timeout 2s;
    proxy_pass http://payment_api;
}

중복을 막는 해결 기준

쓰기 endpoint에는 이유 없이 non_idempotent를 추가하지 않습니다. 재시도가 꼭 필요하면 application이 idempotency key와 DB unique constraint로 같은 요청의 결과를 재사용하도록 먼저 만듭니다. connect 실패, request 전송 뒤 응답 header timeout, 502를 각각 재현해 upstream 주소 수와 DB row 수를 확인합니다. Nginx는 client에 response 일부를 보낸 뒤 발생한 오류를 다음 server로 넘겨 복구할 수 없다는 경계도 함께 문서화합니다.

공식 문서

배포된 Nginx version의 최종 설정과 endpoint의 idempotency 계약을 기준으로 확인합니다.