Skip to content

Commit

Permalink
Rename all the things
Browse files Browse the repository at this point in the history
  • Loading branch information
julik committed Oct 31, 2023
1 parent 6366779 commit 1fae3d0
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 43 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

source "https://rubygems.org"

# Specify your gem's dependencies in raclette.gemspec
# Specify your gem's dependencies in pecorino.gemspec
gemspec

gem "rake", "~> 13.0"
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
raclette (0.1.0)
pecorino (0.1.0)
activerecord (~> 7)
pg

Expand Down Expand Up @@ -148,7 +148,7 @@ PLATFORMS
DEPENDENCIES
activesupport (~> 7)
minitest (~> 5.0)
raclette!
pecorino!
rails (~> 7)
rake (~> 13.0)

Expand Down
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Raclette
# Pecorino

Raclette is a rate limiter based on the concept of leaky buckets. It uses your DB as the storage backend for the throttles. It is compact, easy to install, and does not require additional infrastructure. The approach used by Raclette has been previously used by [prorate](https://github.com/WeTransfer/prorate) with Redis, and that approach has proven itself.
Pecorino is a rate limiter based on the concept of leaky buckets. It uses your DB as the storage backend for the throttles. It is compact, easy to install, and does not require additional infrastructure. The approach used by Pecorino has been previously used by [prorate](https://github.com/WeTransfer/prorate) with Redis, and that approach has proven itself.

Raclette is designed to integrate seamlessly into any Rails application using a Postgres database (at the moment there is no MySQL support, we would be delighted if you could add it).
Pecorino is designed to integrate seamlessly into any Rails application using a Postgres database (at the moment there is no MySQL support, we would be delighted if you could add it).

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'raclette'
gem 'pecorino'
```

And then execute:
Expand All @@ -18,19 +18,19 @@ And then execute:

Or install it yourself as:

$ gem install raclette
$ gem install pecorino

## Usage

First, add and run the migration to create the raclette tables:
First, add and run the migration to create the pecorino tables:

$ bin/rails g raclette:install
$ bin/rails g pecorino:install
$ bin/rails db:migrate

Once that is done, you can use Raclette to start defining your throttles. Imagine you have a resource called `vault` and you want to limit the number of updates to it to 5 per second. To achieve that, instantiate a new `Throttle` in your controller or job code, and then trigger it using `Throttle#request!`. A call to `request!` registers 1 token getting added to the bucket. If the bucket is full, or the throttle is currently in "block" mode (has recently been triggered), a `Raclette::Throttle::Throttled` exception will be raised.
Once that is done, you can use Pecorino to start defining your throttles. Imagine you have a resource called `vault` and you want to limit the number of updates to it to 5 per second. To achieve that, instantiate a new `Throttle` in your controller or job code, and then trigger it using `Throttle#request!`. A call to `request!` registers 1 token getting added to the bucket. If the bucket is full, or the throttle is currently in "block" mode (has recently been triggered), a `Pecorino::Throttle::Throttled` exception will be raised.

```ruby
throttle = Raclette::Throttle.new(key: "vault", leak_rate: 5, capacity: 5)
throttle = Pecorino::Throttle.new(key: "vault", leak_rate: 5, capacity: 5)
throttle.request!
```

Expand All @@ -45,7 +45,7 @@ return render :capacity_exceeded unless throttle.able_to_accept?
If you are dealing with a metered resource (like throughput, money, amount of storage...) you can supply the number of tokens to either `request!` or `able_to_accept?` to indicate the desired top-up of the leaky bucket. For example, if you are maintaining user wallets and want to ensure no more than 100 dollars may be taken from the wallet within a certain amount of time, you can do it like so:

```ruby
throttle = Raclette::Throttle.new(key: "wallet_t_#{current_user.id}", leak_rate: 100 / 60.0 / 60.0, capacity: 100, block_for: 60*60*3)
throttle = Pecorino::Throttle.new(key: "wallet_t_#{current_user.id}", leak_rate: 100 / 60.0 / 60.0, capacity: 100, block_for: 60*60*3)
throttle.request!(20) # Attempt to withdraw 20 dollars
throttle.request!(20) # Attempt to withdraw 20 dollars more
throttle.request!(20) # Attempt to withdraw 20 dollars more
Expand All @@ -57,10 +57,10 @@ throttle.request!(2) # Attempt to withdraw 2 dollars more, will raise `Throttled
Sometimes you don't want to use a throttle, but you want to track the amount added to the leaky bucket over time. If this is what you need, you can use the `LeakyBucket` class:

```ruby
b = Raclette::LeakyBucket.new(key: "some_b", capacity: 100, leak_rate: 5)
b.fillup(2) #=> Raclette::LeakyBucket::State(full?: false, level: 2.0)
b = Pecorino::LeakyBucket.new(key: "some_b", capacity: 100, leak_rate: 5)
b.fillup(2) #=> Pecorino::LeakyBucket::State(full?: false, level: 2.0)
sleep 0.2
b.state #=> Raclette::LeakyBucket::State(full?: false, level: 1.8)
b.state #=> Pecorino::LeakyBucket::State(full?: false, level: 1.8)
```

Check out the inline YARD documentation for more options.
Expand All @@ -69,7 +69,7 @@ Check out the inline YARD documentation for more options.

We recommend running the following bit of code every couple of hours (via cron or similar) to delete the stale blocks and leaky buckets from the system:

Raclette.prune!
Pecorino.prune!

## Development

Expand All @@ -79,12 +79,12 @@ To install this gem onto your local machine, run `bundle exec rake install`. To

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/julik/raclette. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/julik/raclette/blob/main/CODE_OF_CONDUCT.md).
Bug reports and pull requests are welcome on GitHub at https://github.com/julik/pecorino. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/julik/pecorino/blob/main/CODE_OF_CONDUCT.md).

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

## Code of Conduct

Everyone interacting in the Raclette project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/julik/raclette/blob/main/CODE_OF_CONDUCT.md).
Everyone interacting in the Pecorino project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/julik/pecorino/blob/main/CODE_OF_CONDUCT.md).
2 changes: 1 addition & 1 deletion lib/raclette/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Raclette
module Pecorino
VERSION = "0.1.0"
end
14 changes: 7 additions & 7 deletions raclette.gemspec → pecorino.gemspec
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# frozen_string_literal: true

