Skip to content

Rate limiting Plug module based on Redis Lua scripting.

License

Notifications You must be signed in to change notification settings

Recruitee/plug_limit

Repository files navigation

PlugLimit

Hex Version Hex Docs CI

Rate limiting Plug module based on Redis Lua scripting.

Summary

PlugLimit is using Redis Lua scripting to provide rate-limiting functionality for web applications based on a Plug library. PlugLimit has a modular architecture: users can use their own Redis Lua scripts implementing custom rate limiting algorithms.

Salient Redis Lua scripting feature is a race conditions resiliency which makes this library a recommended solution for distributed systems (e.g.: Phoenix servers behind round robin load balancer).

PlugLimit provides two built-in rate limiters: PlugLimit.FixedWindow and PlugLimit.TokenBucket.

Installation

Add PlugLimit library to your application dependencies:

def deps do
  [
    {:plug_limit, "~> 0.1.0"}
  ]
end

Usage

PlugLimit is Redis client agnostic. In a first step you must define Elixir function executing Redis commands, depending on Redis client of your choice:

# config/config.exs
config :plug_limit,
  enabled?: true,
  cmd: {MyApp.Redis, :command, []}

MyApp.Redis.command/2 function must accept Redis command as a first argument and static MFA tuple arg as a second. In most cases :cmd function will be a Redix.command/3 or :eredis.q/2,3 wrapper. Example naive Redix driver wrapper:

#lib/my_app/redis.ex
def command(command, opts \\ [timeout: 500]) do
  {:ok, pid} = Redix.start_link()
  Redix.command(pid, command, opts)
  :ok = Redix.stop(pid)
end

PlugLimit is tested with both Redix and eredis Redis clients.

Phoenix Framework endpoint can be protected with fixed window rate-limiter by placing a PlugLimit.FixedWindow plug call in the request processing pipeline:

#lib/my_app_web/router.ex
pipeline :high_cost_pipeline do
  plug(PlugLimit.FixedWindow,
    limit: 10,
    ttl: 60,
    key: {MyApp.RateLimiter, :user_key, [:high_cost_pipeline]}
  )
  # remaining pipeline plugs...
end

Rate limits for :high_cost_pipeline pipeline will be evaluated with Redis Lua script fixed window algorithm. Client identified with :key will be allowed to issue 10 requests in 60 seconds time window.

MFA tuple defined with :key option specifies user function which should provide Redis key used to uniquely identify given rate-limiter bucket. Redis rate-limiter key name should be derived from Plug.Conn.t() connection struct parameters. Example function to create Redis key name for rate-limiter throttling requests for a given user identified by a connection assigned :user_id:

#lib/my_app/rate_limiter.ex
def user_key(%Plug.Conn{assigns: %{user_id: user_id}}, prefix),
   do: {:ok, ["#{prefix}:#{user_id}"]}

Please refer to PlugLimit module documentation for detailed library configuration description and "Redis Lua script rate limiters" in LIMITERS.md file for Redis Lua scripts implementation guidelines.

TODO

  • Add leaky bucket rate limiting algorithm implementation.