ruby-limiter 2.3.0 → 2.3.1

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: b2f455d4d68023ae8d22bffc74ea4b6ad7abff350d6eb907e64c8e7db7c46caf
4
- data.tar.gz: 18b08614e86f69ae5f3d1cb8209b2f9a3c70364567366fd27464d3a27cad37ee
3
+ metadata.gz: 3fa5f9a22fef901283796de71c5f8f9f2e3c15a0ec53437df63a8fd68ab6d68d
4
+ data.tar.gz: 0fb719626ac5314d31d195d0f5058bdb9830355a25648fcb45bed3317e6c2c39
5
5
  SHA512:
6
- metadata.gz: 6a2e10e3516975acd55786d335431a51f85946000d35fcc4c7bc18b22dcd59d9d651d09f8c4b0af597452b8a737e797cff7d1ca141e6d8ebbc0e2e5f05b96466
7
- data.tar.gz: '08c8276f451020a58782ac27d030be523cab952a22db219c01ee1fde303a2d3df1eb7a76a6013a5418d0986a41bfc24794a5fb2a31c1cff9e90bc4c416227b00'
6
+ metadata.gz: da3c41e851f8d076dbbae9cfdc6679c9c439091f83b84a15488c1a62d77e0f683a337acfb31b5dad4bc458cbe967d1929a1a50c3b7cd177ccdb1b12b80281335
7
+ data.tar.gz: 867e83f64c51472488d584b7eb751b2c8b9836ff8b771f29718d0a55023b95166dde55fe08de217f456030059e279551340c870eceb4ffef0cd5b5f033e7e2f8
@@ -0,0 +1,15 @@
1
+ version: 2
2
+ registries:
3
+ github-com:
4
+ type: git
5
+ url: https://github.com
6
+ username: ${{secrets.DEPENDENCIES_GITHUB_USER}}
7
+ password: ${{secrets.DEPENDENCIES_GITHUB_TOKEN}}
8
+ updates:
9
+ - package-ecosystem: bundler
10
+ directory: "/"
11
+ schedule:
12
+ interval: daily
13
+ open-pull-requests-limit: 100
14
+ insecure-external-code-execution: allow
15
+ registries: "*"
@@ -30,7 +30,6 @@ jobs:
30
30
  - name: Set up Ruby
31
31
  uses: ruby/setup-ruby@v1
32
32
  with:
33
- ruby-version: 3.2
34
33
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
35
34
  - name: Rubocop run
36
35
  run: |
@@ -48,7 +47,7 @@ jobs:
48
47
  runs-on: ubuntu-latest
49
48
  strategy:
50
49
  matrix:
51
- ruby-version: ['3.0', '3.1', '3.2']
50
+ ruby-version: ['3.2', '3.3', '3.4']
52
51
 
53
52
  steps:
54
53
  - uses: actions/checkout@v3
@@ -0,0 +1,23 @@
1
+ # .github/workflows/cla.yml
2
+ name: Contributor License Agreement (CLA)
3
+
4
+ on:
5
+ pull_request_target:
6
+ types: [opened, synchronize]
7
+ issue_comment:
8
+ types: [created]
9
+
10
+ jobs:
11
+ cla:
12
+ runs-on: ubuntu-latest
13
+ if: |
14
+ (github.event.issue.pull_request
15
+ && !github.event.issue.pull_request.merged_at
16
+ && contains(github.event.comment.body, 'signed')
17
+ )
18
+ || (github.event.pull_request && !github.event.pull_request.merged)
19
+ steps:
20
+ - uses: Shopify/shopify-cla-action@v1
21
+ with:
22
+ github-token: ${{ secrets.GITHUB_TOKEN }}
23
+ cla-token: ${{ secrets.CLA_TOKEN }}
@@ -0,0 +1,30 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ release:
9
+ if: github.repository == 'Shopify/limiter'
10
+ runs-on: ubuntu-latest
11
+
12
+ permissions:
13
+ contents: write
14
+ id-token: write
15
+
16
+ environment: release
17
+
18
+ steps:
19
+ - name: Checkout repository
20
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21
+ with:
22
+ persist-credentials: false
23
+
24
+ - name: Set up Ruby
25
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
26
+ with:
27
+ bundler-cache: true
28
+
29
+ - name: Release gem on RubyGems.org
30
+ uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
data/.gitignore CHANGED
@@ -7,4 +7,3 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  .rubocop-https*
10
- Gemfile.lock
data/.rubocop.yml CHANGED
@@ -7,7 +7,6 @@ require:
7
7
 
