omq-backend-rust 0.2.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +9 -5
- data/lib/omq/backend/rust.rb +14 -7
- data/lib/omq/rust/engine.rb +76 -106
- data/lib/omq/rust/java/engine.rb +642 -0
- data/lib/omq/rust/java/platform.rb +95 -0
- data/lib/omq/rust/version.rb +13 -1
- metadata +10 -21
- data/Cargo.toml +0 -3
- data/ext/omq_backend_rust/Cargo.toml +0 -33
- data/ext/omq_backend_rust/build.rs +0 -23
- data/ext/omq_backend_rust/extconf.rb +0 -8
- data/ext/omq_backend_rust/src/error.rs +0 -16
- data/ext/omq_backend_rust/src/lib.rs +0 -51
- data/ext/omq_backend_rust/src/notify.rs +0 -69
- data/ext/omq_backend_rust/src/options.rs +0 -220
- data/ext/omq_backend_rust/src/rb.rs +0 -433
- data/ext/omq_backend_rust/src/runtime.rs +0 -439
- data/ext/omq_backend_rust/src/socket.rs +0 -707
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f12671673afe657015b59cf65873132c88c5cbb15cf34962b54318fb944310c
|
|
4
|
+
data.tar.gz: 3b7de77e6e01dc461fa0aa969870472442a2f9b3b4657fa5b35ca1fe90bd8940
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 77dda34fcf69ef4d048d74276b1ae5cd455b23086de0a826f751862014e0d048dd8f27ccc2042f5332ff91a5410f11243f687450ca0093e58ab19377b64ec9dd
|
|
7
|
+
data.tar.gz: 2536562047da85d828e732a03c5a551a85b2b0774821f3ada32f890ba48f4453696c0243ef5e66248cbdee763623d1cf0f9b45bb74ac5e9aedba6aab014558ed
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.3.0] - 2026-08-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added JRuby support through OMQ.java Maven artifacts for Linux x86_64,
|
|
10
|
+
macOS aarch64, macOS x86_64, and Windows x86_64.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Replaced the private MRI/TruffleRuby native extension with the first-class
|
|
15
|
+
`omq-rs` binding.
|
|
16
|
+
|
|
5
17
|
## [0.2.0] - 2026-08-08
|
|
6
18
|
|
|
7
19
|
### Added
|
data/README.md
CHANGED
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
[](https://www.ruby-lang.org)
|
|
7
7
|
|
|
8
8
|
Rust-backed engine for [OMQ](https://github.com/zeromq/omq.rb). Same socket API,
|
|
9
|
-
but networking runs
|
|
10
|
-
|
|
9
|
+
but networking runs through the first-class
|
|
10
|
+
[omq-rs](https://github.com/paddor/omq.rs/tree/main/bindings/ruby) binding on
|
|
11
|
+
MRI and TruffleRuby, or through OMQ.java on JRuby.
|
|
11
12
|
|
|
12
13
|
## Install
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
The `omq-rs` dependency compiles OMQ.rs during installation on MRI and
|
|
16
|
+
TruffleRuby. JRuby uses OMQ.java on Java 25 or newer. The Java gem selects the
|
|
17
|
+
matching OMQ.java artifact on Linux x86_64, macOS aarch64, macOS x86_64, and
|
|
18
|
+
Windows x86_64.
|
|
15
19
|
|
|
16
20
|
```ruby
|
|
17
21
|
# Gemfile
|
|
@@ -73,8 +77,8 @@ the first `bind` or `connect` for a Rust-backed socket.
|
|
|
73
77
|
## Development
|
|
74
78
|
|
|
75
79
|
```sh
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
OMQ_RS_PATH=/path/to/omq.rs/bindings/ruby bundle install
|
|
81
|
+
OMQ_RS_PATH=/path/to/omq.rs/bindings/ruby bundle exec rake
|
|
78
82
|
```
|
|
79
83
|
|
|
80
84
|
## License
|
data/lib/omq/backend/rust.rb
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "omq"
|
|
4
|
-
require_relative "../rust/omq_backend_rust"
|
|
5
4
|
require_relative "../rust/version"
|
|
6
|
-
require_relative "../rust/engine"
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
if RUBY_ENGINE == "jruby"
|
|
7
|
+
require_relative "../rust/java/engine"
|
|
8
|
+
else
|
|
9
|
+
require "omq/rs"
|
|
10
|
+
require_relative "../rust/engine"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if RUBY_ENGINE == "jruby"
|
|
14
|
+
module OMQ
|
|
15
|
+
module Rust
|
|
16
|
+
@io_threads = 1
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
class << self
|
|
19
|
+
attr_accessor :io_threads
|
|
20
|
+
end
|
|
14
21
|
end
|
|
15
22
|
end
|
|
16
23
|
end
|
data/lib/omq/rust/engine.rb
CHANGED
|
@@ -27,8 +27,10 @@ module OMQ
|
|
|
27
27
|
@materialized = false
|
|
28
28
|
@recv_sentinels = 0
|
|
29
29
|
@compression_options = {}
|
|
30
|
-
|
|
31
|
-
@
|
|
30
|
+
@socket = nil
|
|
31
|
+
@monitor_task = nil
|
|
32
|
+
@monitor_watching = false
|
|
33
|
+
@had_peer = false
|
|
32
34
|
|
|
33
35
|
@routing = RoutingStub.new(self)
|
|
34
36
|
end
|
|
@@ -55,7 +57,7 @@ module OMQ
|
|
|
55
57
|
capture_parent_task(parent: parent)
|
|
56
58
|
apply_endpoint_options!(opts)
|
|
57
59
|
ensure_materialized
|
|
58
|
-
resolved = @
|
|
60
|
+
resolved = @socket.bind(endpoint)
|
|
59
61
|
URI.parse(resolved)
|
|
60
62
|
end
|
|
61
63
|
|
|
@@ -64,54 +66,39 @@ module OMQ
|
|
|
64
66
|
capture_parent_task(parent: parent)
|
|
65
67
|
apply_endpoint_options!(opts)
|
|
66
68
|
ensure_materialized
|
|
67
|
-
@
|
|
69
|
+
@socket.connect(endpoint)
|
|
68
70
|
URI.parse(endpoint)
|
|
69
71
|
end
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
def disconnect(endpoint)
|
|
73
|
-
@
|
|
75
|
+
@socket.disconnect(endpoint)
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
|
|
77
79
|
def unbind(endpoint)
|
|
78
|
-
@
|
|
80
|
+
@socket.unbind(endpoint)
|
|
79
81
|
end
|
|
80
82
|
|
|
81
83
|
|
|
82
84
|
def enqueue_send(parts)
|
|
83
85
|
ensure_materialized
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@send_signal_r ||= io_for_native_fd(@native.send_fd)
|
|
88
|
-
loop do
|
|
89
|
-
result = @native.enqueue_send(parts)
|
|
90
|
-
return if result == :ok
|
|
91
|
-
|
|
92
|
-
@send_signal_r.wait_readable
|
|
93
|
-
@send_signal_r.read_nonblock(256, exception: false)
|
|
94
|
-
end
|
|
86
|
+
@socket.send(normalize_outgoing(parts))
|
|
87
|
+
nil
|
|
95
88
|
end
|
|
96
89
|
|
|
97
90
|
|
|
98
91
|
def dequeue_recv
|
|
99
92
|
ensure_materialized
|
|
100
93
|
|
|
101
|
-
|
|
102
|
-
return @recv_batch.shift
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
msg = try_recv_batch
|
|
94
|
+
msg = try_recv
|
|
106
95
|
return msg if msg
|
|
107
96
|
|
|
108
97
|
return take_recv_sentinel if @recv_sentinels.positive?
|
|
109
98
|
|
|
110
99
|
loop do
|
|
111
|
-
@
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
msg = try_recv_batch
|
|
100
|
+
@socket.wait_readable
|
|
101
|
+
msg = try_recv
|
|
115
102
|
return msg if msg
|
|
116
103
|
|
|
117
104
|
return take_recv_sentinel if @recv_sentinels.positive?
|
|
@@ -121,7 +108,7 @@ module OMQ
|
|
|
121
108
|
|
|
122
109
|
def dequeue_recv_sentinel
|
|
123
110
|
@recv_sentinels += 1
|
|
124
|
-
@
|
|
111
|
+
@socket.wake_recv if @materialized
|
|
125
112
|
nil
|
|
126
113
|
end
|
|
127
114
|
|
|
@@ -134,9 +121,8 @@ module OMQ
|
|
|
134
121
|
@all_peers_gone.resolve(nil) unless @all_peers_gone.resolved?
|
|
135
122
|
@subscriber_joined.resolve(nil) unless @subscriber_joined.resolved?
|
|
136
123
|
FdWatcher.unwatch_owner(self) unless Reactor.native_fiber_scheduler?
|
|
137
|
-
@
|
|
138
|
-
|
|
139
|
-
close_io_wrapper(@send_signal_r)
|
|
124
|
+
@socket&.close
|
|
125
|
+
@monitor_task&.stop
|
|
140
126
|
end
|
|
141
127
|
|
|
142
128
|
|
|
@@ -179,19 +165,10 @@ module OMQ
|
|
|
179
165
|
return if @materialized
|
|
180
166
|
|
|
181
167
|
capture_parent_task unless @parent_task
|
|
182
|
-
|
|
183
|
-
@
|
|
184
|
-
@
|
|
185
|
-
|
|
186
|
-
@materialized = true
|
|
187
|
-
|
|
188
|
-
@routing.replay_pending(@native)
|
|
189
|
-
|
|
190
|
-
spawn_lifecycle_watcher(@native.peer_connected_fd, @peer_connected)
|
|
191
|
-
spawn_lifecycle_watcher(@native.all_peers_gone_fd, @all_peers_gone)
|
|
192
|
-
spawn_lifecycle_watcher(@native.subscriber_joined_fd, @subscriber_joined)
|
|
193
|
-
|
|
194
|
-
start_monitor_forwarder if @monitor_queue
|
|
168
|
+
@socket = OMQ::Rust.socket(@socket_type, **extract_options.transform_keys(&:to_sym))
|
|
169
|
+
@materialized = true
|
|
170
|
+
@routing.replay_pending(@socket)
|
|
171
|
+
start_monitor_forwarder
|
|
195
172
|
end
|
|
196
173
|
|
|
197
174
|
|
|
@@ -201,89 +178,82 @@ module OMQ
|
|
|
201
178
|
end
|
|
202
179
|
|
|
203
180
|
|
|
204
|
-
def
|
|
205
|
-
|
|
206
|
-
return unless
|
|
181
|
+
def try_recv
|
|
182
|
+
message = @socket.try_recv
|
|
183
|
+
return unless message
|
|
184
|
+
return message unless @socket_type.to_sym == :SERVER
|
|
207
185
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
msg
|
|
186
|
+
routing_id, *parts = message
|
|
187
|
+
[[routing_id].pack("N"), *parts]
|
|
211
188
|
end
|
|
212
189
|
|
|
213
190
|
|
|
214
|
-
def
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
rescue IOError, Errno::EBADF
|
|
221
|
-
ensure
|
|
222
|
-
close_io_wrapper(io)
|
|
223
|
-
end
|
|
224
|
-
else
|
|
225
|
-
FdWatcher.watch_once(fd, owner: self) do
|
|
226
|
-
promise.resolve(true) unless promise.resolved? || @closed
|
|
227
|
-
end
|
|
191
|
+
def normalize_outgoing(parts)
|
|
192
|
+
return parts unless @socket_type.to_sym == :SERVER
|
|
193
|
+
|
|
194
|
+
routing_id, *body = parts
|
|
195
|
+
unless routing_id.is_a?(String) && routing_id.bytesize == 4
|
|
196
|
+
raise ArgumentError, "SERVER routing ID must be a 4-byte String"
|
|
228
197
|
end
|
|
198
|
+
[routing_id.unpack1("N"), *body]
|
|
229
199
|
end
|
|
230
200
|
|
|
231
201
|
|
|
232
202
|
def start_monitor_forwarder
|
|
203
|
+
return if @monitor_task || @monitor_watching
|
|
204
|
+
|
|
233
205
|
if Reactor.native_fiber_scheduler?
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
until @closed
|
|
237
|
-
monitor_io.wait_readable
|
|
238
|
-
monitor_io.read_nonblock(256, exception: false)
|
|
239
|
-
while (data = @native.try_recv_monitor)
|
|
240
|
-
track_connection_event(data)
|
|
241
|
-
@monitor_queue.enqueue(MonitorEvent.new(**data))
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
rescue IOError, Errno::EBADF
|
|
245
|
-
ensure
|
|
246
|
-
close_io_wrapper(monitor_io)
|
|
206
|
+
@monitor_task = @parent_task.async(transient: true, annotation: "rust-monitor") do
|
|
207
|
+
monitor_loop
|
|
247
208
|
end
|
|
248
209
|
else
|
|
249
|
-
|
|
210
|
+
@monitor_watching = true
|
|
211
|
+
FdWatcher.watch_loop(@socket.monitor_fd, owner: self) do |io|
|
|
250
212
|
io.read_nonblock(256, exception: false)
|
|
251
|
-
while (data = @
|
|
252
|
-
|
|
253
|
-
@monitor_queue.enqueue(MonitorEvent.new(**data))
|
|
213
|
+
while (data = @socket.try_monitor_event)
|
|
214
|
+
handle_monitor_event(data)
|
|
254
215
|
end
|
|
216
|
+
@socket.monitor_fd unless @closed
|
|
255
217
|
end
|
|
256
218
|
end
|
|
257
219
|
end
|
|
258
220
|
|
|
259
221
|
|
|
260
|
-
def
|
|
261
|
-
|
|
262
|
-
|
|
222
|
+
def monitor_loop
|
|
223
|
+
until @closed
|
|
224
|
+
data = @socket.monitor.recv
|
|
225
|
+
break unless data
|
|
263
226
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
io.close
|
|
269
|
-
rescue IOError, SystemCallError
|
|
227
|
+
handle_monitor_event(data)
|
|
228
|
+
end
|
|
229
|
+
rescue IOError, Errno::EBADF
|
|
230
|
+
raise unless @closed
|
|
270
231
|
end
|
|
271
232
|
|
|
272
233
|
|
|
273
|
-
def
|
|
274
|
-
|
|
275
|
-
|
|
234
|
+
def handle_monitor_event(data)
|
|
235
|
+
type = data.fetch(:event)
|
|
236
|
+
endpoint = data[:endpoint]
|
|
237
|
+
connection_id = data[:connection_id]
|
|
276
238
|
|
|
277
|
-
case
|
|
239
|
+
case type
|
|
278
240
|
when :handshake_succeeded
|
|
241
|
+
@had_peer = true
|
|
279
242
|
@connections[connection_id || Object.new] = true
|
|
243
|
+
@peer_connected.resolve(true) unless @peer_connected.resolved?
|
|
280
244
|
when :disconnected
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
@connections.shift
|
|
245
|
+
connection_id ? @connections.delete(connection_id) : @connections.shift
|
|
246
|
+
if @had_peer && @connections.empty? && !@all_peers_gone.resolved?
|
|
247
|
+
@all_peers_gone.resolve(true)
|
|
285
248
|
end
|
|
249
|
+
when :subscribe_received
|
|
250
|
+
@subscriber_joined.resolve(true) unless @subscriber_joined.resolved?
|
|
286
251
|
end
|
|
252
|
+
|
|
253
|
+
return unless @monitor_queue
|
|
254
|
+
|
|
255
|
+
detail = data.except(:event, :endpoint)
|
|
256
|
+
@monitor_queue.enqueue(MonitorEvent.new(type:, endpoint:, detail: detail.empty? ? nil : detail))
|
|
287
257
|
end
|
|
288
258
|
|
|
289
259
|
|
|
@@ -444,9 +414,9 @@ module OMQ
|
|
|
444
414
|
|
|
445
415
|
|
|
446
416
|
def subscribe(prefix)
|
|
447
|
-
|
|
417
|
+
socket = @engine.instance_variable_get(:@socket)
|
|
448
418
|
if @engine.instance_variable_get(:@materialized)
|
|
449
|
-
|
|
419
|
+
socket.subscribe(prefix.b)
|
|
450
420
|
else
|
|
451
421
|
@pending_subscribe << prefix.b
|
|
452
422
|
end
|
|
@@ -454,14 +424,14 @@ module OMQ
|
|
|
454
424
|
|
|
455
425
|
|
|
456
426
|
def unsubscribe(prefix)
|
|
457
|
-
@engine.instance_variable_get(:@
|
|
427
|
+
@engine.instance_variable_get(:@socket).unsubscribe(prefix.b)
|
|
458
428
|
end
|
|
459
429
|
|
|
460
430
|
|
|
461
431
|
def join(group)
|
|
462
|
-
|
|
432
|
+
socket = @engine.instance_variable_get(:@socket)
|
|
463
433
|
if @engine.instance_variable_get(:@materialized)
|
|
464
|
-
|
|
434
|
+
socket.join(group)
|
|
465
435
|
else
|
|
466
436
|
@pending_join << group
|
|
467
437
|
end
|
|
@@ -469,14 +439,14 @@ module OMQ
|
|
|
469
439
|
|
|
470
440
|
|
|
471
441
|
def leave(group)
|
|
472
|
-
@engine.instance_variable_get(:@
|
|
442
|
+
@engine.instance_variable_get(:@socket).leave(group)
|
|
473
443
|
end
|
|
474
444
|
|
|
475
445
|
|
|
476
|
-
def replay_pending(
|
|
477
|
-
@pending_subscribe.each { |p|
|
|
446
|
+
def replay_pending(socket)
|
|
447
|
+
@pending_subscribe.each { |p| socket.subscribe(p) }
|
|
478
448
|
@pending_subscribe.clear
|
|
479
|
-
@pending_join.each { |g|
|
|
449
|
+
@pending_join.each { |g| socket.join(g) }
|
|
480
450
|
@pending_join.clear
|
|
481
451
|
end
|
|
482
452
|
end
|