Giter Site home page Giter Site logo

grempe / ex_rated Goto Github PK

View Code? Open in Web Editor NEW
445.0 4.0 51.0 141 KB

ExRated, the Elixir OTP GenServer with the naughty name that allows you to rate-limit calls to any service that requires it.

License: Other

Elixir 100.00%
elixir rate rate-limiting rate-limit bucket phoenix otp api rate-limits

ex_rated's Introduction

Hi there, I'm Glenn - aka @grempe ๐Ÿ‘‹

Twitter Follow

I'm a Husband, Father, Developer, and the Founder & CEO of Truestamp (on GitHub @truestamp)!!

  • ๐ŸŒฑ Iโ€™m currently growing Truestamp ๐Ÿคฃ
  • ๐Ÿ‘ฏ Iโ€™m focused on the intersection of Privacy, Security, Cryptography, and Integrity
  • ๐Ÿฅ… Goals: Help my customers verify the integrity of their most important data
  • โšก Fun fact: My vehicle license plate has a cryptography reference. I think it confuses most who see it

Connect with me


GitHub Stats


Truestamp's GitHub stats

Top Langs

ex_rated's People

Contributors

ahamez avatar brianberlin avatar cefigueiredo avatar christopheradams avatar denvera avatar dependabot-preview[bot] avatar dependabot[bot] avatar grempe avatar jaimeiniesta avatar jechol avatar kianmeng avatar laodc avatar mitchellhenke avatar mnishiguchi avatar mrdziuban avatar nabaskes avatar nicksanders avatar ruudk avatar shijithkjayan avatar tbk145 avatar vitortrin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

ex_rated's Issues

Avoid GenServer Serialization

Hey!

At the moment it appears that every request is serialized through a single genserver. This can easily introduce a bottleneck, and also produces a lot more GC pressure on that one process than is necessary.

This blog post sketches the basics of doing rate limiting via concurrent access to an ets table. https://dockyard.com/blog/2017/05/19/optimizing-elixir-and-phoenix-with-ets. The design of ExRated is such that this would be a relatively small change. It's already using the update_counter function, but from within the genserver itself. This could just be moved to the client side, and a default supplied.

Implementing limits in a day (86400 secs) doesn't seem to start from the first check_rate?

Hi all,

I implement ExRated in my Phoenix app today using https://dev.to/mnishiguchi/rate-limiter-for-phoenix-app-3j2n

Does this mean 86400 secs from the first ever check_rate? :

ExRated.check_rate("my-1000-per-day-bucket", 86400000, 1000)
{:ok, 1}
iex(3)> ExRated.check_rate("my-1000-per-day-bucket", 86400000, 1000)
{:ok, 2}
iex(4)> ExRated.check_rate("my-1000-per-day-bucket", 86400000, 1000)
{:ok, 3}
iex(5)> ExRated.check_rate("my-1000-per-day-bucket", 86400000, 1000)
{:ok, 4}
iex(6)> ExRated.check_rate("my-1000-per-day-bucket", 86400000, 1000)
{:ok, 5}
iex(7)> ExRated.check_rate("my-1000-per-day-bucket", 86400000, 1000)
{:ok, 6}
iex(8)> ExRated.inspect_bucket("my-1000-per-day-bucket", 86400000, 1000)
{6, 994, 14412292, 1682020774747, 1682020781003}
iex(9)> ExRated.inspect_bucket("my-1000-per-day-bucket", 86400000, 1000)
{6, 994, 14408229, 1682020774747, 1682020781003}

Shouldn't I see ms_to_next_bucket to be very close to 86400000?

Thanks.

Does the bucket name have to be a string?

I'm using tuples for bucket names and they seem to work fine, however the README and typespecs explicitly say string. I briefly looked through the code and didn't see anything that required bucket names to be strings, but I may have missed something.

If they're not restricted to strings it might be helpful to update the docs and typespecs.

ExRated fails to start

Erlang/OTP 24 [erts-12.0.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Elixir 1.13.1 (compiled with Erlang/OTP 24)
** (Mix) Could not start application treasure_hunter: TreasureHunter.Application.start(:normal, []) returned an error: shutdown: failed to start child: ExRated
    ** (EXIT) an exception was raised:
        ** (ArgumentError) argument error
            (stdlib 3.10) :ets.new(:ets_rated_buckets, [:named_table, :ordered_set, :public, {:read_concurrency, true}, {:write_concurrency, true}])
            (ex_rated 2.1.0) lib/ex_rated.ex:178: ExRated.open_table/2
            (ex_rated 2.1.0) lib/ex_rated.ex:123: ExRated.init/1
            (stdlib 3.10) gen_server.erl:374: :gen_server.init_it/2
            (stdlib 3.10) gen_server.erl:342: :gen_server.init_it/6
            (stdlib 3.10) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

Add a clear_rate/reset_rate function

I would like to be able to use ex_rated for login attempts.

On successful login I would like to delete/reset the bucket

Would you be interested in a pull request adding a function like

ExRated.clear_rate("LOGIN:192.168.1.20", 60_000)

ExRated.check_rate should return the time until the bucket is reset

Right now, if ExRated.check_rate returns an error, it returns {:error, limit}. This makes it difficult to efficiently handle actions that get rejected due to rate limits. If we instead return a tuple like {:error, limit, time_until_next_bucket_reset}, then it would be possible for the caller to schedule the action for the earliest possible point that it may next succeed at.

I'm happy to take a stab at this if you let me know what specific interface you'd like to support, or if you have any other ideas on handling this use-case.

Thanks! I love the hex :)