8
8
  AllCops:
9
9
  NewCops: enable
10
- TargetRubyVersion: 3.2
11
10
 
12
11
  Naming/FileName:
13
12
  Exclude:
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v2.3.1
4
+
5
+ - balanced mode now keeps calls `interval / size` apart after the queue has been idle; previously an idle interval let `size` calls through at once and the queue kept bursting until reset (#61)
6
+ - balanced ring is built from a single clock read (#62)
7
+
3
8
  ## v2.3.0
4
9
 
5
10
  - add support for resetting RateQueue
data/Gemfile.lock ADDED
@@ -0,0 +1,71 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ruby-limiter (2.3.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ code-scanning-rubocop (0.6.1)
11
+ rubocop (~> 1.0)
12
+ json (2.7.1)
13
+ language_server-protocol (3.17.0.3)
14
+ minitest (5.25.5)
15
+ minitest-focus (1.4.0)
16
+ minitest (>= 4, < 6)
17
+ mocha (2.1.0)
18
+ ruby2_keywords (>= 0.0.5)
19
+ parallel (1.24.0)
20
+ parser (3.3.0.5)
21
+ ast (~> 2.4.1)
22
+ racc
23
+ racc (1.7.3)
24
+ rainbow (3.1.1)
25
+ rake (13.3.0)
26
+ regexp_parser (2.9.0)
27
+ rexml (3.2.8)
28
+ strscan (>= 3.0.9)
29
+ rubocop (1.62.1)
30
+ json (~> 2.3)
31
+ language_server-protocol (>= 3.17.0)
32
+ parallel (~> 1.10)
33
+ parser (>= 3.3.0.2)
34
+ rainbow (>= 2.2.2, < 4.0)
35
+ regexp_parser (>= 1.8, < 3.0)
36
+ rexml (>= 3.2.5, < 4.0)
37
+ rubocop-ast (>= 1.31.1, < 2.0)
38
+ ruby-progressbar (~> 1.7)
39
+ unicode-display_width (>= 2.4.0, < 3.0)
40
+ rubocop-ast (1.31.2)
41
+ parser (>= 3.3.0.4)
42
+ rubocop-minitest (0.33.0)
43
+ rubocop (>= 1.39, < 2.0)
44
+ rubocop-rake (0.6.0)
45
+ rubocop (~> 1.0)
46
+ rubocop-shopify (2.15.1)
47
+ rubocop (~> 1.51)
48
+ ruby-progressbar (1.13.0)
49
+ ruby2_keywords (0.0.5)
50
+ strscan (3.1.0)
51
+ unicode-display_width (2.5.0)
52
+
53
+ PLATFORMS
54
+ arm64-darwin-23
55
+ ruby
56
+
57
+ DEPENDENCIES
58
+ bundler
59
+ code-scanning-rubocop (~> 0.6.1)
60
+ minitest (~> 5.20)
61
+ minitest-focus (~> 1.4)
62
+ mocha (~> 2.1)
63
+ rake (~> 13.0)
64
+ rubocop (~> 1.57)
65
+ rubocop-minitest (~> 0.33.0)
66
+ rubocop-rake (~> 0.6.0)
67
+ rubocop-shopify (~> 2.14)
68
+ ruby-limiter!
69
+
70
+ BUNDLED WITH
71
+ 2.6.9
data/README.md CHANGED
@@ -57,8 +57,8 @@ end
57
57
 
58
58
  #### Load balancing
59
59
 
60
- By default all calls to the `limit_method` will be bursted, e.g. as quick as possible, until the rate is exceeded.
61
- Then we wait for the remainder of the interval to continue. To even out the burst, an optional `balanced` parameter can be
60
+ By default all calls to the `limit_method` will be bursted, e.g. as quick as possible, until the rate is exceeded. Then
61
+ we wait for the remainder of the interval to continue. To even out the burst, an optional `balanced` parameter can be
62
62
  provided to enable interleaving between the method calls, e.g: `interleave = interval / size`.
63
63
 
64
64
  ``` ruby
@@ -79,7 +79,8 @@ For example: with an interval of 60 seconds and a rate of 60:
79
79
 
80
80
  There are times when it may be necessary to reset the rate limiter for a method, for example during testing.
81
81
 
82
- This can be done by calling `reset_method_limit!` on the class, where "method" is replaced with the name of the method being limited.
82
+ This can be done by calling `reset_method_limit!` on the class, where "method" is replaced with the name of the method
83
+ being limited.
83
84
 
84
85
  Given the example above, the following would reset the rate limit for the `tick` method during test setup:
85
86
 
@@ -96,8 +97,8 @@ end
96
97
  ### Advanced Usage
97
98
 
98
99
  In cases where the mixin is not appropriate the `RateQueue` class can be used directly. As in the mixin examples above,
99
- the `interval` parameter is optional (and defaults to 1 minute). It is also possible
100
- to provide the block to `RateQueue`, which will be executed on each limit hit (useful for metrics).
100
+ the `interval` parameter is optional (and defaults to 1 minute). It is also possible to provide the block to
101
+ `RateQueue`, which will be executed on each limit hit (useful for metrics).
101
102
 
102
103
  ``` ruby
103
104
  class Widget
@@ -118,7 +119,8 @@ end
118
119
 
119
120
  #### Resetting a RateQueue
120
121
 
121
- In some circumstances it may be desirable to reset a rate queue, for example after invoking an API that resets an external rate limit.
122
+ In some circumstances it may be desirable to reset a rate queue, for example after invoking an API that resets an
123
+ external rate limit.
122
124
 
123
125
  This can be done by calling `reset` on the queue.
124
126
 
@@ -130,9 +132,12 @@ This can be done by calling `reset` on the queue.
130
132
 
131
133
  ## Development
132
134
 
133
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
135
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can
136
+ also run `bin/console` for an interactive prompt that will allow you to experiment.
134
137
 
135
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
138
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the
139
+ version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version,
140
+ push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
136
141
 
137
142
  ## Contributing
138
143
 
data/dev.yml CHANGED
@@ -3,7 +3,7 @@
3
3
  name: limiter
4
4
 
5
5
  up:
6
- - ruby: 3.2.2
6
+ - ruby
7
7
  - bundler
8
8
 
9
9
  commands:
@@ -20,6 +20,7 @@ module Limiter
20
20
  @mutex.synchronize do
21
21
  @ring = @balanced ? balanced_ring : unbalanced_ring
22
22
  @head = 0
23
+ @last = nil
23
24
  end
24
25
  end
25
26
 
@@ -29,9 +30,9 @@ module Limiter
29
30
  @mutex.synchronize do
30
31
  time = @ring[@head]
31
32
 
32
- sleep_until(time + @interval)
33
+ sleep_until(next_time(time))
33
34
 
34
- @ring[@head] = clock.time
35
+ @last = @ring[@head] = clock.time
35
36
  @head = (@head + 1) % @size
36
37
  end
37
38
 
@@ -40,6 +41,13 @@ module Limiter
40
41
 
41
42
  private
42
43
 
44
+ def next_time(time)
45
+ deadline = time + @interval
46
+ return deadline unless @balanced && @last
47
+
48
+ [deadline, @last + gap].max
49
+ end
50
+
43
51
  def sleep_until(time)
44
52
  interval = time - clock.time
45
53
  return unless interval.positive?
@@ -57,7 +65,10 @@ module Limiter
57
65
  end
58
66
 
59
67
  def balanced_ring
60
- (0...@size).map { |i| base_time + (gap * i) }
68
+ start = base_time
69
+ step = gap
70
+
71
+ Array.new(@size) { |i| start + (step * i) }
61
72
  end
62
73
 
63
74
  def gap
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Limiter
4
- VERSION = "2.3.0"
4
+ VERSION = "2.3.1"
5
5
  end
data/limiter.gemspec CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.summary = "Simple Ruby rate limiting mechanism."
14
14
  spec.homepage = "https://github.com/Shopify/limiter"
15
15
  spec.license = "MIT"
16
- spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
16
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
17
17
 
18
18
  if spec.respond_to?(:metadata)
19
19
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-limiter
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - S. Brent Faulkner
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2023-10-29 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -150,19 +149,22 @@ dependencies:
150
149
  - - "~>"
151
150
  - !ruby/object:Gem::Version
152
151
  version: '2.14'
153
- description:
154
152
  email:
155
153
  - brent.faulkner@shopify.com
156
154
  executables: []
157
155
  extensions: []
158
156
  extra_rdoc_files: []
159
157
  files:
158
+ - ".github/dependabot.yml"
160
159
  - ".github/workflows/ci.yml"
161
- - ".github/workflows/gem-push.yml"
160
+ - ".github/workflows/cla.yml"
161
+ - ".github/workflows/release.yml"
162
162
  - ".gitignore"
163
163
  - ".rubocop.yml"
164
+ - ".ruby-version"
164
165
  - CHANGELOG.md
165
166
  - Gemfile
167
+ - Gemfile.lock
166
168
  - LICENSE.txt
167
169
  - README.md
168
170
  - Rakefile
@@ -183,7 +185,6 @@ licenses:
183
185
  - MIT
184
186
  metadata:
185
187
  allowed_push_host: https://rubygems.org
186
- post_install_message:
187
188
  rdoc_options: []
188
189
  require_paths:
189
190
  - lib
@@ -191,15 +192,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
191
192
  requirements:
192
193
  - - ">="
193
194
  - !ruby/object:Gem::Version
194
- version: 2.6.0
195
+ version: 3.0.0
195
196
  required_rubygems_version: !ruby/object:Gem::Requirement
196
197
  requirements:
197
198
  - - ">="
198
199
  - !ruby/object:Gem::Version
199
200
  version: '0'
200
201
  requirements: []
201
- rubygems_version: 3.4.10
202
- signing_key:
202
+ rubygems_version: 3.6.7
203
203
  specification_version: 4
204
204
  summary: Simple Ruby rate limiting mechanism.
205
205
  test_files: []
@@ -1,31 +0,0 @@
1
- name: Ruby Gem
2
-
3
- on:
4
- push:
5
- tags: [ "v*" ]
6
-
7
- jobs:
8
- build:
9
- name: Build + Publish
10
- runs-on: ubuntu-latest
11
- permissions:
12
- contents: read
13
- packages: write
14
-
15
- steps:
16
- - uses: actions/checkout@v3
17
- - name: Set up Ruby
18
- uses: ruby/setup-ruby@v1
19
- with:
20
- ruby-version: 3.2
21
-
22
- - name: Publish to RubyGems
23
- run: |
24
- mkdir -p $HOME/.gem
25
- touch $HOME/.gem/credentials
26
- chmod 0600 $HOME/.gem/credentials
27
- printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials
28
- gem build *.gemspec
29
- gem push *.gem
30
- env:
31
- RUBYGEMS_API_KEY: "${{secrets.RUBYGEMS_API_KEY}}"