ractor_mgr 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/ractor_mgr.rb +150 -0
  3. 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: []