2019년 1월 30일 수요일

Multiple server instance 환경에서 form double submit을 server에서 검출하기

Client쪽에서 form의 submit이 중복되지 않도록 막는 방법은 여러가지가 알려져 있다. 모두 JavaScript에서 처리하는 방법들이고, 다양한 질문과 답변들을 찾아보면 개념적으로는 몇가지로 수렴한다.

다중 Server에서 form의 중복 submit을 검출하는 방법은 별로 쓸만한 것을 찾지 못했다.

Server instance가 여러 개이기 때문에 상태를 저장하기 위해 DB 등 외부의 node를 사용해야 한다. Form에 uuid 등 중복되지 않는 키를 할당하고 해당 키에 대해서 트랜잭션이 이미 발생했는지 여부를 DB에 query를 해서 확인하는 방식이다. 그런데, 이 방법은 불필요한 DB transaction이 발생하고, 일정 시간이 지난 후 불필요한 레코드들을 정리해야 해서 귀찮다.

Form이 submit되었을 때 uuid를 키로 사용해서 redis 또는 memcached에 item을 하나 생성해 놓는 것은 좀더 가벼운 해결책이 될 것 같다. 일정 시간이 지나면 cache item은 자동 정리가 되기 때문에 귀찮게 정리해줘야 할 일도 없다.

문제는, cache에 기록하는 작업이 끝나기 전에 cache를 읽으려고 할 때 발생. 두번의 form submit이 발생하는 사이 동일한 시간 간격으로 cache에 접근하려고 할텐데, overlap이 발생하면 중복된 submit을 알아챌 수 없다. django-lock-tokens같은 프로젝트는 resource에 대한 lock을 하는 방식. 그런데, 마찬가지의 문제가 있을 것 같다.

좋은 방법이 없을까?

How to manage concurrency in Django models를 보면 select_for_update를 사용해서 transaction이 끝날 때까지 lock을 하는 (pessimistic) 방법이 나온다.

우리가 사용하는 cache backend는 Redis이니 Distributed locks with Redis에서 제공하는 방법도 좋을 것 같다.