require_relative "lib/raclette/version"
require_relative "lib/pecorino/version"

Gem::Specification.new do |spec|
spec.name = "raclette"
spec.version = Raclette::VERSION
spec.name = "pecorino"
spec.version = Pecorino::VERSION
spec.authors = ["Julik Tarkhanov"]
spec.email = ["[email protected]"]

spec.summary = "Database-based rate limiter using leaky buckets"
spec.description = "Raclette allows you to define throttles and rate meters for your metered resources, all through your standard DB"
spec.homepage = "https://github.com/cheddar-me/raclette"
spec.description = "Pecorino allows you to define throttles and rate meters for your metered resources, all through your standard DB"
spec.homepage = "https://github.com/cheddar-me/pecorino"
spec.license = "MIT"
spec.required_ruby_version = ">= 2.4.0"

# spec.metadata["allowed_push_host"] = "TODO: Set to 'https://mygemserver.com'"

spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = "https://github.com/cheddar-me/raclette"
spec.metadata["changelog_uri"] = "https://github.com/cheddar-me/raclette/CHANGELOG.md"
spec.metadata["source_code_uri"] = "https://github.com/cheddar-me/pecorino"
spec.metadata["changelog_uri"] = "https://github.com/cheddar-me/pecorino/CHANGELOG.md"

# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
Expand Down
8 changes: 4 additions & 4 deletions test/leaky_bucket_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class LeakyBucketTest < ActiveSupport::TestCase
test "on iteration #{n} accepts a certain number of tokens and returns the new bucket level" do
slow_test!

bucket = Raclette::LeakyBucket.new(key: Random.uuid, leak_rate: 1.1, capacity: 15)
bucket = Pecorino::LeakyBucket.new(key: Random.uuid, leak_rate: 1.1, capacity: 15)
assert_in_delta bucket.state.level, 0, 0.0001

state = bucket.fillup(20)
Expand All @@ -30,7 +30,7 @@ class LeakyBucketTest < ActiveSupport::TestCase
test "does not allow a bucket to be created with a negative value" do
slow_test!

bucket = Raclette::LeakyBucket.new(key: Random.uuid, leak_rate: 1.1, capacity: 15)
bucket = Pecorino::LeakyBucket.new(key: Random.uuid, leak_rate: 1.1, capacity: 15)
assert_in_delta bucket.state.level, 0, 0.0001

state = bucket.fillup(-10)
Expand All @@ -40,7 +40,7 @@ class LeakyBucketTest < ActiveSupport::TestCase
test "allows check for the bucket leaking out" do
slow_test!

