async-limiter 2.0.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/generic-limiter.md +2 -2
- data/context/getting-started.md +3 -3
- data/context/limited-limiter.md +6 -6
- data/context/queued-limiter.md +4 -4
- data/context/timing-strategies.md +3 -3
- data/lib/async/limiter/generic.rb +14 -1
- data/lib/async/limiter/limited.rb +41 -2
- data/lib/async/limiter/queued.rb +37 -2
- data/lib/async/limiter/timing/fixed_window.rb +1 -2
- data/lib/async/limiter/version.rb +1 -1
- data/license.md +3 -3
- data/readme.md +41 -0
- data/releases.md +6 -0
- data.tar.gz.sig +0 -0
- metadata +4 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d9b6e7e0d162f4068ced173efa8f365f53e809bf36b80fb41c21988865ea558b
|
|
4
|
+
data.tar.gz: d6ed3ed9482da942bf2fdec9403d2c842174eaa090cf0ea61036cf839c731d9c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 496cd5748700aa4c7eb897fc2c47720ea10c7650ab2407e0d15394ce56d44c1d7bd82a31f4138b9437d1e0e474449defe2a5f190d2140f41bc44afafa68bfe8b
|
|
7
|
+
data.tar.gz: 8733a7138915188e423fee9f9b4213cc087d93b86f69a43e3ac7954272e94eb15ef99e2ea100ccfd621aebc6d4f292267c298d609f559448f92e45c7c8ffb0dd
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/generic-limiter.md
CHANGED
|
@@ -35,7 +35,7 @@ require "async/limiter"
|
|
|
35
35
|
|
|
36
36
|
Async do
|
|
37
37
|
limiter = Async::Limiter::Generic.new
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
# Create async tasks through the limiter:
|
|
40
40
|
tasks = 5.times.map do |i|
|
|
41
41
|
limiter.async do |task|
|
|
@@ -81,7 +81,7 @@ Async do
|
|
|
81
81
|
# Allow unlimited concurrency but rate limit to 10 operations per second:
|
|
82
82
|
timing = Async::Limiter::Timing::LeakyBucket.new(10.0, 50.0)
|
|
83
83
|
limiter = Async::Limiter::Generic.new(timing: timing)
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
# All tasks start immediately, but timing strategy controls rate:
|
|
86
86
|
100.times do |i|
|
|
87
87
|
limiter.async do |task|
|
data/context/getting-started.md
CHANGED
|
@@ -86,7 +86,7 @@ Async do
|
|
|
86
86
|
|
|
87
87
|
limiter.acquire(timeout: 3)
|
|
88
88
|
# => nil
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
limiter.acquire(timeout: 3) do
|
|
91
91
|
puts "Acquired."
|
|
92
92
|
end or puts "Timed out!"
|
|
@@ -199,8 +199,8 @@ fair_limiter.acquire(cost: 8.0) do
|
|
|
199
199
|
end
|
|
200
200
|
|
|
201
201
|
# These must wait even though they need fewer tokens
|
|
202
|
-
fair_limiter.acquire(cost: 0.5)
|
|
203
|
-
fair_limiter.acquire(cost: 0.5)
|
|
202
|
+
fair_limiter.acquire(cost: 0.5){puts "Quick op 1"} # Blocked
|
|
203
|
+
fair_limiter.acquire(cost: 0.5){puts "Quick op 2"} # Blocked
|
|
204
204
|
```
|
|
205
205
|
|
|
206
206
|
#### Choosing the Right Strategy
|
data/context/limited-limiter.md
CHANGED
|
@@ -89,16 +89,16 @@ The limiter prevents convoy effects where quick timeouts aren't blocked by slow
|
|
|
89
89
|
limiter = Async::Limiter::Limited.new(1)
|
|
90
90
|
Async do
|
|
91
91
|
limiter.acquire # Fill to capacity.
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
results = []
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
# Start multiple tasks with different timeouts:
|
|
96
96
|
tasks = [
|
|
97
|
-
Async
|
|
98
|
-
Async
|
|
99
|
-
Async
|
|
97
|
+
Async{limiter.acquire(timeout: 1.0); results << "Long timeout."},
|
|
98
|
+
Async{limiter.acquire(timeout: 0.1); results << "Short timeout."},
|
|
99
|
+
Async{limiter.acquire(timeout: 0); results << "Non-blocking."},
|
|
100
100
|
]
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
# All tasks complete quickly, even with a long timeout task present:
|
|
103
103
|
tasks.map(&:wait)
|
|
104
104
|
puts results
|
data/context/queued-limiter.md
CHANGED
|
@@ -42,7 +42,7 @@ For fine-grained control over resource lifecycle:
|
|
|
42
42
|
|
|
43
43
|
```ruby
|
|
44
44
|
queue = Async::Queue.new
|
|
45
|
-
3.times
|
|
45
|
+
3.times{|i| queue.push("resource_#{i}")}
|
|
46
46
|
|
|
47
47
|
limiter = Async::Limiter::Queued.new(queue)
|
|
48
48
|
|
|
@@ -71,7 +71,7 @@ Async do
|
|
|
71
71
|
queue = Async::PriorityQueue.new
|
|
72
72
|
limiter = Async::Limiter::Queued.new(queue)
|
|
73
73
|
results = []
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
# Start tasks with different priorities
|
|
76
76
|
tasks = [
|
|
77
77
|
Async do
|
|
@@ -100,9 +100,9 @@ Async do
|
|
|
100
100
|
2.times do |i|
|
|
101
101
|
limiter.release("worker_#{i}")
|
|
102
102
|
end
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
tasks.each(&:wait)
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
puts results
|
|
107
107
|
# High priority task gets resource first, then medium, then low.
|
|
108
108
|
end
|
|
@@ -385,7 +385,7 @@ require "async/queue"
|
|
|
385
385
|
|
|
386
386
|
# Create resource queue
|
|
387
387
|
queue = Async::Queue.new
|
|
388
|
-
3.times
|
|
388
|
+
3.times{|i| queue.push("worker_#{i}")}
|
|
389
389
|
|
|
390
390
|
# Add timing constraint
|
|
391
391
|
timing = Async::Limiter::Timing::FixedWindow.new(2.0,
|
|
@@ -437,8 +437,8 @@ class RateLimitedAPIClient
|
|
|
437
437
|
|
|
438
438
|
def make_request(endpoint, cost: 1.0)
|
|
439
439
|
@limiter.acquire(cost: cost) do
|
|
440
|
-
|
|
441
|
-
|
|
440
|
+
# Make actual HTTP request:
|
|
441
|
+
puts "Making request to #{endpoint} at #{Time.now}"
|
|
442
442
|
simulate_http_request(endpoint)
|
|
443
443
|
end
|
|
444
444
|
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2025, by Shopify Inc.
|
|
4
|
+
# Copyright, 2025-2026, by Shopify Inc.
|
|
5
5
|
# Copyright, 2025, by Samuel Williams.
|
|
6
6
|
|
|
7
7
|
require "async/task"
|
|
8
8
|
require "async/deadline"
|
|
9
|
+
require "json"
|
|
9
10
|
require_relative "timing/none"
|
|
10
11
|
require_relative "timing/sliding_window"
|
|
11
12
|
require_relative "token"
|
|
@@ -135,6 +136,18 @@ module Async
|
|
|
135
136
|
end
|
|
136
137
|
end
|
|
137
138
|
|
|
139
|
+
# Get a JSON-compatible representation of the limiter statistics.
|
|
140
|
+
# @returns [Hash] Statistics hash with current state.
|
|
141
|
+
def as_json(...)
|
|
142
|
+
statistics
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Get a JSON string representation of the limiter statistics.
|
|
146
|
+
# @returns [String] JSON encoded statistics.
|
|
147
|
+
def to_json(...)
|
|
148
|
+
as_json.to_json(...)
|
|
149
|
+
end
|
|
150
|
+
|
|
138
151
|
protected
|
|
139
152
|
|
|
140
153
|
def acquire_synchronized(timeout, cost, **options)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2020, by Bruno Sutic.
|
|
5
|
-
# Copyright, 2025, by Shopify Inc.
|
|
5
|
+
# Copyright, 2025-2026, by Shopify Inc.
|
|
6
6
|
# Copyright, 2025, by Samuel Williams.
|
|
7
7
|
|
|
8
8
|
require_relative "generic"
|
|
@@ -28,6 +28,8 @@ module Async
|
|
|
28
28
|
|
|
29
29
|
@limit = limit
|
|
30
30
|
@count = 0
|
|
31
|
+
@waiting_count = 0
|
|
32
|
+
@reacquire_waiting_count = 0
|
|
31
33
|
|
|
32
34
|
@available = ConditionVariable.new
|
|
33
35
|
end
|
|
@@ -38,6 +40,26 @@ module Async
|
|
|
38
40
|
# @attribute [Integer] Current count of active tasks.
|
|
39
41
|
attr_reader :count
|
|
40
42
|
|
|
43
|
+
# @returns [Integer] Current count of active tasks.
|
|
44
|
+
def acquired_count
|
|
45
|
+
@mutex.synchronize{@count}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @returns [Integer] Current count of available capacity.
|
|
49
|
+
def available_count
|
|
50
|
+
@mutex.synchronize{@limit - @count}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @returns [Integer] Current count of tasks waiting for capacity.
|
|
54
|
+
def waiting_count
|
|
55
|
+
@mutex.synchronize{@waiting_count}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @returns [Integer] Current count of reacquiring tasks waiting for capacity.
|
|
59
|
+
def reacquire_waiting_count
|
|
60
|
+
@mutex.synchronize{@reacquire_waiting_count}
|
|
61
|
+
end
|
|
62
|
+
|
|
41
63
|
# Check if a new task can be acquired.
|
|
42
64
|
# @returns [Boolean] True if under the limit.
|
|
43
65
|
def limited?
|
|
@@ -64,6 +86,10 @@ module Async
|
|
|
64
86
|
{
|
|
65
87
|
limit: @limit,
|
|
66
88
|
count: @count,
|
|
89
|
+
acquired_count: @count,
|
|
90
|
+
available_count: @limit - @count,
|
|
91
|
+
waiting_count: @waiting_count,
|
|
92
|
+
reacquire_waiting_count: @reacquire_waiting_count,
|
|
67
93
|
timing: @timing.statistics
|
|
68
94
|
}
|
|
69
95
|
end
|
|
@@ -72,15 +98,23 @@ module Async
|
|
|
72
98
|
protected
|
|
73
99
|
|
|
74
100
|
# Acquire resource with optional deadline.
|
|
75
|
-
def acquire_resource(deadline, **options)
|
|
101
|
+
def acquire_resource(deadline, reacquire: false, **options)
|
|
76
102
|
# Fast path: immediate return for expired deadlines, but only if at capacity
|
|
77
103
|
return nil if deadline&.expired? && @count >= @limit
|
|
78
104
|
|
|
105
|
+
waiting = false
|
|
106
|
+
|
|
79
107
|
# Wait for capacity with deadline tracking
|
|
80
108
|
while @count >= @limit
|
|
81
109
|
remaining = deadline&.remaining
|
|
82
110
|
return nil if remaining && remaining <= 0
|
|
83
111
|
|
|
112
|
+
unless waiting
|
|
113
|
+
@waiting_count += 1
|
|
114
|
+
@reacquire_waiting_count += 1 if reacquire
|
|
115
|
+
waiting = true
|
|
116
|
+
end
|
|
117
|
+
|
|
84
118
|
unless @available.wait(@mutex, remaining)
|
|
85
119
|
return nil # Timeout exceeded
|
|
86
120
|
end
|
|
@@ -89,6 +123,11 @@ module Async
|
|
|
89
123
|
@count += 1
|
|
90
124
|
|
|
91
125
|
return true
|
|
126
|
+
ensure
|
|
127
|
+
if waiting
|
|
128
|
+
@waiting_count -= 1
|
|
129
|
+
@reacquire_waiting_count -= 1 if reacquire
|
|
130
|
+
end
|
|
92
131
|
end
|
|
93
132
|
|
|
94
133
|
# Release resource.
|
data/lib/async/limiter/queued.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2025, by Francisco Mejia.
|
|
5
|
-
# Copyright, 2025, by Shopify Inc.
|
|
5
|
+
# Copyright, 2025-2026, by Shopify Inc.
|
|
6
6
|
# Copyright, 2025, by Samuel Williams.
|
|
7
7
|
|
|
8
8
|
require_relative "generic"
|
|
@@ -33,11 +33,33 @@ module Async
|
|
|
33
33
|
def initialize(queue = self.class.default_queue, timing: Timing::None, parent: nil)
|
|
34
34
|
super(timing: timing, parent: parent)
|
|
35
35
|
@queue = queue
|
|
36
|
+
@acquired_count = 0
|
|
37
|
+
@reacquire_waiting_count = 0
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
# @attribute [Queue] The queue managing resources.
|
|
39
41
|
attr_reader :queue
|
|
40
42
|
|
|
43
|
+
# @returns [Integer] Current count of acquired resources.
|
|
44
|
+
def acquired_count
|
|
45
|
+
@mutex.synchronize{@acquired_count}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @returns [Integer] Current count of available resources.
|
|
49
|
+
def available_count
|
|
50
|
+
@queue.size
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @returns [Integer] Current count of tasks waiting for resources.
|
|
54
|
+
def waiting_count
|
|
55
|
+
@queue.waiting_count
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @returns [Integer] Current count of reacquiring tasks waiting for resources.
|
|
59
|
+
def reacquire_waiting_count
|
|
60
|
+
@mutex.synchronize{@reacquire_waiting_count}
|
|
61
|
+
end
|
|
62
|
+
|
|
41
63
|
# Check if a new task can be acquired.
|
|
42
64
|
# @returns [Boolean] True if resources are available.
|
|
43
65
|
def limited?
|
|
@@ -60,6 +82,10 @@ module Async
|
|
|
60
82
|
{
|
|
61
83
|
waiting: @queue.waiting_count,
|
|
62
84
|
available: @queue.size,
|
|
85
|
+
acquired_count: @acquired_count,
|
|
86
|
+
available_count: @queue.size,
|
|
87
|
+
waiting_count: @queue.waiting_count,
|
|
88
|
+
reacquire_waiting_count: @reacquire_waiting_count,
|
|
63
89
|
timing: @timing.statistics
|
|
64
90
|
}
|
|
65
91
|
end
|
|
@@ -69,14 +95,23 @@ module Async
|
|
|
69
95
|
|
|
70
96
|
# Acquire a resource from the queue with optional deadline.
|
|
71
97
|
def acquire_resource(deadline, reacquire: false, **options)
|
|
98
|
+
@reacquire_waiting_count += 1 if reacquire
|
|
99
|
+
|
|
72
100
|
@mutex.unlock
|
|
73
|
-
|
|
101
|
+
resource = @queue.pop(timeout: deadline&.remaining, **options)
|
|
102
|
+
return resource
|
|
74
103
|
ensure
|
|
75
104
|
@mutex.lock
|
|
105
|
+
@reacquire_waiting_count -= 1 if reacquire
|
|
106
|
+
@acquired_count += 1 if resource
|
|
76
107
|
end
|
|
77
108
|
|
|
78
109
|
# Release a previously acquired resource back to the queue.
|
|
79
110
|
def release_resource(value)
|
|
111
|
+
@mutex.synchronize do
|
|
112
|
+
@acquired_count -= 1 if @acquired_count > 0
|
|
113
|
+
end
|
|
114
|
+
|
|
80
115
|
# Return a default resource to the queue:
|
|
81
116
|
@queue.push(value)
|
|
82
117
|
end
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# Copyright, 2020, by Bruno Sutic.
|
|
5
5
|
# Copyright, 2025, by Shopify Inc.
|
|
6
6
|
# Copyright, 2025, by Samuel Williams.
|
|
7
|
+
# Copyright, 2026, by William T. Nelson.
|
|
7
8
|
|
|
8
9
|
require_relative "sliding_window"
|
|
9
10
|
|
|
@@ -25,8 +26,6 @@ module Async
|
|
|
25
26
|
# Get current timing strategy statistics.
|
|
26
27
|
# @returns [Hash] Statistics hash with current state.
|
|
27
28
|
def statistics
|
|
28
|
-
current_time = Time.now
|
|
29
|
-
|
|
30
29
|
{
|
|
31
30
|
name: "FixedWindow",
|
|
32
31
|
window_duration: @duration,
|
data/license.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Copyright, 2020-2021, by Bruno Sutic.
|
|
4
4
|
Copyright, 2025, by Francisco Mejia.
|
|
5
|
-
Copyright, 2025, by Shopify Inc.
|
|
6
|
-
Copyright, 2025, by Samuel Williams.
|
|
7
|
-
Copyright, 2025, by William T. Nelson.
|
|
5
|
+
Copyright, 2025-2026, by Shopify Inc.
|
|
6
|
+
Copyright, 2025-2026, by Samuel Williams.
|
|
7
|
+
Copyright, 2025-2026, by William T. Nelson.
|
|
8
8
|
|
|
9
9
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
10
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
|
@@ -20,6 +20,31 @@ Please see the [project documentation](https://socketry.github.io/async-limiter/
|
|
|
20
20
|
|
|
21
21
|
- [Token Usage](https://socketry.github.io/async-limiter/guides/token-usage/index) - This guide explains how to use tokens for advanced resource management with `async-limiter`. Tokens provide sophisticated resource handling with support for re-acquisition and automatic cleanup.
|
|
22
22
|
|
|
23
|
+
## Releases
|
|
24
|
+
|
|
25
|
+
Please see the [project releases](https://socketry.github.io/async-limiter/releases/index) for all releases.
|
|
26
|
+
|
|
27
|
+
### v2.1.0
|
|
28
|
+
|
|
29
|
+
- Add telemetry counters to `Async::Limiter::Limited` and `Async::Limiter::Queued`: `acquired_count`, `available_count`, `waiting_count`, and `reacquire_waiting_count` for observability into limiter state.
|
|
30
|
+
- Add `as_json` and `to_json` methods to `Async::Limiter::Generic` for JSON serialization of limiter statistics.
|
|
31
|
+
- Fix unused variable warning in `Async::Limiter::Timing::FixedWindow`.
|
|
32
|
+
|
|
33
|
+
### v2.0.0
|
|
34
|
+
|
|
35
|
+
The 2.0.x release should be considered somewhat unstable.
|
|
36
|
+
|
|
37
|
+
- **Breaking**: Complete API redesign. The v1.x classes (`Async::Limiter::Concurrent`, `Async::Limiter::Unlimited`, etc.) have been replaced with a new inheritance-based architecture.
|
|
38
|
+
- **Breaking**: Removed `blocking?` method due to inherent race conditions. Use `acquire(timeout: 0)` for non-blocking checks.
|
|
39
|
+
- **Breaking**: Timing strategies now use consumption-only model (no explicit `release` methods).
|
|
40
|
+
- **Breaking**: Window classes moved from `limiter/window/` to `limiter/timing/` with renamed classes.
|
|
41
|
+
- [New Architecture (replaces v1.x classes)](https://socketry.github.io/async-limiter/releases/index#new-architecture-\(replaces-v1.x-classes\))
|
|
42
|
+
- [Advanced Timeout Features](https://socketry.github.io/async-limiter/releases/index#advanced-timeout-features)
|
|
43
|
+
- [Cost-Based Acquisition](https://socketry.github.io/async-limiter/releases/index#cost-based-acquisition)
|
|
44
|
+
- [Enhanced Timing Strategies](https://socketry.github.io/async-limiter/releases/index#enhanced-timing-strategies)
|
|
45
|
+
- [Token-Based Resource Management](https://socketry.github.io/async-limiter/releases/index#token-based-resource-management)
|
|
46
|
+
- [Thread Safety and Performance](https://socketry.github.io/async-limiter/releases/index#thread-safety-and-performance)
|
|
47
|
+
|
|
23
48
|
## See Also
|
|
24
49
|
|
|
25
50
|
- [falcon](https://github.com/socketry/falcon) - A high-performance web server
|
|
@@ -36,6 +61,22 @@ We welcome contributions to this project.
|
|
|
36
61
|
4. Push to the branch (`git push origin my-new-feature`).
|
|
37
62
|
5. Create new Pull Request.
|
|
38
63
|
|
|
64
|
+
### Running Tests
|
|
65
|
+
|
|
66
|
+
To run the test suite:
|
|
67
|
+
|
|
68
|
+
``` shell
|
|
69
|
+
bundle exec sus
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Making Releases
|
|
73
|
+
|
|
74
|
+
To make a new release:
|
|
75
|
+
|
|
76
|
+
``` shell
|
|
77
|
+
bundle exec bake gem:release:patch # or minor or major
|
|
78
|
+
```
|
|
79
|
+
|
|
39
80
|
### Developer Certificate of Origin
|
|
40
81
|
|
|
41
82
|
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v2.1.0
|
|
4
|
+
|
|
5
|
+
- Add telemetry counters to `Async::Limiter::Limited` and `Async::Limiter::Queued`: `acquired_count`, `available_count`, `waiting_count`, and `reacquire_waiting_count` for observability into limiter state.
|
|
6
|
+
- Add `as_json` and `to_json` methods to `Async::Limiter::Generic` for JSON serialization of limiter statistics.
|
|
7
|
+
- Fix unused variable warning in `Async::Limiter::Timing::FixedWindow`.
|
|
8
|
+
|
|
3
9
|
## v2.0.0
|
|
4
10
|
|
|
5
11
|
The 2.0.x release should be considered somewhat unstable.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-limiter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bruno Sutic
|
|
8
8
|
- Shopify Inc.
|
|
9
9
|
- Samuel Williams
|
|
10
|
+
- William T. Nelson
|
|
10
11
|
- Francisco Mejia
|
|
11
|
-
- William
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain:
|
|
14
14
|
- |
|
|
@@ -99,14 +99,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
99
99
|
requirements:
|
|
100
100
|
- - ">="
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '3.
|
|
102
|
+
version: '3.3'
|
|
103
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
104
|
requirements:
|
|
105
105
|
- - ">="
|
|
106
106
|
- !ruby/object:Gem::Version
|
|
107
107
|
version: '0'
|
|
108
108
|
requirements: []
|
|
109
|
-
rubygems_version:
|
|
109
|
+
rubygems_version: 4.0.6
|
|
110
110
|
specification_version: 4
|
|
111
111
|
summary: Execution rate limiting for Async
|
|
112
112
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|