Quick Flow
rate limit은 player가 조작을 빠르게 누르는 일을 막기 위한 기능이 아니라, server가 처리할 수 있는 비용을 넘는 command를 authoritative queue에 넣지 않기 위한 경계입니다. connection 수, bytes, message type, account action, match 전체 비용은 서로 다른 bucket으로 봐야 합니다.
packet 수신
-> 최대 크기와 frame 형식 검사
-> connection bytes·messages bucket 확인
-> account와 command kind bucket 확인
-> 권한·cooldown·state revision 검사
-> tick command queue에 넣기한도를 넘긴 packet은 항상 같은 방식으로 처리하지 않습니다. 최신 이동 input처럼 대체 가능한 것은 drop하고, 구매·채팅처럼 client가 결과를 알아야 하는 것은 rate-limited error를 응답하며, malformed·replay·반복 위반은 strike를 쌓아 connection을 끊습니다. 무거운 command를 queue에 계속 쌓아 둔 채 나중에 처리하는 것은 rate limiting이 아닙니다.
bucket을 나누는 이유
function acceptCommand(client: Client, command: Command) {
if (!client.byteBucket.tryTake(command.size)) return drop("byte-limit");
if (!client.kindBuckets[command.kind].tryTake(1)) return reject("rate-limited");
if (!accountBuckets.tryTake(client.accountId, command.kind, 1)) return reject("account-limit");
if (!isAllowedInMatchState(client, command)) return reject("invalid-state");
enqueueForNextTick(client, command);
}connection bucket만 두면 reconnect로 우회할 수 있고, account bucket만 두면 shared network의 정상 player를 잘못 묶을 수 있습니다. pre-auth 단계에는 IP 또는 device signal의 완화된 제한을 쓰고, 인증 뒤에는 account·session·party 수준으로 옮깁니다. IP는 NAT와 mobile network 때문에 영구 identity가 아니므로 단독 차단 기준으로 과신하지 않습니다.
command마다 server 비용과 gameplay 허용 빈도를 따로 정합니다. MoveInput은 tick보다 빠르게 보내도 최신값 하나만 필요할 수 있고, Purchase는 초당 몇 회보다 transaction concurrency가 중요하며, Chat은 moderation queue의 비용도 고려해야 합니다. client animation 속도를 server rate limit의 근거로 쓰지 않습니다.
격리와 관찰
한 connection의 parse error, oversized message, queue overflow가 같은 process의 다른 match까지 영향을 주면 격리가 부족합니다. connection별 pending bytes와 command queue 상한을 두고, 초과 시 그 connection을 close하거나 읽기를 멈춥니다. match별 rate limit도 두어 bot party가 한 match의 event·replication budget을 독점하지 않게 합니다.
metric에는 제한된 command kind와 결과 코드처럼 낮은 cardinality만 넣고, account ID·IP·raw payload는 보안 log 또는 trace context에 제한적으로 남깁니다. 반복 위반은 단순한 network fault와 구분할 수 있게 reason code를 기록합니다.
자주 틀리는 부분
client가 "다음 입력은 100ms 뒤"라고 보낸 값을 신뢰해 bucket을 소모시키면 timestamp를 조작할 수 있습니다. rate limit의 시간은 server monotonic clock으로 계산합니다.
또한 rate limit 통과를 authorization 통과로 취급하면 안 됩니다. 한도 안에 있어도 다른 player inventory를 변경하거나 현재 phase에서 불가능한 command는 거절해야 합니다.
참고 링크
2 sources