omq 0.8.0 → 0.10.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 +87 -0
- data/README.md +9 -49
- data/lib/omq/channel.rb +3 -3
- data/lib/omq/client_server.rb +6 -6
- data/lib/omq/engine.rb +641 -0
- data/lib/omq/options.rb +46 -0
- data/lib/omq/pair.rb +2 -2
- data/lib/omq/peer.rb +3 -3
- data/lib/omq/pub_sub.rb +6 -6
- data/lib/omq/push_pull.rb +2 -2
- data/lib/omq/radio_dish.rb +2 -2
- data/lib/omq/reactor.rb +128 -0
- data/lib/omq/readable.rb +42 -0
- data/lib/omq/req_rep.rb +4 -4
- data/lib/omq/router_dealer.rb +4 -4
- data/lib/omq/routing/channel.rb +83 -0
- data/lib/omq/routing/client.rb +56 -0
- data/lib/omq/routing/dealer.rb +57 -0
- data/lib/omq/routing/dish.rb +78 -0
- data/lib/omq/routing/fan_out.rb +131 -0
- data/lib/omq/routing/gather.rb +46 -0
- data/lib/omq/routing/pair.rb +86 -0
- data/lib/omq/routing/peer.rb +101 -0
- data/lib/omq/routing/pub.rb +60 -0
- data/lib/omq/routing/pull.rb +46 -0
- data/lib/omq/routing/push.rb +81 -0
- data/lib/omq/routing/radio.rb +140 -0
- data/lib/omq/routing/rep.rb +101 -0
- data/lib/omq/routing/req.rb +65 -0
- data/lib/omq/routing/round_robin.rb +168 -0
- data/lib/omq/routing/router.rb +110 -0
- data/lib/omq/routing/scatter.rb +82 -0
- data/lib/omq/routing/server.rb +101 -0
- data/lib/omq/routing/sub.rb +78 -0
- data/lib/omq/routing/xpub.rb +72 -0
- data/lib/omq/routing/xsub.rb +83 -0
- data/lib/omq/routing.rb +66 -0
- data/lib/omq/scatter_gather.rb +4 -4
- data/lib/omq/single_frame.rb +18 -0
- data/lib/omq/socket.rb +24 -9
- data/lib/omq/transport/inproc.rb +355 -0
- data/lib/omq/transport/ipc.rb +117 -0
- data/lib/omq/transport/tcp.rb +111 -0
- data/lib/omq/version.rb +1 -1
- data/lib/omq/writable.rb +65 -0
- data/lib/omq.rb +60 -4
- metadata +38 -58
- data/exe/omq +0 -6
- data/lib/omq/cli/base_runner.rb +0 -459
- data/lib/omq/cli/channel.rb +0 -8
- data/lib/omq/cli/client_server.rb +0 -111
- data/lib/omq/cli/config.rb +0 -54
- data/lib/omq/cli/formatter.rb +0 -75
- data/lib/omq/cli/pair.rb +0 -31
- data/lib/omq/cli/peer.rb +0 -8
- data/lib/omq/cli/pipe.rb +0 -265
- data/lib/omq/cli/pub_sub.rb +0 -14
- data/lib/omq/cli/push_pull.rb +0 -14
- data/lib/omq/cli/radio_dish.rb +0 -27
- data/lib/omq/cli/req_rep.rb +0 -83
- data/lib/omq/cli/router_dealer.rb +0 -76
- data/lib/omq/cli/scatter_gather.rb +0 -14
- data/lib/omq/cli.rb +0 -540
- data/lib/omq/zmtp/engine.rb +0 -551
- data/lib/omq/zmtp/options.rb +0 -48
- data/lib/omq/zmtp/reactor.rb +0 -131
- data/lib/omq/zmtp/readable.rb +0 -29
- data/lib/omq/zmtp/routing/channel.rb +0 -81
- data/lib/omq/zmtp/routing/client.rb +0 -56
- data/lib/omq/zmtp/routing/dealer.rb +0 -57
- data/lib/omq/zmtp/routing/dish.rb +0 -80
- data/lib/omq/zmtp/routing/fan_out.rb +0 -131
- data/lib/omq/zmtp/routing/gather.rb +0 -48
- data/lib/omq/zmtp/routing/pair.rb +0 -84
- data/lib/omq/zmtp/routing/peer.rb +0 -100
- data/lib/omq/zmtp/routing/pub.rb +0 -62
- data/lib/omq/zmtp/routing/pull.rb +0 -48
- data/lib/omq/zmtp/routing/push.rb +0 -80
- data/lib/omq/zmtp/routing/radio.rb +0 -139
- data/lib/omq/zmtp/routing/rep.rb +0 -101
- data/lib/omq/zmtp/routing/req.rb +0 -65
- data/lib/omq/zmtp/routing/round_robin.rb +0 -143
- data/lib/omq/zmtp/routing/router.rb +0 -109
- data/lib/omq/zmtp/routing/scatter.rb +0 -81
- data/lib/omq/zmtp/routing/server.rb +0 -100
- data/lib/omq/zmtp/routing/sub.rb +0 -80
- data/lib/omq/zmtp/routing/xpub.rb +0 -74
- data/lib/omq/zmtp/routing/xsub.rb +0 -86
- data/lib/omq/zmtp/routing.rb +0 -65
- data/lib/omq/zmtp/single_frame.rb +0 -20
- data/lib/omq/zmtp/transport/inproc.rb +0 -359
- data/lib/omq/zmtp/transport/ipc.rb +0 -118
- data/lib/omq/zmtp/transport/tcp.rb +0 -117
- data/lib/omq/zmtp/writable.rb +0 -61
- data/lib/omq/zmtp.rb +0 -81
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a81ea3e22e2133f508016129454fca72393068b259400e3d47aaf92d0948136
|
|
4
|
+
data.tar.gz: 71839df55e8ae9edb9db9710b77c5449853cde909c60867958a46e8350f27ead
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e9d603fb6f44b53626e37d88da5dea1297f932bfbc7b3af0b32e5ab4ebdc6c2151f4b7482bca70d5060d31ffc49ba3c771603b6ed1b4dd17ed51733339de67d
|
|
7
|
+
data.tar.gz: b40525791795b8e040a4a275a628c1cdca42eab43fcabfab76407de97f669ff9c1b21d05490509c0e0f5cd0b53505c7f85281343b2c312bdb89fd3eee7d4af75
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,92 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.10.0 — 2026-04-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Auto-close sockets via Async task tree** — all engine tasks (accept
|
|
10
|
+
loops, connection tasks, send/recv pumps, heartbeats, reconnect loops,
|
|
11
|
+
reapers) now live under the caller's Async task. When the `Async` block
|
|
12
|
+
exits, tasks are stopped and `ensure` blocks close IO resources.
|
|
13
|
+
Explicit `Socket#close` is no longer required (but remains available
|
|
14
|
+
and idempotent).
|
|
15
|
+
- **Non-Async usage** — sockets work outside `Async do…end`. A shared IO
|
|
16
|
+
thread hosts the task tree; all blocking operations (bind, connect,
|
|
17
|
+
send, receive, close) are dispatched to it transparently via
|
|
18
|
+
`Reactor.run`. The IO thread shuts down cleanly at process exit,
|
|
19
|
+
respecting the longest linger across all sockets.
|
|
20
|
+
- **Recv prefetching** — `#receive` internally drains up to 64 messages
|
|
21
|
+
per queue dequeue, buffering the excess behind a Mutex. Subsequent
|
|
22
|
+
calls return from the buffer without touching the queue. Thread-safe
|
|
23
|
+
on JRuby. TCP 64B pipelined: 30k → 221k msg/s (7x).
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **Transports are pure IO** — TCP and IPC transports no longer spawn
|
|
28
|
+
tasks. They create server sockets and return them; Engine owns the
|
|
29
|
+
accept loops.
|
|
30
|
+
- **Reactor simplified** — `spawn_pump` and `PumpHandle` removed.
|
|
31
|
+
Reactor exposes `root_task` (shared IO thread's root Async task)
|
|
32
|
+
and `run` (cross-thread dispatch). `stop!` respects max linger.
|
|
33
|
+
- **Flatten `OMQ::ZMTP` namespace into `OMQ`** — with the ZMTP protocol
|
|
34
|
+
layer extracted to `protocol-zmtp`, the `ZMTP` sub-namespace no longer
|
|
35
|
+
makes sense. Engine, routing, transport, and mixins now live directly
|
|
36
|
+
under `OMQ::`. Protocol-zmtp types are referenced as `Protocol::ZMTP::*`.
|
|
37
|
+
|
|
38
|
+
### Performance
|
|
39
|
+
|
|
40
|
+
- **Direct pipe bypass for single-peer inproc** — PAIR, CHANNEL, and
|
|
41
|
+
single-peer RoundRobin types (PUSH, REQ, DEALER, CLIENT, SCATTER)
|
|
42
|
+
enqueue directly into the receiver's recv queue, skipping the
|
|
43
|
+
send_queue and send pump entirely.
|
|
44
|
+
Inproc PUSH/PULL: 200k → 980k msg/s (5x).
|
|
45
|
+
- **Uncapped send queue drain** — the send pump drains the entire queue
|
|
46
|
+
per cycle instead of capping at 64 messages. IO::Stream auto-flushes
|
|
47
|
+
at 64 KB, so writes hit the wire naturally under load.
|
|
48
|
+
IPC latency −12%, TCP latency −10%.
|
|
49
|
+
- **Remove `.b` allocations from PUB/SUB subscription matching** —
|
|
50
|
+
`FanOut#subscribed?` no longer creates temporary binary strings per
|
|
51
|
+
comparison; both topic and prefix are guaranteed binary at rest.
|
|
52
|
+
- **Reuse `written` Set and `latest` Hash across batches** in all send
|
|
53
|
+
pumps (fan-out, round-robin, router, server, peer, rep, radio),
|
|
54
|
+
eliminating per-batch object allocation.
|
|
55
|
+
- **O(1) `connection_removed` for identity-routed sockets** — Router,
|
|
56
|
+
Server, and Peer now maintain a reverse index instead of scanning.
|
|
57
|
+
- **`freeze_message` fast path** — skip `.b.freeze` when the string is
|
|
58
|
+
already a frozen binary string.
|
|
59
|
+
- **Pre-frozen empty frame constants** for REQ/REP delimiter frames.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **Reapers no longer crash on inproc DirectPipe** — PUSH and SCATTER
|
|
64
|
+
reapers skipped for DirectPipe connections that have no receive queue
|
|
65
|
+
(latent bug previously masked by transient task error swallowing).
|
|
66
|
+
- **`send_pump_idle?` made public** on all routing strategies — was
|
|
67
|
+
accidentally private, crashing `Engine#drain_send_queues` with
|
|
68
|
+
linger > 0.
|
|
69
|
+
|
|
70
|
+
## 0.9.0 — 2026-03-31
|
|
71
|
+
|
|
72
|
+
### Breaking
|
|
73
|
+
|
|
74
|
+
- **CLI extracted into omq-cli gem** — the `omq` executable, all CLI
|
|
75
|
+
code (`lib/omq/cli/`), tests, and `CLI.md` have moved to the
|
|
76
|
+
[omq-cli](https://github.com/paddor/omq-cli) gem. `gem install omq`
|
|
77
|
+
no longer provides the `omq` command — use `gem install omq-cli`.
|
|
78
|
+
- **`OMQ.outgoing` / `OMQ.incoming`** registration API moved to omq-cli.
|
|
79
|
+
Library-only users are unaffected (these were CLI-specific).
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
- **Gemspec is library-only** — no `exe/`, no `bindir`, no `executables`.
|
|
84
|
+
- **README** — restored title, replaced inline CLI section with a
|
|
85
|
+
pointer to omq-cli, fixed ZMTP attribution for protocol-zmtp.
|
|
86
|
+
- **DESIGN.md** — acknowledged protocol-zmtp, clarified transient
|
|
87
|
+
task / linger interaction, removed ZMTP wire protocol section (now in
|
|
88
|
+
protocol-zmtp), simplified inproc description, removed CLI section.
|
|
89
|
+
|
|
3
90
|
## 0.8.0 — 2026-03-31
|
|
4
91
|
|
|
5
92
|
### Breaking
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# OMQ —
|
|
1
|
+
# OMQ — Where did the C dependency go!?
|
|
2
2
|
|
|
3
3
|
[](https://github.com/zeromq/omq/actions/workflows/ci.yml)
|
|
4
4
|
[](https://rubygems.org/gems/omq)
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
`gem install omq` — that's it. No libzmq, no compiler, no system packages. Just Ruby.
|
|
9
9
|
|
|
10
|
-
OMQ
|
|
10
|
+
OMQ builds ZeroMQ socket patterns on top of [protocol-zmtp](https://github.com/paddor/protocol-zmtp) (a pure Ruby [ZMTP 3.1](https://rfc.zeromq.org/spec/23/) codec) using [Async](https://github.com/socketry/async) fibers. It speaks native ZeroMQ on the wire and interoperates with libzmq, pyzmq, CZMQ, and everything else in the ZMQ ecosystem.
|
|
11
11
|
|
|
12
12
|
> **234k msg/s** inproc | **49k msg/s** ipc | **36k msg/s** tcp
|
|
13
13
|
>
|
|
@@ -31,12 +31,12 @@ See [GETTING_STARTED.md](GETTING_STARTED.md) for a ~30 min walkthrough of all ma
|
|
|
31
31
|
|
|
32
32
|
- **Zero dependencies on C** — no extensions, no FFI, no libzmq. `gem install` just works everywhere
|
|
33
33
|
- **Fast** — YJIT-optimized hot paths, batched sends, 234k msg/s inproc with 12 µs latency
|
|
34
|
-
-
|
|
34
|
+
- **[`omq` CLI](https://github.com/paddor/omq-cli)** — `gem install omq-cli` for a command-line tool with Ruby eval, Ractor parallelism, and script handlers
|
|
35
35
|
- **Every socket pattern** — req/rep, pub/sub, push/pull, dealer/router, xpub/xsub, pair, and all draft types
|
|
36
36
|
- **Every transport** — tcp, ipc (Unix domain sockets), inproc (in-process queues)
|
|
37
37
|
- **Async-native** — built on fibers, non-blocking from the ground up. A shared IO thread handles sockets outside of Async — no reactor needed for simple scripts
|
|
38
38
|
- **Wire-compatible** — interoperates with libzmq, pyzmq, CZMQ over tcp and ipc
|
|
39
|
-
- **Bind/connect order doesn't matter** — connect before bind, bind before connect, peers come and go. ZeroMQ reconnects and
|
|
39
|
+
- **Bind/connect order doesn't matter** — connect before bind, bind before connect, peers come and go. ZeroMQ reconnects automatically and queued messages drain when peers arrive
|
|
40
40
|
|
|
41
41
|
For architecture internals, see [DESIGN.md](DESIGN.md).
|
|
42
42
|
|
|
@@ -150,58 +150,18 @@ All sockets are thread-safe. Default HWM is 1000 messages per socket. Classes li
|
|
|
150
150
|
| **PEER** | Routing-ID | Fair-queue | Block |
|
|
151
151
|
| **CHANNEL** | Exclusive 1-to-1 | Exclusive 1-to-1 | Block |
|
|
152
152
|
|
|
153
|
-
##
|
|
153
|
+
## CLI
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
Install [omq-cli](https://github.com/paddor/omq-cli) for a command-line tool that sends, receives, pipes, and transforms ZeroMQ messages from the terminal:
|
|
156
156
|
|
|
157
157
|
```sh
|
|
158
|
-
|
|
159
|
-
omq rep -b tcp://:5555 --echo
|
|
160
|
-
|
|
161
|
-
# Upcase server — -e evals Ruby on each incoming message
|
|
162
|
-
omq rep -b tcp://:5555 -e '$F.map(&:upcase)'
|
|
158
|
+
gem install omq-cli
|
|
163
159
|
|
|
164
|
-
|
|
160
|
+
omq rep -b tcp://:5555 --echo
|
|
165
161
|
echo "hello" | omq req -c tcp://localhost:5555
|
|
166
|
-
# => HELLO
|
|
167
|
-
|
|
168
|
-
# PUB/SUB
|
|
169
|
-
omq sub -b tcp://:5556 -s "weather." &
|
|
170
|
-
echo "weather.nyc 72F" | omq pub -c tcp://localhost:5556 -d 0.3
|
|
171
|
-
|
|
172
|
-
# Pipeline with filtering
|
|
173
|
-
tail -f /var/log/syslog | omq push -c tcp://collector:5557
|
|
174
|
-
omq pull -b tcp://:5557 -e 'next unless /error/; $F'
|
|
175
|
-
|
|
176
|
-
# Transform outgoing messages with -E
|
|
177
|
-
echo hello | omq push -c tcp://localhost:5557 -E '$F.map(&:upcase)'
|
|
178
|
-
|
|
179
|
-
# REQ: transform request and reply independently
|
|
180
|
-
echo hello | omq req -c tcp://localhost:5555 \
|
|
181
|
-
-E '$F.map(&:upcase)' -e '$F.map(&:reverse)'
|
|
182
|
-
|
|
183
|
-
# Pipe: PULL → eval → PUSH in one process
|
|
184
|
-
omq pipe -c ipc://@work -c ipc://@sink -e '$F.map(&:upcase)'
|
|
185
|
-
|
|
186
|
-
# Pipe with Ractor workers for CPU parallelism (-P = all CPUs)
|
|
187
|
-
omq pipe -c ipc://@work -c ipc://@sink -P -r./fib -e 'fib(Integer($_)).to_s'
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
`-e` (recv-eval) transforms incoming messages, `-E` (send-eval) transforms outgoing messages. `$F` is the message parts array, `$_` is the first part. Use `-r` to require gems or load scripts that register handlers via `OMQ.incoming` / `OMQ.outgoing`:
|
|
191
|
-
|
|
192
|
-
```ruby
|
|
193
|
-
# my_handler.rb
|
|
194
|
-
db = DB.connect("postgres://localhost/app")
|
|
195
|
-
|
|
196
|
-
OMQ.incoming { db.query($F.first) }
|
|
197
|
-
at_exit { db.close }
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
```sh
|
|
201
|
-
omq pull -b tcp://:5557 -r./my_handler.rb
|
|
202
162
|
```
|
|
203
163
|
|
|
204
|
-
See [
|
|
164
|
+
See the [omq-cli README](https://github.com/paddor/omq-cli) for full documentation.
|
|
205
165
|
|
|
206
166
|
## Development
|
|
207
167
|
|
data/lib/omq/channel.rb
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
class CHANNEL < Socket
|
|
5
|
-
include
|
|
6
|
-
include
|
|
7
|
-
include
|
|
5
|
+
include Readable
|
|
6
|
+
include Writable
|
|
7
|
+
include SingleFrame
|
|
8
8
|
|
|
9
9
|
def initialize(endpoints = nil, linger: 0)
|
|
10
10
|
_init_engine(:CHANNEL, linger: linger)
|
data/lib/omq/client_server.rb
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
class CLIENT < Socket
|
|
5
|
-
include
|
|
6
|
-
include
|
|
7
|
-
include
|
|
5
|
+
include Readable
|
|
6
|
+
include Writable
|
|
7
|
+
include SingleFrame
|
|
8
8
|
|
|
9
9
|
def initialize(endpoints = nil, linger: 0)
|
|
10
10
|
_init_engine(:CLIENT, linger: linger)
|
|
@@ -13,9 +13,9 @@ module OMQ
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
class SERVER < Socket
|
|
16
|
-
include
|
|
17
|
-
include
|
|
18
|
-
include
|
|
16
|
+
include Readable
|
|
17
|
+
include Writable
|
|
18
|
+
include SingleFrame
|
|
19
19
|
|
|
20
20
|
def initialize(endpoints = nil, linger: 0)
|
|
21
21
|
_init_engine(:SERVER, linger: linger)
|