tiny_rl 1.2.0 → 2.1.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 +119 -2
  3. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfd587321069791e9d18bbec07594ae54bef5df6198245fbfb54928a11a22fcc
4
- data.tar.gz: 0be56b681489bb625133a441d3503d2aa5a1591537c278807b47e1bfcaf48d67
3
+ metadata.gz: 9d9dfba7e07aff49aacdce244cf39bc54f87afb03ab40823ee5e7aa56863f80a
4
+ data.tar.gz: fb9a2e33e35858d05da57d5976ab2db87644b694c67ca35df8240b959f0e0580
5
5
  SHA512:
6
- metadata.gz: bb9867ea40155fff57133183c3280c557b64d905a2540ae7d06c7e5944bd9d4bdbd0887220cd2d631d0a16d96ab6779bc97131e1df46f4eed5b606b4bf86ce13
7
- data.tar.gz: fd95d64e2fc4de0801bc9658f2478b73e2f0c1b86633e977af286cc64d96d4885d8a710fa9b7205a20281cb8d94192eee0804464f158b04af52fad57c130ffa9
6
+ metadata.gz: 6b823cdb7a65a0e35a15ef298a383346809a4d443b6132b4221f21c025cdfbd797a95982fabe89a3761d8b601d7cdb120f011c38b783ad0cab09b22033fb8a8c
7
+ data.tar.gz: 80e43f6a8e144cdadf44f3a660960cbb7876c74259d07652dc838de1e8d32ca790684fa8809b1a44b734fa4ac5c69f0bdbaa49dc97acf71121b1ca8a952b152f
data/lib/tiny_rl.rb CHANGED
@@ -1,2 +1,119 @@
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
+ attr_reader :limit,
36
+ :per,
37
+ :strategy
38
+
39
+ # rate: number of requests per unit time
40
+ # per: unit of time (one of SECOND, MINUTE, etc.)
41
+ # you can pass any integer in as the `per' parameter and it will be
42
+ # interpreted as a number of seconds
43
+ # strategy: what to do when you're over the rate limit
44
+ # drop: drop the request, never perform the method call
45
+ # error: raise an exception when rate exceeded
46
+ def initialize(limit, per, strategy)
47
+ raise TinyRlInvalidStrategyError.new("Strategy `#{strategy.inspect}' is not one of the allowed strategies") unless STRATEGIES.include?(strategy)
48
+ @total_jobs = 0
49
+ @dropped_jobs = 0
50
+ @errored_jobs = 0
51
+ @limit = limit
52
+ @per = per
53
+ @strategy = strategy
54
+
55
+ @job_call_times = []
56
+ end
57
+
58
+ # this method will track the timestamp of something that you want to rate
59
+ # limit. this is generic so that you cand rate limit a bunch of things
60
+ # collectively with a single TinyRl instance.
61
+ #
62
+ # for strategy :drop
63
+ # returns true if the job was run, false if it was dropped
64
+ # for strategy :error
65
+ # returns true if the job was run, raises an exception otherwise
66
+ def track
67
+ @total_jobs += 1
68
+
69
+ if at_capacity?
70
+ case @strategy
71
+ when :drop
72
+ @dropped_jobs += 1
73
+ when :error
74
+ @errored_jobs += 1
75
+ raise TinyRlExceededRateLimitError.new("Rate limit of #{@limit} per #{@per} sec exceeded")
76
+ end
77
+ false
78
+ else
79
+ @job_call_times << Time.now
80
+ true
81
+ end
82
+ end
83
+
84
+ # returns true if the rate limit has currently been reached, false otherwise
85
+ def at_capacity?
86
+ _clear_old_jobs
87
+ @job_call_times.length == @limit
88
+ end
89
+
90
+ # useful for monitoring by consuming programs, and for issuing warnings when
91
+ # you're close, but not over, your limit
92
+ def used_capacity
93
+ _clear_old_jobs
94
+ @job_call_times.length
95
+ end
96
+
97
+ # removes old jobs before. used before checking capacity
98
+ def _clear_old_jobs
99
+ loop do
100
+ break if (@job_call_times.length == 0) || (@job_call_times.first >= (Time.now - @per))
101
+ @job_call_times.shift
102
+ end
103
+ end
104
+
105
+ # for debugging, really
106
+ def to_s
107
+ <<~TO_S
108
+ limit: #{@limit} per #{@per} seconds
109
+ at_capacity?: #{at_capacity?}
110
+ used_capacity: #{used_capacity}
111
+ total_jobs: #{@total_jobs}
112
+ dropped_jobs: #{@dropped_jobs}
113
+ errored_jobs: #{@errored_jobs}
114
+ TO_S
115
+ end
116
+ end
117
+
118
+ class TinyRlExceededRateLimitError < RuntimeError; end
119
+ class TinyRlInvalidStrategyError < RuntimeError; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_rl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Lunt