omq-ractor 0.1.1 → 0.1.2
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
- data/README.md +26 -11
- data/lib/omq/ractor.rb +29 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15931abc881035b0cac7d80667168cd3dbdd120226178b76725d2fa40260ac5f
|
|
4
|
+
data.tar.gz: 82c4e9531bb7afc5e471d435fadb068b0e7cbf92b9a8db70a063c7623a9cb5bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41ebd53843bb6e69859a6546bc1d6f7926f8e03894c10de1bffdbd2a6d294e282bc1b8c04d735920178ea4b25bdd509d5822fee1979121dc6b307224150785fb
|
|
7
|
+
data.tar.gz: 7f490382cb90a176d28a1d67e75f4edd5f53b2e10740366d81d49c0f5e73b21e63284ae084b1eb7616a9f54d609d64cc42b2e3ff39738f57e035293f6e8d6963
|
data/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# OMQ::Ractor -- Networked Ractors
|
|
2
2
|
|
|
3
|
+
[](https://github.com/paddor/omq-ractor/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/omq-ractor)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.ruby-lang.org)
|
|
7
|
+
|
|
3
8
|
Ruby Ractors give you true parallelism -- each Ractor gets its own GVL,
|
|
4
9
|
so CPU-bound work runs on separate cores. But they can only talk to each
|
|
5
10
|
other inside a single process, using `Ractor::Port`. No networking, no
|
|
@@ -45,8 +50,7 @@ Async do
|
|
|
45
50
|
worker = OMQ::Ractor.new(pull, push) do |omq|
|
|
46
51
|
pull_p, push_p = omq.sockets # handshake (must be first call)
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
msg = pull_p.receive
|
|
53
|
+
while msg = pull_p.receive # nil on close
|
|
50
54
|
push_p << expensive_transform(msg)
|
|
51
55
|
end
|
|
52
56
|
end
|
|
@@ -61,25 +65,36 @@ lightweight wrappers around `Ractor::Port` pairs.
|
|
|
61
65
|
|
|
62
66
|
### Multiplexing with Ractor.select
|
|
63
67
|
|
|
68
|
+
`Ractor.select` waits on multiple `Ractor::Port` objects and returns
|
|
69
|
+
`[port, value]`. Use `#to_port` to get the underlying port, and
|
|
70
|
+
`#socket_for` to map back to the proxy:
|
|
71
|
+
|
|
64
72
|
```ruby
|
|
65
73
|
worker = OMQ::Ractor.new(pull_a, pull_b, push) do |omq|
|
|
66
|
-
|
|
74
|
+
sockets = omq.sockets
|
|
75
|
+
a, b, out = sockets
|
|
67
76
|
|
|
68
77
|
loop do
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
port, msg = Ractor.select(a.to_port, b.to_port)
|
|
79
|
+
break if msg.nil? # socket closed
|
|
80
|
+
source = sockets.socket_for(port) # => a or b
|
|
81
|
+
out << process(source, msg)
|
|
71
82
|
end
|
|
72
83
|
end
|
|
73
84
|
```
|
|
74
85
|
|
|
86
|
+
Note: `Ractor.select` returns raw port values, bypassing `SocketProxy#receive`.
|
|
87
|
+
For topic-based sockets, `msg` will be the full `[topic, payload]` array --
|
|
88
|
+
use `#receive` or `#receive_with_topic` on a single proxy instead if you
|
|
89
|
+
need topic stripping.
|
|
90
|
+
|
|
75
91
|
### Bidirectional (PAIR, REQ/REP, DEALER)
|
|
76
92
|
|
|
77
93
|
```ruby
|
|
78
94
|
worker = OMQ::Ractor.new(pair) do |omq|
|
|
79
95
|
p = omq.sockets.first
|
|
80
96
|
|
|
81
|
-
|
|
82
|
-
msg = p.receive
|
|
97
|
+
while msg = p.receive
|
|
83
98
|
p << transform(msg)
|
|
84
99
|
end
|
|
85
100
|
end
|
|
@@ -122,8 +137,7 @@ Async do
|
|
|
122
137
|
|
|
123
138
|
OMQ::Ractor.new(pull, push) do |omq|
|
|
124
139
|
p_in, p_out = omq.sockets
|
|
125
|
-
|
|
126
|
-
msg = p_in.receive
|
|
140
|
+
while msg = p_in.receive
|
|
127
141
|
p_out << expensive_transform(msg)
|
|
128
142
|
end
|
|
129
143
|
end
|
|
@@ -160,8 +174,9 @@ Use `serialize: false` for raw messages (frozen string arrays):
|
|
|
160
174
|
```ruby
|
|
161
175
|
worker = OMQ::Ractor.new(pull, push, serialize: false) do |omq|
|
|
162
176
|
p_in, p_out = omq.sockets
|
|
163
|
-
msg = p_in.receive # frozen string array, e.g. ["hello"]
|
|
164
|
-
|
|
177
|
+
while msg = p_in.receive # frozen string array, e.g. ["hello"]
|
|
178
|
+
p_out << [msg.first.upcase] # must send frozen string arrays
|
|
179
|
+
end
|
|
165
180
|
end
|
|
166
181
|
```
|
|
167
182
|
|
data/lib/omq/ractor.rb
CHANGED
|
@@ -230,6 +230,33 @@ module OMQ
|
|
|
230
230
|
end
|
|
231
231
|
|
|
232
232
|
|
|
233
|
+
# Array of SocketProxy objects with a port→proxy lookup for
|
|
234
|
+
# Ractor.select results.
|
|
235
|
+
#
|
|
236
|
+
class SocketSet < Array
|
|
237
|
+
def initialize(proxies)
|
|
238
|
+
super(proxies)
|
|
239
|
+
@by_port = {}
|
|
240
|
+
proxies.each do |proxy|
|
|
241
|
+
@by_port[proxy.to_port] = proxy if proxy.to_port rescue nil
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Returns the SocketProxy whose input port matches +port+.
|
|
246
|
+
# Use after Ractor.select to map back to the proxy:
|
|
247
|
+
#
|
|
248
|
+
# port, msg = Ractor.select(a.to_port, b.to_port)
|
|
249
|
+
# source = sockets.socket_for(port)
|
|
250
|
+
#
|
|
251
|
+
# @param port [Ractor::Port]
|
|
252
|
+
# @return [SocketProxy, nil]
|
|
253
|
+
#
|
|
254
|
+
def socket_for(port)
|
|
255
|
+
@by_port[port]
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
|
|
233
260
|
# -- Context -------------------------------------------------------
|
|
234
261
|
|
|
235
262
|
# Frozen, shareable context passed to the worker Ractor.
|
|
@@ -252,9 +279,10 @@ module OMQ
|
|
|
252
279
|
|
|
253
280
|
@setup_port.send(input_ports)
|
|
254
281
|
|
|
255
|
-
@socket_configs.each_with_index.map do |cfg, i|
|
|
282
|
+
proxies = @socket_configs.each_with_index.map do |cfg, i|
|
|
256
283
|
SocketProxy.new(input_ports[i], @output_ports[i], cfg[:topic_type])
|
|
257
284
|
end
|
|
285
|
+
SocketSet.new(proxies)
|
|
258
286
|
end
|
|
259
287
|
end
|
|
260
288
|
|