앞 단계에서 API 키 기준으로 세고, 게이트웨이에서 자르고, 오차 5퍼센트 까지 허용한다고 정했습니다. 이제 어떻게 셀지를 고릅니다. 가장 값싼 방법부터 봅니다.
시각으로 창을 자른다
키와 분을 붙여 만든 이름 하나에 숫자를 올립니다.
키 abc 의 10시 01분 창: 값을 1 올리고 1,000 을 넘었는지 본다
창이 바뀌면 이름이 바뀌므로 값도 새로 시작한다
키마다 정수 하나만 쓰므로 메모리가 거의 들지 않고, 판정도 값 하나 올리는 것으로 끝납니다. 1ms 예산에 여유가 큽니다.
그런데 경계에서 두 배가 지나간다
두 창은 각자 한도를 지켰습니다. 규칙은 어긋나지 않았는데 결과가 어긋납니다. 서버가 실제로 받은 부하는 2초에 2,000회이고, 이것은 한도가 막으려던 것입니다.
| 관점 | 판정 |
|---|---|
| 창 단위로 보면 | 각각 1,000회. 정상 |
| 실제 부하로 보면 | 2초에 2,000회. 한도의 두 배 |
허용 오차 5퍼센트 는 1,050회를 허용하려고 적은 값입니다. 2,000회는 그 범위 밖이므로 이 방법은 탈락합니다. 요구사항에 오차를 숫자로 적어 둔 덕분에 취향이 아니라 근거로 자를 수 있습니다.
그래도 이 방법이 맞는 경우가 있다
경계 버스트가 문제가 되는 것은 한도가 곧 용량 한계에 가까울 때입니다. 한도를 넉넉하게 잡아 두었고 두 배가 와도 서버가 견딘다면, 정수 하나로 끝나는 이 방법이 가장 좋은 선택입니다.
| 상황 | 고정 윈도우가 |
|---|---|
| 한도가 용량 한계에 가깝다 | 위험하다. 두 배가 그대로 들어온다 |
| 한도가 남용 방지용이고 여유가 크다 | 충분하다. 더 정교한 방법은 과설계다 |
"정확한 방법이 늘 낫다" 가 아닙니다. 무엇을 막으려는 한도인지에 따라 답이 갈립니다.
창 이름을 언제 지우나
창이 지나면 그 이름의 값은 다시 쓰이지 않습니다. 남겨 두면 키 수만큼 쓰레기가 쌓이므로 만료를 창 길이보다 조금 길게 걸어 둡니다. 지우는 코드를 따로 만들지 않고 저장소가 치우게 합니다.
시계가 어긋나면 창도 어긋난다
창 이름을 각 게이트웨이가 자기 시계로 만들면, 시계가 몇 초 다른 두 대가 서로 다른 창에 값을 올립니다. 그러면 같은 시점의 요청이 두 창에 나뉘어 한도가 느슨해집니다. 창 이름은 저장소 쪽 시각으로 만들거나, 게이트웨이 시계를 맞춰 두는 것이 전제입니다.