bucket = Raclette::LeakyBucket.new(key: Random.uuid, leak_rate: 1.1, capacity: 15)
bucket = Pecorino::LeakyBucket.new(key: Random.uuid, leak_rate: 1.1, capacity: 15)
assert_in_delta bucket.state.level, 0, 0.0001

state = bucket.fillup(10)
Expand All @@ -54,7 +54,7 @@ class LeakyBucketTest < ActiveSupport::TestCase
test "allows the bucket to leak out completely" do
slow_test!

bucket = Raclette::LeakyBucket.new(key: Random.uuid, leak_rate: 2, capacity: 1)
bucket = Pecorino::LeakyBucket.new(key: Random.uuid, leak_rate: 2, capacity: 1)
assert_predicate bucket.fillup(1), :full?

sleep(0.25)
Expand Down
4 changes: 2 additions & 2 deletions test/raclette_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

require "test_helper"

class RacletteTest < Minitest::Test
class PecorinoTest < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::Raclette::VERSION
refute_nil ::Pecorino::VERSION
end

def test_it_does_something_useful
Expand Down
2 changes: 1 addition & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "raclette"
require "pecorino"

require "minitest/autorun"
require "rails/test_help"
18 changes: 9 additions & 9 deletions test/throttle_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require "test_helper"

class RacletteThrottleTest < ActiveSupport::TestCase
class PecorinoThrottleTest < ActiveSupport::TestCase
self.use_transactional_tests = false

def random_leaky_bucket_name(random: Random.new)
Expand All @@ -13,22 +13,22 @@ def random_leaky_bucket_name(random: Random.new)
test "throttles using request!() and blocks" do
slow_test!

throttle = Raclette::Throttle.new(key: random_leaky_bucket_name, leak_rate: 30, capacity: 30, block_for: 3)
throttle = Pecorino::Throttle.new(key: random_leaky_bucket_name, leak_rate: 30, capacity: 30, block_for: 3)

29.times do
throttle.request!
end

# Depending on timing either the 31st or the 30th request may start to throttle
err = assert_raises Raclette::Throttle::Throttled do
err = assert_raises Pecorino::Throttle::Throttled do
loop { throttle.request! }
end

assert_in_delta err.retry_after, 3, 0.5
sleep 0.5

# Ensure we are still throttled
err = assert_raises Raclette::Throttle::Throttled do
err = assert_raises Pecorino::Throttle::Throttled do
throttle.request!
end
assert_equal throttle, err.throttle
Expand All @@ -43,7 +43,7 @@ def random_leaky_bucket_name(random: Random.new)
test "still throttles using request() without raising exceptions" do
slow_test!

throttle = Raclette::Throttle.new(key: random_leaky_bucket_name, leak_rate: 30, capacity: 30, block_for: 3)
throttle = Pecorino::Throttle.new(key: random_leaky_bucket_name, leak_rate: 30, capacity: 30, block_for: 3)

20.times do
state = throttle.request
Expand Down Expand Up @@ -74,14 +74,14 @@ def random_leaky_bucket_name(random: Random.new)
test "able_to_accept? returns the prediction whether the throttle will accept" do
slow_test!

throttle = Raclette::Throttle.new(key: random_leaky_bucket_name, leak_rate: 30, capacity: 30, block_for: 2)
throttle = Pecorino::Throttle.new(key: random_leaky_bucket_name, leak_rate: 30, capacity: 30, block_for: 2)

assert throttle.able_to_accept?
assert throttle.able_to_accept?(29)
refute throttle.able_to_accept?(31)

# Depending on timing either the 30th or the 31st request may start to throttle
assert_raises Raclette::Throttle::Throttled do
assert_raises Pecorino::Throttle::Throttled do
loop { throttle.request! }
end
refute throttle.able_to_accept?
Expand All @@ -93,14 +93,14 @@ def random_leaky_bucket_name(random: Random.new)
test "starts to throttle sooner with a higher fillup rate" do
slow_test!

throttle = Raclette::Throttle.new(key: random_leaky_bucket_name, leak_rate: 30, capacity: 30, block_for: 3)
throttle = Pecorino::Throttle.new(key: random_leaky_bucket_name, leak_rate: 30, capacity: 30, block_for: 3)

15.times do
throttle.request!(2)
end

# Depending on timing either the 31st or the 30th request may start to throttle
err = assert_raises Raclette::Throttle::Throttled do
err = assert_raises Pecorino::Throttle::Throttled do
loop { throttle.request! }
end

Expand Down

0 comments on commit 1fae3d0

Please sign in to comment.