Custom bucket increments

In our new and fancy world of LLMs and OpenAI, we have rate limits by number of message-tokens per minute, which means the bucket needs to be increased by a somewhat arbitrary number. Obviously, I could just call "check_rate" sequentially in a loop a few hundred times, but that seems... A little silly.

Would it be possible to expand the public API a little, and allow us to pass in an optional number-by-which-to-increase-the-number-of-tokens-in-the-bucket?

New buckets with a request limit of 0 return :ok

https://github.com/grempe/ex_rated/blob/master/lib/ex_rated.ex#L214

^this line assumes that the request limit is always >= 1, so it always returns ok. If the request limit is 0, we should return {:error, :limit}

My use case for this was testing logic that doesn't call an external API if we are out of requests. We could test this by making the time period long, the limit = 1, and making two requests, but tests that are time-based should be avoided in general.

How to list the existing buckets?

Is there a way to list the buckets that are currently defined? I guess I can read them from the ETS table, but maybe there's a cleaner way to do that, something like ExRated.list_buckets.

I need this because I'm going to be scraping web sites, so I want to limit the number of requests to each site. Each site will have a temporary bucket with a dynamic name ("example.com", "github.com") that will be deleted when finished, but I want to know, at a given time, how many buckets are defined and their state.

Can't compile ex_rated on elixir 1.13.0-rc.0-otp-24

== Compilation error in file lib/ex_rated.ex ==
** (KeyError) key :vars not found in: #Macro.Env<aliases: [], context: nil, context_modules: [], file: "/Users/scott/Documents/ArchRepo/ex_rated/lib/ex_rated.ex", function: {:delete_bucket, 2}, functions: [{Kernel, [!=: 2, !==: 2, *: 2, **: 2, +: 1, +: 2, ++: 2, -: 1, -: 2, --: 2, /: 2, <: 2, <=: 2, ==: 2, ===: 2, =~: 2, >: 2, >=: 2, abs: 1, apply: 2, apply: 3, binary_part: 3, bit_size: 1, byte_size: 1, ceil: 1, div: 2, elem: 2, exit: 1, floor: 1, function_exported?: 3, get_and_update_in: 3, get_in: 2, hd: 1, inspect: 1, inspect: 2, is_atom: 1, is_binary: 1, is_bitstring: 1, is_boolean: 1, is_float: 1, is_function: 1, ...]}], lexical_tracker: #PID<0.1337.0>, line: 241, macro_aliases: [], macros: [{Ex2ms, [fun: 1]}, {Kernel, [!: 1, &&: 2, ..: 2, ..//: 3, <>: 2, @: 1, alias!: 1, and: 2, binding: 0, binding: 1, def: 1, def: 2, defdelegate: 2, defexception: 1, defguard: 1, defguardp: 1, defimpl: 2, defimpl: 3, defmacro: 1, defmacro: 2, defmacrop: 1, defmacrop: 2, defmodule: 2, defoverridable: 1, defp: 1, defp: 2, defprotocol: 2, defstruct: 1, destructure: 2, get_and_update_in: 2, if: 2, in: 2, is_exception: 1, is_exception: 2, is_nil: 1, is_struct: 1, ...]}], module: ExRated, requires: [Ex2ms, GenServer, Kernel, Kernel.Typespec], ...>
    (ex2ms 1.6.0) lib/ex2ms.ex:213: Ex2ms.translate_param/2
    (ex2ms 1.6.0) lib/ex2ms.ex:184: Ex2ms.translate_head/2
    (ex2ms 1.6.0) lib/ex2ms.ex:116: Ex2ms.translate_clause/2
    (elixir 1.13.0-rc.0) lib/enum.ex:1593: Enum."-map/2-lists^map/1-0-"/2
    (ex2ms 1.6.0) expanding macro: Ex2ms.fun/1
    lib/ex_rated.ex:241: ExRated.delete_bucket/2

Memory leak problem

Hey!
Thanks for a great library.
We use it in production under high load, but after one month or so we experienced out of memory on the server. We found that the reason is this library.
Our usage example (it's a plug):

# SKIP
  defp check_rate(conn, options) do
    interval_milliseconds = options[:interval_seconds] * 1000
    max_requests = options[:max_requests]
    ExRated.check_rate(bucket_name(conn), interval_milliseconds, max_requests)
  end

  # Bucket name should be a combination of ip address and request path, like so:
  #
  # "127.0.0.1:/api/v1/authorizations"
  defp bucket_name(conn) do
    path = Enum.join(conn.path_info, "/")
    ip   = conn.remote_ip |> Tuple.to_list |> Enum.join(".")
    "#{ip}:#{path}"
  end
# SKIP

And configurations: config :ex_rated, :timeout, 600_000
In my controller: plug MyApp.Plug.RateLimit, max_requests: 5, interval_seconds: 60

Maybe you have any ideas what might be a problem? Of course I'll try to investigate it and if I find a solution, will create a PR.

Open to a Pluggable Storage adapter refactor?

Hey there!

Are you open to a PR that pulls the datastore (ets) usage into an adapter style? We're looking to implement rate limiting, but would like something that works across nodes in our cluster.

We'd probably pull the ets implementation into an included adapter, and write another adapter for something like redis as a separate repo.

Elixir Version for ex2ms

ex2ms requires an older version of Elixir. mix deps.get shows this:

warning: the dependency :ex2ms requires Elixir "<= 1.3.0" but you are running on v1.3.4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.