Skip to content

Instantly share code, notes, and snippets.

@webcoyote
Created December 11, 2012 19:06
Show Gist options
  • Save webcoyote/4261121 to your computer and use it in GitHub Desktop.
Save webcoyote/4261121 to your computer and use it in GitHub Desktop.
Transaction rate-limiting
unsigned PlatformTimeMs () {
#if defined(_WINDOWS_)
return GetTickCount();
#else
#error Your implementation here
// something like clock_gettime(CLOCK_MONOTONIC, ...) for Unix/Linux
#endif
}
CRateLimiter::CRateLimiter ()
: m_timeMs(PlatformTimeMs())
{}
bool CRateLimiter::AddTime (unsigned costMs, unsigned maxCostMs) {
ASSERT(costMs < maxCostMs);
// Reset rate-limiter time value if it has expired
// - handles integer overflow safely
unsigned currTimeMs = PlatformTimeMs();
if ((int) (currTimeMs - m_timeMs) > 0)
m_timeMs = currTimeMs;
// Has the user accrued too much time-cost?
// - handles integer overflow safely
unsigned newTimeMs = m_timeMs + costMs;
if ((int) (newTimeMs - currTimeMs) >= (int) maxCostMs)
return false;
m_timeMs = newTimeMs;
return true;
}
class CRateLimiter {
public:
CRateLimiter ();
bool AddTime (unsigned costMs, unsigned maxCostMs);
private:
unsigned m_timeMs;
};
// Using these values a player can attempt to login once
// every 30 seconds, but with as many as ten login attempts
// in a burst. While this sounds like a lot many players
// forget their passwords and need a number of attempts to
// remember it, which I discovered by analyzing log files.
// They should try LastPass, which is an awesome solution
// to this problem.
const unsigned LOGIN_COST_MS = 30 * 1000;
const unsigned MAX_LOGIN_COST_MS = 10 * LOGIN_COST_MS;
ErrorCode CPlayer::PlayerLogin () {
if (!m_rateLimiter.AddTime(LOGIN_COST_MS, MAX_LOGIN_COST_MS))
return ERROR_LOGIN_RATE_LIMIT;
... login code here
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment