tiny_rl 1.1.0 → 2.0.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tiny_rl.rb +115 -2
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31e146834dcf046d74daa459ecf49be6a1a3f640443c807a8177fda51f8d5b8e
4
- data.tar.gz: 1874d4946e1b4733e32b0e56e0d5ba54e69f5ec4e4613cc3bb3ffb268a6a591d
3
+ metadata.gz: 2ff77bc53d01edfc80ddab66a55e6cca3333bb943199b8e707796fb950423bed
4
+ data.tar.gz: 0cee04760a9250243b7bcb715d437d958fa8d9ff0deb8d5df169d5972c05c695
5
5
  SHA512:
6
- metadata.gz: 101faf881ca40f0c18163e9c9be50750393258eb1b5f77d5ab125d597a752b61aa828492073f14c66dc07d1a51c91f1877aecefec779ae72b503b5aad5b23f15
7
- data.tar.gz: 90839e1152797de61e32bbe97d356bee46d3bc0a2c947ee1c8ea459a91128d8efc642d5b54d3cb29e3441185087c7ed1223da71875d388ccb68f11c9de396a43
6
+ metadata.gz: 37640f7aeaa30e0f65f082a50501d6a6b6f1f538f2c20424e18d5c6eeeb4ebab33a1414aaa743272dd4363c59b8ce3774c37f01e9cdd2adf9596719305fc84d2
7
+ data.tar.gz: f52bad20a8ec1c15438d8c66a649ab4d6b1faa522d4f3b853d4228e09206d36267f3cf1d88b91d8fdb353b5a513d5cc67c7a77aab0fc67fac2f2c9739e63a27f
data/lib/tiny_rl.rb CHANGED
@@ -1,2 +1,115 @@
1
- require_relative 'tiny_rl/job'
2
- require_relative 'tiny_rl/exec'
1
+ require 'time'
2
+
3
+ # this class simply tracks a rate limit, the timestamps of things you want to
4
+ # limit, and allows you the check both the used capacity and whether or not the
5
+ # rate limit has been reached. there's some additional tracking of the total
6
+ # number of jobs, dropped jobs, and errored jobs, just for some simple
7
+ # accounting.
8
+ #
9
+ # Usage:
10
+ # > rl = TinyRl.new(5, TinyRl::MINUTE, :drop)
11
+ # => <a rate limiter that will drop method calls over a rate of 5 per minute>
12
+ # > 10.times{ rl.track }
13
+ # > rl.used_capacity
14
+ # => 5
15
+ # > rl.at_capacity?
16
+ # => true
17
+ #
18
+ # if you use the :error strategy, then an instance of
19
+ # TinyRl::ExceededRateLimitError will be raised when you exceed the rate limit
20
+ # when calling #track.
21
+ #
22
+ # if you'd like to check the capacity of the TinyRl before taking some action,
23
+ # then check either TinyRl#at_capacity? or TinyRl#used_capacity as appropriate.
24
+ class TinyRl
25
+ STRATEGIES = %i(drop error)
26
+
27
+ SECOND = 1
28
+ MINUTE = SECOND * 60
29
+ HOUR = MINUTE * 60
30
+ DAY = HOUR * 24
31
+ WEEK = DAY * 7
32
+ MONTH = DAY * 30
33
+ YEAR = DAY * 365
34
+
35
+ # rate: number of requests per unit time
36
+ # per: unit of time (one of SECOND, MINUTE, etc.)
37
+ # you can pass any integer in as the `per' parameter and it will be
38
+ # interpreted as a number of seconds
39
+ # strategy: what to do when you're over the rate limit
40
+ # drop: drop the request, never perform the method call
41
+ # error: raise an exception when rate exceeded
42
+ def initialize(limit, per, strategy)
43
+ raise TinyRlInvalidStrategyError.new("Strategy `#{strategy.inspect}' is not one of the allowed strategies") unless STRATEGIES.include?(strategy)
44
+ @total_jobs = 0
45
+ @dropped_jobs = 0
46
+ @errored_jobs = 0
47
+ @limit = limit
48
+ @per = per
49
+ @strategy = strategy
50
+
51
+ @job_call_times = []
52
+ end
53
+
54
+ # this method will track the timestamp of something that you want to rate
55
+ # limit. this is generic so that you cand rate limit a bunch of things
56
+ # collectively with a single TinyRl instance.
57
+ #
58
+ # for strategy :drop
59
+ # returns true if the job was run, false if it was dropped
60
+ # for strategy :error
61
+ # returns true if the job was run, raises an exception otherwise
62
+ def track
63
+ @total_jobs += 1
64
+
65
+ if at_capacity?
66
+ case @strategy
67
+ when :drop
68
+ @dropped_jobs += 1
69
+ when :error
70
+ @errored_jobs += 1
71
+ raise TinyRlExceededRateLimitError.new("Rate limit of #{@limit} per #{@per} sec exceeded")
72
+ end
73
+ false
74
+ else
75
+ @job_call_times << Time.now
76
+ true
77
+ end
78
+ end
79
+
80
+ # returns true if the rate limit has currently been reached, false otherwise
81
+ def at_capacity?
82
+ _clear_old_jobs
83
+ @job_call_times.length == @limit
84
+ end
85
+
86
+ # useful for monitoring by consuming programs, and for issuing warnings when
87
+ # you're close, but not over, your limit
88
+ def used_capacity
89
+ _clear_old_jobs
90
+ @job_call_times.length
91
+ end
92
+
93
+ # removes old jobs before. used before checking capacity
94
+ def _clear_old_jobs
95
+ loop do
96
+ break if (@job_call_times.length == 0) || (@job_call_times.first >= (Time.now - @per))
97
+ @job_call_times.shift
98
+ end
99
+ end
100
+
101
+ # for debugging, really
102
+ def to_s
103
+ <<~TO_S
104
+ limit: #{@limit} per #{@per} seconds
105
+ at_capacity?: #{at_capacity?}
106
+ used_capacity: #{used_capacity}
107
+ total_jobs: #{@total_jobs}
108
+ dropped_jobs: #{@dropped_jobs}
109
+ errored_jobs: #{@errored_jobs}
110
+ TO_S
111
+ end
112
+ end
113
+
114
+ class TinyRlExceededRateLimitError < RuntimeError; end
115
+ class TinyRlInvalidStrategyError < RuntimeError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_rl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Lunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-25 00:00:00.000000000 Z
11
+ date: 2022-12-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: a tiny rate limiter with almost no features
14
14
  email: jefflunt@gmail.com