ractor_mgr 1.2.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 +7 -0
- data/lib/ractor_mgr.rb +150 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b0ce6bb9fff5fc074a6fef8418cf1ebd0c68ecc5b93b0fb3879303d36136e80f
|
4
|
+
data.tar.gz: b5fc847cee9599d221ddd0f20f88bc6fff55655b05dd19659f4d212c653f1089
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: accd93b787253f2a77a9c385b51c36ee62e1424c12995e01a38d6228059a1667f5b799e65444bc854f87e5cd7e50fa31752a77485ade87a224312c071a777304
|
7
|
+
data.tar.gz: 2f807db78d0f1fa0a3095aea97870bf6cb66e814fb99de6b1ca9b7fcc081df0c9caa7406dc7743eabb26326537a34f0849dff4856b352626715068fd07829d5c
|
data/lib/ractor_mgr.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'tiny_eta'
|
2
|
+
|
3
|
+
# RactorMgr is a job queue combined with a monitor for a list of Ractors. the
|
4
|
+
# job of a RactorMgr is:
|
5
|
+
#
|
6
|
+
# 1. store the list of jobs to be completed
|
7
|
+
# 2. feed the worker Ractors with jobs
|
8
|
+
# 3. when a worker completes a job give it another job (if there's more work to
|
9
|
+
# do)
|
10
|
+
# 4. generally monitor the workers to see how many jobs have been completed, as
|
11
|
+
# well as providing status of current Ractors (:idle, :working)
|
12
|
+
#
|
13
|
+
# you create your own worker Ractors, and the RactorMgr takes care of making
|
14
|
+
# sure those workers stay as busy as possible.
|
15
|
+
#
|
16
|
+
# Usage:
|
17
|
+
#
|
18
|
+
# # make some workers
|
19
|
+
# workers = 5.times.map do
|
20
|
+
# Ractor.new do
|
21
|
+
# loop do
|
22
|
+
# Ractor.yield(Ractor.receive * 2)
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # create some jobs
|
28
|
+
# jobs = [1] * 1_000_000; nil
|
29
|
+
#
|
30
|
+
# # feed the jobs to the workers via the RactorMgr
|
31
|
+
# rm = RactorMgr.new(
|
32
|
+
# jobs: jobs,
|
33
|
+
# workers: workers
|
34
|
+
# )
|
35
|
+
# rm.join # optional, if you want to wait
|
36
|
+
#
|
37
|
+
# NOTE on Ractor interface: the RactorMgr expects to send a worker a job, and
|
38
|
+
# then be able to call #take on that Ractor. it is assumed that as soon as the
|
39
|
+
# worker returns a value from #take that that worker is available for another
|
40
|
+
# job. this means you should implement your Ractors as an infinite loop where:
|
41
|
+
# - the Ractor takes in a job definition
|
42
|
+
# - the Ractor yields some value when the job is done
|
43
|
+
#
|
44
|
+
# ... thus the worker Ractor will continue accepting jobs as long as the
|
45
|
+
# RactorMgr keeps feeding it jobs.
|
46
|
+
#
|
47
|
+
# WARN: while the RactorMgr is running the job list should not be altered.
|
48
|
+
#
|
49
|
+
# status:
|
50
|
+
# - :idle if the RactorMgr is finished
|
51
|
+
# - :running if the RactorMgr is running
|
52
|
+
# join: calls #join on the internal Thread, blocking until all jobs have
|
53
|
+
# finished
|
54
|
+
class RactorMgr
|
55
|
+
attr_reader :status,
|
56
|
+
:results
|
57
|
+
|
58
|
+
def initialize(jobs:, workers:)
|
59
|
+
@status = :idle
|
60
|
+
@jobs = jobs
|
61
|
+
@workers = workers
|
62
|
+
@job_index = 0
|
63
|
+
@jobs_finished = 0
|
64
|
+
@results = []
|
65
|
+
|
66
|
+
##
|
67
|
+
# start work
|
68
|
+
@start_time = Time.now
|
69
|
+
|
70
|
+
# initial job fill
|
71
|
+
@workers.each do |w|
|
72
|
+
w.send(@jobs[@job_index])
|
73
|
+
@job_index += 1
|
74
|
+
end
|
75
|
+
|
76
|
+
# Thread for feeding workers
|
77
|
+
@mgr_thread = Thread.new do
|
78
|
+
@status = :running
|
79
|
+
|
80
|
+
loop do
|
81
|
+
break if @job_index == @jobs.length
|
82
|
+
|
83
|
+
w, r = Ractor.select(*@workers)
|
84
|
+
@results << r
|
85
|
+
@jobs_finished += 1
|
86
|
+
w.send(@jobs[@job_index])
|
87
|
+
@job_index += 1
|
88
|
+
end
|
89
|
+
|
90
|
+
@workers.map do |w|
|
91
|
+
@results << w.take
|
92
|
+
@jobs_finished += 1
|
93
|
+
end
|
94
|
+
|
95
|
+
@status = :idle
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def done?
|
100
|
+
jobs_finished == @jobs.length
|
101
|
+
end
|
102
|
+
|
103
|
+
# the total number of jobs that will be run
|
104
|
+
def jobs_total
|
105
|
+
@jobs.length
|
106
|
+
end
|
107
|
+
|
108
|
+
# the number of jobs that have finished
|
109
|
+
def jobs_finished
|
110
|
+
@jobs_finished
|
111
|
+
end
|
112
|
+
|
113
|
+
# the number of jobs currently running
|
114
|
+
def jobs_running
|
115
|
+
@job_index - @jobs_finished
|
116
|
+
end
|
117
|
+
|
118
|
+
# the number of jobs remaining
|
119
|
+
def jobs_remaining
|
120
|
+
jobs_total - jobs_finished
|
121
|
+
end
|
122
|
+
|
123
|
+
# percentage complete as a Float in the range of 0.0..1.0
|
124
|
+
def percent_complete_f(digits=0)
|
125
|
+
(jobs_finished / jobs_total.to_f)
|
126
|
+
end
|
127
|
+
|
128
|
+
# percentage complete (a floating point number between 0.0..100.0)
|
129
|
+
def percent_complete_s(digits=0)
|
130
|
+
((jobs_finished / jobs_total.to_f) * 100).round(digits)
|
131
|
+
end
|
132
|
+
|
133
|
+
# returns a TinyEta string for ETA time
|
134
|
+
#
|
135
|
+
# see: https://github.com/jefflunt/tiny_eta
|
136
|
+
def eta
|
137
|
+
TinyEta.eta(Time.now - @start_time, percent_complete_f)
|
138
|
+
end
|
139
|
+
|
140
|
+
# just like Thread#join, blocks until all jobs are complete
|
141
|
+
def join
|
142
|
+
@mgr_thread.join
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
|
146
|
+
# out-of-the-box monitoring
|
147
|
+
def to_s
|
148
|
+
"RactorMgr #{jobs_running} running, progress: #{jobs_finished}/#{jobs_total} #{percent_complete_s(1)}% (ETA #{eta})"
|
149
|
+
end
|
150
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ractor_mgr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeff Lunt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tiny_eta
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.1
|
27
|
+
description: a job queue and monitor for Ractors
|
28
|
+
email: jefflunt@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/ractor_mgr.rb
|
34
|
+
homepage: https://github.com/jefflunt/ractor_mgr
|
35
|
+
licenses:
|
36
|
+
- MIT
|
37
|
+
metadata: {}
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubygems_version: 3.4.1
|
54
|
+
signing_key:
|
55
|
+
specification_version: 4
|
56
|
+
summary: want to have a job queue for a list of identical Ractors, and have a little
|
57
|
+
manager that feeds them work as they become availble? then this library is for you
|
58
|
+
test_files: []
|