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.
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: []