async-tools 0.2.2 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +13 -1
- data/lib/async/app/component.rb +2 -12
- data/lib/async/app/event_logger.rb +11 -0
- data/lib/async/app.rb +11 -4
- data/lib/async/bus.rb +6 -16
- data/lib/async/cache.rb +35 -0
- data/lib/async/throttler.rb +49 -0
- data/lib/async/tools/version.rb +1 -1
- data/lib/async/tools.rb +5 -3
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47081e302bddc4861bc4526016dd26f4f601014d24b6696427b100446704b6b3
|
4
|
+
data.tar.gz: 6b71e93bbdbb81df60b5e20b0323a514759c1ab4e974c28a339cf8979fcc3d82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0636dd84879123e42e22599289ceb3f474f14a7ebdf32abe379ad02d7ac7b4e6f1dafbe54a96686d37bbfa0428b0bc7950c975f3b20c37c89c80c6b8e46cad0
|
7
|
+
data.tar.gz: 8045ea3c5c51d63b7ccd7349697af0c7f34c7ce937c11d0d65a76b154b5ca04bf37aee70a21261901e770c805bb7b487def5915f2def9193f8488e8dc1136a28
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
async-tools (0.2.
|
4
|
+
async-tools (0.2.5)
|
5
5
|
async (~> 2.3)
|
6
6
|
zeitwerk (~> 2.6)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
+
activesupport (7.0.4.2)
|
12
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
|
+
i18n (>= 1.6, < 2)
|
14
|
+
minitest (>= 5.1)
|
15
|
+
tzinfo (~> 2.0)
|
11
16
|
ast (2.4.2)
|
12
17
|
async (2.3.1)
|
13
18
|
console (~> 1.10)
|
@@ -32,12 +37,15 @@ GEM
|
|
32
37
|
backport (1.2.0)
|
33
38
|
benchmark (0.2.1)
|
34
39
|
childprocess (4.1.0)
|
40
|
+
concurrent-ruby (1.2.2)
|
35
41
|
console (1.16.2)
|
36
42
|
fiber-local
|
37
43
|
diff-lcs (1.5.0)
|
38
44
|
docile (1.4.0)
|
39
45
|
e2mmap (0.1.0)
|
40
46
|
fiber-local (1.0.0)
|
47
|
+
i18n (1.12.0)
|
48
|
+
concurrent-ruby (~> 1.0)
|
41
49
|
iniparse (1.5.0)
|
42
50
|
io-event (1.1.6)
|
43
51
|
jaro_winkler (1.5.4)
|
@@ -46,6 +54,7 @@ GEM
|
|
46
54
|
rexml
|
47
55
|
kramdown-parser-gfm (1.1.0)
|
48
56
|
kramdown (~> 2.0)
|
57
|
+
minitest (5.18.0)
|
49
58
|
nokogiri (1.14.1-x86_64-linux)
|
50
59
|
racc (~> 1.4)
|
51
60
|
overcommit (0.60.0)
|
@@ -134,6 +143,8 @@ GEM
|
|
134
143
|
tilt (2.0.11)
|
135
144
|
timers (4.3.5)
|
136
145
|
traces (0.8.0)
|
146
|
+
tzinfo (2.0.6)
|
147
|
+
concurrent-ruby (~> 1.0)
|
137
148
|
unicode-display_width (2.4.2)
|
138
149
|
webrick (1.7.0)
|
139
150
|
yard (0.9.28)
|
@@ -144,6 +155,7 @@ PLATFORMS
|
|
144
155
|
x86_64-linux
|
145
156
|
|
146
157
|
DEPENDENCIES
|
158
|
+
activesupport
|
147
159
|
async-http
|
148
160
|
async-rspec
|
149
161
|
async-tools!
|
data/lib/async/app/component.rb
CHANGED
@@ -4,18 +4,8 @@ module Async::App::Component
|
|
4
4
|
def self.included(base)
|
5
5
|
base.extend(Async::App::Injector)
|
6
6
|
base.inject(:bus)
|
7
|
-
|
8
7
|
base.include(Async::Logger)
|
9
|
-
|
10
|
-
strict = Dry.Types::Strict
|
11
|
-
|
12
|
-
string_like = (strict::String | strict::Symbol).constructor(&:to_s)
|
13
|
-
kv = strict::Hash.map(string_like, strict::String)
|
14
|
-
|
15
|
-
base.const_set(:T, Module.new do
|
16
|
-
include Dry.Types
|
17
|
-
const_set(:StringLike, string_like)
|
18
|
-
const_set(:KV, kv)
|
19
|
-
end)
|
20
8
|
end
|
9
|
+
|
10
|
+
def run = nil
|
21
11
|
end
|
data/lib/async/app.rb
CHANGED
@@ -15,11 +15,9 @@ class Async::App
|
|
15
15
|
@task = Async::Task.current
|
16
16
|
|
17
17
|
set_traps!
|
18
|
-
|
19
|
-
bus: Async::Bus.new(app_name),
|
20
|
-
**container_config
|
21
|
-
}.each { container.register(_1, _2) }
|
18
|
+
init_container!
|
22
19
|
|
20
|
+
start_event_logger!
|
23
21
|
start_metrics_server!
|
24
22
|
run!
|
25
23
|
info { "Started" }
|
@@ -53,6 +51,13 @@ class Async::App
|
|
53
51
|
trap("TERM") { stop }
|
54
52
|
end
|
55
53
|
|
54
|
+
def init_container!
|
55
|
+
{
|
56
|
+
bus: Async::Bus.new,
|
57
|
+
**container_config
|
58
|
+
}.each { container.register(_1, _2) }
|
59
|
+
end
|
60
|
+
|
56
61
|
def force_exit!
|
57
62
|
fatal { "Forced exit" }
|
58
63
|
exit(1)
|
@@ -63,4 +68,6 @@ class Async::App
|
|
63
68
|
bus.subscribe("metrics.updated") { server.update_metrics(_1) }
|
64
69
|
end
|
65
70
|
end
|
71
|
+
|
72
|
+
def start_event_logger! = EventLogger.new.run
|
66
73
|
end
|
data/lib/async/bus.rb
CHANGED
@@ -2,30 +2,20 @@
|
|
2
2
|
|
3
3
|
class Async::Bus
|
4
4
|
include Async::Logger
|
5
|
-
#
|
6
|
-
# add it to your bundle yourself
|
7
|
-
|
8
|
-
# Semantics:
|
9
|
-
# - Lazily registeres events
|
10
|
-
# - Synchronous by default
|
11
|
-
# - Catches exceptions in subscribers, logs them
|
12
|
-
def initialize(name)
|
13
|
-
@name = name
|
14
|
-
@w = Class.new.include(Dry::Events::Publisher[name]).new
|
15
|
-
end
|
5
|
+
# A tiny wrapper around ac ActiveSupport::Notifications
|
16
6
|
|
17
7
|
# BLOCKING unless subscribers run in tasks
|
18
8
|
def publish(name, *args, **params)
|
19
|
-
|
20
|
-
@w.publish(name, payload: (args.first || params))
|
9
|
+
ActiveSupport::Notifications.instrument(name, payload: (args.first || params))
|
21
10
|
rescue StandardError => e
|
22
11
|
log_error(name, e)
|
23
12
|
end
|
24
13
|
|
25
14
|
# NON-BLOCKING
|
26
|
-
def subscribe(
|
27
|
-
|
28
|
-
|
15
|
+
def subscribe(pattern)
|
16
|
+
ActiveSupport::Notifications.subscribe(pattern) do |name, _start, _finish, _id, params|
|
17
|
+
yield params[:payload], name
|
18
|
+
end
|
29
19
|
end
|
30
20
|
|
31
21
|
# NON-BLOCKING, runs subscriber in a task
|
data/lib/async/cache.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::Cache
|
4
|
+
Item = Struct.new("Item", :task, :value, :created_at, :duration) do
|
5
|
+
def expired? = created_at && Time.now - created_at >= duration
|
6
|
+
end
|
7
|
+
|
8
|
+
def cache(id, duration:, parent: Async::Task.current)
|
9
|
+
cleanup!
|
10
|
+
find_or_create(id, duration:) do |item|
|
11
|
+
parent.async do |task|
|
12
|
+
item.task = task
|
13
|
+
item.value = yield(id) if block_given?
|
14
|
+
item.created_at = Time.now
|
15
|
+
end.wait
|
16
|
+
end.value
|
17
|
+
end
|
18
|
+
|
19
|
+
def cleanup! = storage.delete_if { _2.expired? }
|
20
|
+
def count = storage.count
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def find_or_create(id, duration:)
|
25
|
+
storage[id].tap do |item|
|
26
|
+
item.duration = duration
|
27
|
+
item.task&.wait
|
28
|
+
return item if item.created_at
|
29
|
+
|
30
|
+
yield(item)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def storage = @storage ||= Hash.new { _1[_2] = Item.new }
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# inspired by https://github.com/negativecode/vines/blob/master/lib/vines/token_bucket.rb
|
4
|
+
class Async::Throttler
|
5
|
+
def initialize(capacity, rate, parent: Async::Task.current)
|
6
|
+
raise ArgumentError, "capacity must be > 0" unless capacity.positive?
|
7
|
+
raise ArgumentError, "rate must be > 0" unless rate.positive?
|
8
|
+
|
9
|
+
@capacity = capacity
|
10
|
+
@tokens = capacity
|
11
|
+
@rate = rate
|
12
|
+
@parent = parent
|
13
|
+
|
14
|
+
@timestamp = Time.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def wait(timeout: 0)
|
18
|
+
with_timeout(timeout) do
|
19
|
+
while @tokens < 1
|
20
|
+
fill!
|
21
|
+
sleep(1.0 / @rate)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@tokens -= 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def async(parent: @parent, timeout: 0, &)
|
29
|
+
wait(timeout:)
|
30
|
+
parent.async(&)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def with_timeout(timeout, &)
|
36
|
+
return yield if timeout.zero?
|
37
|
+
|
38
|
+
Fiber.scheduler.with_timeout(timeout, &)
|
39
|
+
end
|
40
|
+
|
41
|
+
def fill!
|
42
|
+
return if @tokens >= @capacity
|
43
|
+
|
44
|
+
now = Time.new
|
45
|
+
@tokens += @rate * (now - @timestamp)
|
46
|
+
@tokens = @capacity if @tokens > @capacity
|
47
|
+
@timestamp = now
|
48
|
+
end
|
49
|
+
end
|
data/lib/async/tools/version.rb
CHANGED
data/lib/async/tools.rb
CHANGED
@@ -22,9 +22,11 @@ module Async
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.map(collection,
|
26
|
-
|
27
|
-
|
25
|
+
def self.map(collection, concurrency: nil, parent: Async::Task.current, &)
|
26
|
+
Async::Semaphore.new(concurrency || collection.count, parent:).then do |s|
|
27
|
+
collection.map do |item|
|
28
|
+
s.async { yield(item) }
|
29
|
+
end.map(&:wait)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gleb Sinyavskiy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -62,16 +62,19 @@ files:
|
|
62
62
|
- bin/setup
|
63
63
|
- lib/async/app.rb
|
64
64
|
- lib/async/app/component.rb
|
65
|
+
- lib/async/app/event_logger.rb
|
65
66
|
- lib/async/app/injector.rb
|
66
67
|
- lib/async/app/metrics/ruby_runtime_monitor.rb
|
67
68
|
- lib/async/app/metrics/serializer.rb
|
68
69
|
- lib/async/app/metrics/server.rb
|
69
70
|
- lib/async/app/metrics/store.rb
|
70
71
|
- lib/async/bus.rb
|
72
|
+
- lib/async/cache.rb
|
71
73
|
- lib/async/channel.rb
|
72
74
|
- lib/async/logger.rb
|
73
75
|
- lib/async/q.rb
|
74
76
|
- lib/async/result_notification.rb
|
77
|
+
- lib/async/throttler.rb
|
75
78
|
- lib/async/timer.rb
|
76
79
|
- lib/async/tools.rb
|
77
80
|
- lib/async/tools/version.rb
|