pecorino 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfcca80bad8895a9b45ed8a3ed0afe06b52a0c6a851d8506b102a82b5375c2a3
4
- data.tar.gz: 7f57dd803797acfdf29a8d7cae31854528012af249887ae49e398b992a48f9d4
3
+ metadata.gz: 2f94aa734cb0bb50657f5484bbdd8bfcaabc7c2d6b7d9329361d41456ba49db6
4
+ data.tar.gz: 97e01c53e828092ce60be1412a288a70446ec9cc5ab783a6fa6e3ba147de1ee5
5
5
  SHA512:
6
- metadata.gz: dea8f3c693c1d9b5412cdc50cd333d1de6b92d90ec1358ae3c3f5d03fef685649bccd4f6fcc8757e04aa7aba5b13411b325b69b374be0bf30a86d10c16977613
7
- data.tar.gz: ee00695a622947cbdc14a300d73d06a850abd2357b3c1357e7904e7c93f2ea5513a4edbb98349a896b5b39cc57b08e889a221e78319e5344f778000dd7059f06
6
+ metadata.gz: 4d83ebb84009492403ca8950d181f4689b42782ab3f65f7fe5091cab92fc4f739ecd64625449ab784309a122f09e62525c262c71f3c602d3d538f4ac511a78e3
7
+ data.tar.gz: 93d3a2845713c6dc71ff1f35e5433fcbca6837fdb336db00a7e403e5719f1e65eadac7dc819301c8886d40dc3f99c59b196d64004145de786d0875c42b98e635
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [0.3.0] - 2024-01-18
2
+
3
+ - Allow `over_time` in addition to `leak_rate`, which is a more intuitive parameter to tweak
4
+ - Set default `block_for` to the time it takes the bucket to leak out completely instead of 30 seconds
5
+
1
6
  ## [0.2.0] - 2024-01-09
2
7
 
3
8
  - [Add support for SQLite](https://github.com/cheddar-me/pecorino/pull/9)
data/README.md CHANGED
@@ -25,7 +25,7 @@ And then execute:
25
25
  Once the installation 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.
26
26
 
27
27
  ```ruby
28
- throttle = Pecorino::Throttle.new(key: "vault", leak_rate: 5, capacity: 5)
28
+ throttle = Pecorino::Throttle.new(key: "vault", over_time: 1.second, capacity: 5)
29
29
  throttle.request!
30
30
  ```
31
31
  In a Rails controller you can then rescue from this exception to render the appropriate response:
@@ -58,7 +58,7 @@ return render :capacity_exceeded unless throttle.able_to_accept?
58
58
  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:
59
59
 
60
60
  ```ruby
61
- throttle = Pecorino::Throttle.new(key: "wallet_t_#{current_user.id}", leak_rate: 100 / 60.0 / 60.0, capacity: 100, block_for: 60*60*3)
61
+ throttle = Pecorino::Throttle.new(key: "wallet_t_#{current_user.id}", over_time_: 1.hour, capacity: 100, block_for: 60*60*3)
62
62
  throttle.request!(20) # Attempt to withdraw 20 dollars
63
63
  throttle.request!(20) # Attempt to withdraw 20 dollars more
64
64
  throttle.request!(20) # Attempt to withdraw 20 dollars more
@@ -59,21 +59,40 @@ class Pecorino::LeakyBucket
59
59
  end
60
60
  end
61
61
 
62
+ # The key (name) of the leaky bucket
63
+ # @return [String]
64
+ attr_reader :key
65
+
66
+ # The leak rate (tokens per second) of the bucket
67
+ # @return [Float]
68
+ attr_reader :leak_rate
69
+
70
+ # The capacity of the bucket in tokens
71
+ # @return [Float]
72
+ attr_reader :capacity
73
+
62
74
  # Creates a new LeakyBucket. The object controls 1 row in the database is
63
75
  # specific to the bucket key.
64
76
  #
65
77
  # @param key[String] the key for the bucket. The key also gets used
66
78
  # to derive locking keys, so that operations on a particular bucket
67
79
  # are always serialized.
68
- # @param leak_rate[Float] the leak rate of the bucket, in tokens per second
80
+ # @param leak_rate[Float] the leak rate of the bucket, in tokens per second.
81
+ # Either `leak_rate` or `over_time` can be used, but not both.
82
+ # @param over_time[#to_f] over how many seconds the bucket will leak out to 0 tokens.
83
+ # The value is assumed to be the number of seconds
84
+ # - or a duration which returns the number of seconds from `to_f`.
85
+ # Either `leak_rate` or `over_time` can be used, but not both.
69
86
  # @param capacity[Numeric] how many tokens is the bucket capped at.
70
87
  # Filling up the bucket using `fillup()` will add to that number, but
71
88
  # the bucket contents will then be capped at this value. So with
72
89
  # bucket_capacity set to 12 and a `fillup(14)` the bucket will reach the level
73
90
  # of 12, and will then immediately start leaking again.
74
- def initialize(key:, leak_rate:, capacity:)
91
+ def initialize(key:, capacity:, leak_rate: nil, over_time: nil)
92
+ raise ArgumentError, "Either leak_rate: or over_time: must be specified" if leak_rate.nil? && over_time.nil?
93
+ raise ArgumentError, "Either leak_rate: or over_time: may be specified, but not both" if leak_rate && over_time
94
+ @leak_rate = leak_rate || (over_time.to_f / capacity)
75
95
  @key = key
76
- @leak_rate = leak_rate.to_f
77
96
  @capacity = capacity.to_f
78
97
  end
79
98
 
@@ -43,13 +43,14 @@ class Pecorino::Throttle
43
43
  end
44
44
 
45
45
  # @param key[String] the key for both the block record and the leaky bucket
46
- # @param block_for[Numeric] the number of seconds to block any further requests for
46
+ # @param block_for[Numeric] the number of seconds to block any further requests for. Defaults to time it takes
47
+ # the bucket to leak out to the level of 0
47
48
  # @param leaky_bucket_options Options for `Pecorino::LeakyBucket.new`
48
49
  # @see PecorinoLeakyBucket.new
49
- def initialize(key:, block_for: 30, **)
50
- @key = key.to_s
51
- @block_for = block_for.to_f
50
+ def initialize(key:, block_for: nil, **)
52
51
  @bucket = Pecorino::LeakyBucket.new(key:, **)
52
+ @key = key.to_s
53
+ @block_for = block_for ? block_for.to_f : (@bucket.capacity / @bucket.leak_rate)
53
54
  end
54
55
 
55
56
  # Tells whether the throttle will let this number of requests pass without raising
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pecorino
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pecorino
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-09 00:00:00.000000000 Z
11
+ date: 2024-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord