omq 0.19.3 → 0.20.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 +50 -0
- data/README.md +30 -33
- data/lib/omq/engine/recv_pump.rb +29 -2
- data/lib/omq/engine.rb +4 -0
- data/lib/omq/options.rb +6 -3
- data/lib/omq/pair.rb +16 -3
- data/lib/omq/pub_sub.rb +53 -14
- data/lib/omq/push_pull.rb +11 -6
- data/lib/omq/req_rep.rb +31 -6
- data/lib/omq/router_dealer.rb +31 -6
- data/lib/omq/routing/fair_queue.rb +6 -0
- data/lib/omq/routing/pair.rb +1 -1
- data/lib/omq/routing/round_robin.rb +15 -3
- data/lib/omq/socket.rb +23 -5
- data/lib/omq/version.rb +1 -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: 6800afbb9c1e8dd9f7a973f7105ff94a13bf99bc4f6a5c302fc3ff7c2ee3220a
|
|
4
|
+
data.tar.gz: a166c7b6d54596565574b24183d64c649b8a58c8d826bf3074eb800cb9f0f52f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50c173ee88a276291a82864adadeb11500265d3df24164de183407af0b1586498105b4a837a75027f2762b5a91a9f70ad6dbf0a718f9dc179e920ba3a238e266
|
|
7
|
+
data.tar.gz: da22b101249f108c9dc59763f1589fb1cd350b73bdbd62a3f59424079db68cb892bc1a93052cab603ae0f62eefb35fee1ff3a10743f500ed68dab622d3a3a027
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.20.0 — 2026-04-14
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Default `linger` is now `Float::INFINITY`** (matches libzmq). Sockets
|
|
8
|
+
wait forever on close for queued messages to drain unless `linger` is
|
|
9
|
+
set explicitly. Pass `linger: 0` to keep the old "drop on close"
|
|
10
|
+
behavior. `Options#linger` now always returns a `Numeric` (never `nil`).
|
|
11
|
+
- **Socket constructors accept a block.** `OMQ::PUSH.new { |p| ... }`
|
|
12
|
+
yields the socket, then closes it (even on exception) — `File.open`
|
|
13
|
+
style. Applies to every socket type.
|
|
14
|
+
- **Per-socket-type constructors take the full kwarg set** they support:
|
|
15
|
+
`send_hwm`, `recv_hwm`, `send_timeout`, `recv_timeout`, `linger`,
|
|
16
|
+
`backend`, plus pattern-specific ones (`subscribe:`, `on_mute:`,
|
|
17
|
+
`conflate:`). Previously some only accepted `linger`.
|
|
18
|
+
- **Hot-path recv pump: size-1 fast path for byte counting.** The
|
|
19
|
+
`FAIRNESS_BYTES` accumulator in `RecvPump#start_direct` (and its
|
|
20
|
+
transform variant) now short-circuits single-frame messages instead
|
|
21
|
+
of iterating, keeping both entry methods monomorphic for YJIT.
|
|
22
|
+
- **Hot-path round-robin `batch_bytes`** short-circuits single-frame
|
|
23
|
+
batches the same way, replacing `parts.sum { ... }` with a direct
|
|
24
|
+
`bytesize` call.
|
|
25
|
+
- **Fair-queue single-connection fast path.** `try_dequeue` now skips
|
|
26
|
+
`Enumerator#next` when a fair-queue recv socket has exactly one peer
|
|
27
|
+
(the common case) and dequeues directly from the sole queue.
|
|
28
|
+
- **`drain_send_queues` is cancellation-safe.** `Async::Stop` raised at
|
|
29
|
+
the drain sleep point (e.g. from a parent `task.stop`) is now rescued
|
|
30
|
+
so `Socket#close` can finish the rest of its teardown instead of
|
|
31
|
+
propagating the cancellation out of the ensure path.
|
|
32
|
+
- **Hot-path `Array#[0]` → `Array#first`** in writable batching and
|
|
33
|
+
pair routing — `#first` has a dedicated YJIT specialization that is
|
|
34
|
+
measurably faster on single-frame messages.
|
|
35
|
+
- **Benchmark size sweep reworked.** `SIZES` is now a ×4 geometric
|
|
36
|
+
progression `128, 512, 2048, 8192, 32_768` bytes, replacing
|
|
37
|
+
`64 / 1024 / 8192 / 65_536`. Fills the 64 B → 1 KiB gap, drops 64 KiB
|
|
38
|
+
(tcp/ipc already saturated at 32 KiB, inproc regressed). `report.rb
|
|
39
|
+
--update-readme` and `bench/README.md` regenerated.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- **Slow `send_timeout` test.** The `raises IO::TimeoutError when send
|
|
44
|
+
blocks longer than send_timeout` test now constructs its PUSH with
|
|
45
|
+
`linger: 0`. Previously the undeliverable fill message combined with
|
|
46
|
+
the new default `linger: Float::INFINITY` made the close-in-ensure
|
|
47
|
+
path wait out the full linger budget, silently eating the enclosing
|
|
48
|
+
`task.with_timeout` and inflating suite runtime.
|
|
49
|
+
- **Test suite runtime.** `TEST_ASYNC_TIMEOUT` lowered from 5 s to 1 s:
|
|
50
|
+
real hangs fail fast and the full suite finishes in ~3 s instead of
|
|
51
|
+
~8 s.
|
|
52
|
+
|
|
3
53
|
## 0.19.3 — 2026-04-13
|
|
4
54
|
|
|
5
55
|
### Changed
|
data/README.md
CHANGED
|
@@ -1,41 +1,32 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ØMQ — ZeroMQ for Ruby, no C required
|
|
2
2
|
|
|
3
3
|
[](https://github.com/zeromq/omq/actions/workflows/ci.yml)
|
|
4
4
|
[](https://rubygems.org/gems/omq)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.ruby-lang.org)
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
12
|
-
> **980k msg/s** inproc | **38k msg/s** ipc | **31k msg/s** tcp
|
|
8
|
+
> **932k msg/s** inproc | **328k msg/s** ipc | **329k msg/s** tcp
|
|
13
9
|
>
|
|
14
|
-
> **
|
|
10
|
+
> **11.5 µs** inproc latency | **54 µs** ipc | **69 µs** tcp
|
|
15
11
|
>
|
|
16
12
|
> Ruby 4.0 + YJIT on a Linux VM — see [`bench/`](bench/) for full results
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## What is ZeroMQ?
|
|
21
|
-
|
|
22
|
-
Brokerless message-oriented middleware. No central server, no extra hop — processes talk directly to each other, cutting latency in half compared to broker-based systems. You get the patterns you'd normally build on top of RabbitMQ or Redis — pub/sub, work distribution, request/reply, fan-out — but decentralized, with no single point of failure.
|
|
23
|
-
|
|
24
|
-
Networking is hard. ZeroMQ abstracts away reconnection, queuing, load balancing, and framing so you can focus on what your system actually does. Start with threads talking over `inproc://`, split into processes with `ipc://`, scale across machines with `tcp://` — same code, same API, just change the URL.
|
|
14
|
+
`gem install omq` and you're done. No libzmq, no compiler, no system packages — just Ruby talking to every other ZeroMQ peer out there.
|
|
25
15
|
|
|
26
|
-
|
|
16
|
+
ØMQ gives your Ruby processes a way to talk to each other — and to anything else speaking ZeroMQ — without a broker in the middle. Same API whether they live in the same process, on the same machine, or across the network. Reconnects, queuing, and back-pressure are handled for you; you write the interesting part.
|
|
27
17
|
|
|
28
|
-
|
|
18
|
+
New to ZeroMQ? Start with [GETTING_STARTED.md](GETTING_STARTED.md) — a ~30 min walkthrough of every major pattern with working code.
|
|
29
19
|
|
|
30
20
|
## Highlights
|
|
31
21
|
|
|
32
22
|
- **Zero dependencies on C** — no extensions, no FFI, no libzmq. `gem install` just works everywhere
|
|
33
|
-
- **Fast** — YJIT-optimized hot paths, batched sends,
|
|
34
|
-
- **[`omq` CLI](https://github.com/paddor/omq-cli)** —
|
|
23
|
+
- **Fast** — YJIT-optimized hot paths, batched sends, GC-tuned allocations, buffered I/O via [io-stream](https://github.com/socketry/io-stream), direct-pipe inproc bypass
|
|
24
|
+
- **[`omq` CLI](https://github.com/paddor/omq-cli)** — a powerful swiss army knife for ØMQ. `gem install omq-cli`
|
|
35
25
|
- **Every socket pattern** — req/rep, pub/sub, push/pull, dealer/router, xpub/xsub, pair, and all draft types
|
|
36
26
|
- **Every transport** — tcp, ipc (Unix domain sockets), inproc (in-process queues)
|
|
37
|
-
- **Async-native** — built on fibers, non-blocking from the ground up
|
|
38
|
-
- **
|
|
27
|
+
- **Async-native** — built on fibers, non-blocking from the ground up
|
|
28
|
+
- **Works outside Async too** — a shared IO thread handles sockets for callers that aren't inside a reactor, so simple scripts just work
|
|
29
|
+
- **Wire-compatible** — interoperates with libzmq, pyzmq, CZMQ, zmq.rs over tcp and ipc
|
|
39
30
|
- **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
31
|
|
|
41
32
|
For architecture internals, see [DESIGN.md](DESIGN.md).
|
|
@@ -164,15 +155,15 @@ All sockets are thread-safe. Default HWM is 1000 messages per socket. `max_messa
|
|
|
164
155
|
|
|
165
156
|
#### Draft (single-frame only)
|
|
166
157
|
|
|
167
|
-
|
|
158
|
+
Each draft pattern lives in its own gem — install only the ones you use.
|
|
168
159
|
|
|
169
|
-
| Pattern | Send | Receive | When HWM full |
|
|
170
|
-
|
|
171
|
-
| **CLIENT** / **SERVER** | Work-stealing / routing-ID | Fair-queue | Block |
|
|
172
|
-
| **RADIO** / **DISH** | Group fan-out | Group filter | Drop |
|
|
173
|
-
| **SCATTER** / **GATHER** | Work-stealing | Fair-queue | Block |
|
|
174
|
-
| **PEER** | Routing-ID | Fair-queue | Block |
|
|
175
|
-
| **CHANNEL** | Exclusive 1-to-1 | Exclusive 1-to-1 | Block |
|
|
160
|
+
| Pattern | Send | Receive | When HWM full | Gem |
|
|
161
|
+
|---------|------|---------|---------------|-----|
|
|
162
|
+
| **CLIENT** / **SERVER** | Work-stealing / routing-ID | Fair-queue | Block | [`omq-rfc-clientserver`](https://github.com/paddor/omq-rfc-clientserver) |
|
|
163
|
+
| **RADIO** / **DISH** | Group fan-out | Group filter | Drop | [`omq-rfc-radiodish`](https://github.com/paddor/omq-rfc-radiodish) |
|
|
164
|
+
| **SCATTER** / **GATHER** | Work-stealing | Fair-queue | Block | [`omq-rfc-scattergather`](https://github.com/paddor/omq-rfc-scattergather) |
|
|
165
|
+
| **PEER** | Routing-ID | Fair-queue | Block | [`omq-rfc-p2p`](https://github.com/paddor/omq-rfc-p2p) |
|
|
166
|
+
| **CHANNEL** | Exclusive 1-to-1 | Exclusive 1-to-1 | Block | [`omq-rfc-channel`](https://github.com/paddor/omq-rfc-channel) |
|
|
176
167
|
|
|
177
168
|
## CLI
|
|
178
169
|
|
|
@@ -192,6 +183,12 @@ See the [omq-cli README](https://github.com/paddor/omq-cli) for full documentati
|
|
|
192
183
|
- **[omq-ffi](https://github.com/paddor/omq-ffi)** — libzmq FFI backend. Same OMQ socket API, but backed by libzmq instead of the pure Ruby ZMTP stack. Useful for interop testing and when you need libzmq-specific features. Requires libzmq installed.
|
|
193
184
|
- **[omq-ractor](https://github.com/paddor/omq-ractor)** — bridge OMQ sockets into Ruby Ractors for true parallel processing across cores. I/O stays on the main Ractor, worker Ractors do pure computation.
|
|
194
185
|
|
|
186
|
+
### Protocol extensions (RFCs)
|
|
187
|
+
|
|
188
|
+
Optional plug-ins that extend the ZMTP wire protocol. Each is a separate gem; load the ones you need.
|
|
189
|
+
|
|
190
|
+
- **[omq-rfc-zstd](https://github.com/paddor/omq-rfc-zstd)** — transparent Zstandard compression on the wire, negotiated per peer via READY properties.
|
|
191
|
+
|
|
195
192
|
## Development
|
|
196
193
|
|
|
197
194
|
```sh
|
|
@@ -210,16 +207,16 @@ the stack.
|
|
|
210
207
|
# clone OMQ and its sibling repos into the same parent directory
|
|
211
208
|
git clone https://github.com/paddor/omq.git
|
|
212
209
|
git clone https://github.com/paddor/protocol-zmtp.git
|
|
213
|
-
git clone https://github.com/paddor/
|
|
214
|
-
git clone https://github.com/paddor/omq-rfc-blake3zmq.git
|
|
215
|
-
git clone https://github.com/paddor/omq-rfc-channel.git
|
|
210
|
+
git clone https://github.com/paddor/omq-rfc-zstd.git
|
|
216
211
|
git clone https://github.com/paddor/omq-rfc-clientserver.git
|
|
217
|
-
git clone https://github.com/paddor/omq-rfc-p2p.git
|
|
218
|
-
git clone https://github.com/paddor/omq-rfc-qos.git
|
|
219
212
|
git clone https://github.com/paddor/omq-rfc-radiodish.git
|
|
220
213
|
git clone https://github.com/paddor/omq-rfc-scattergather.git
|
|
214
|
+
git clone https://github.com/paddor/omq-rfc-channel.git
|
|
215
|
+
git clone https://github.com/paddor/omq-rfc-p2p.git
|
|
216
|
+
git clone https://github.com/paddor/omq-rfc-qos.git
|
|
221
217
|
git clone https://github.com/paddor/omq-ffi.git
|
|
222
218
|
git clone https://github.com/paddor/omq-ractor.git
|
|
219
|
+
git clone https://github.com/paddor/nuckle.git
|
|
223
220
|
|
|
224
221
|
cd omq
|
|
225
222
|
OMQ_DEV=1 bundle install
|
data/lib/omq/engine/recv_pump.rb
CHANGED
|
@@ -104,7 +104,19 @@ module OMQ
|
|
|
104
104
|
recv_queue.enqueue(msg)
|
|
105
105
|
|
|
106
106
|
count += 1
|
|
107
|
-
|
|
107
|
+
|
|
108
|
+
# hot path
|
|
109
|
+
if count_bytes
|
|
110
|
+
if msg.size == 1
|
|
111
|
+
bytes += msg.first.bytesize
|
|
112
|
+
else
|
|
113
|
+
i, n = 0, msg.size
|
|
114
|
+
while i < n
|
|
115
|
+
bytes += msg[i].bytesize
|
|
116
|
+
i += 1
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
108
120
|
end
|
|
109
121
|
|
|
110
122
|
task.yield
|
|
@@ -132,13 +144,28 @@ module OMQ
|
|
|
132
144
|
loop do
|
|
133
145
|
count = 0
|
|
134
146
|
bytes = 0
|
|
147
|
+
|
|
135
148
|
while count < FAIRNESS_MESSAGES && bytes < FAIRNESS_BYTES
|
|
136
149
|
msg = conn.receive_message
|
|
137
150
|
engine.emit_verbose_msg_received(conn, msg)
|
|
138
151
|
recv_queue.enqueue(msg)
|
|
152
|
+
|
|
139
153
|
count += 1
|
|
140
|
-
|
|
154
|
+
|
|
155
|
+
# hot path
|
|
156
|
+
if count_bytes
|
|
157
|
+
if msg.size == 1
|
|
158
|
+
bytes += msg.first.bytesize
|
|
159
|
+
else
|
|
160
|
+
i, n = 0, msg.size
|
|
161
|
+
while i < n
|
|
162
|
+
bytes += msg[i].bytesize
|
|
163
|
+
i += 1
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
141
167
|
end
|
|
168
|
+
|
|
142
169
|
task.yield
|
|
143
170
|
end
|
|
144
171
|
rescue Async::Stop, Async::Cancel
|
data/lib/omq/engine.rb
CHANGED
|
@@ -587,6 +587,10 @@ module OMQ
|
|
|
587
587
|
break if deadline && (deadline - Async::Clock.now) <= 0
|
|
588
588
|
sleep 0.001
|
|
589
589
|
end
|
|
590
|
+
rescue Async::Stop
|
|
591
|
+
# Parent task is being cancelled — stop draining and let close
|
|
592
|
+
# proceed with the rest of teardown instead of propagating the
|
|
593
|
+
# cancellation out of the ensure path.
|
|
590
594
|
end
|
|
591
595
|
|
|
592
596
|
|
data/lib/omq/options.rb
CHANGED
|
@@ -10,9 +10,11 @@ module OMQ
|
|
|
10
10
|
DEFAULT_HWM = 1000
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
# @param linger [
|
|
13
|
+
# @param linger [Numeric] linger period in seconds on close
|
|
14
|
+
# (default Float::INFINITY = wait forever, matching libzmq).
|
|
15
|
+
# Pass 0 for immediate drop-on-close.
|
|
14
16
|
#
|
|
15
|
-
def initialize(linger:
|
|
17
|
+
def initialize(linger: Float::INFINITY)
|
|
16
18
|
@send_hwm = DEFAULT_HWM
|
|
17
19
|
@recv_hwm = DEFAULT_HWM
|
|
18
20
|
@linger = linger
|
|
@@ -39,7 +41,8 @@ module OMQ
|
|
|
39
41
|
# @!attribute recv_hwm
|
|
40
42
|
# @return [Integer] receive high water mark (default 1000, 0 = unbounded)
|
|
41
43
|
# @!attribute linger
|
|
42
|
-
# @return [
|
|
44
|
+
# @return [Numeric] linger period in seconds on close
|
|
45
|
+
# (Float::INFINITY = wait forever, 0 = immediate drop)
|
|
43
46
|
# @!attribute identity
|
|
44
47
|
# @return [String] socket identity for ROUTER addressing (default "")
|
|
45
48
|
# @!attribute router_mandatory
|
data/lib/omq/pair.rb
CHANGED
|
@@ -8,12 +8,25 @@ module OMQ
|
|
|
8
8
|
include Writable
|
|
9
9
|
|
|
10
10
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
11
|
-
# @param linger [
|
|
11
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
12
|
+
# @param send_hwm [Integer, nil] send high water mark (nil uses default)
|
|
13
|
+
# @param recv_hwm [Integer, nil] receive high water mark (nil uses default)
|
|
14
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
15
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
12
16
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
13
17
|
#
|
|
14
|
-
def initialize(endpoints = nil, linger:
|
|
15
|
-
|
|
18
|
+
def initialize(endpoints = nil, linger: Float::INFINITY,
|
|
19
|
+
send_hwm: nil, recv_hwm: nil,
|
|
20
|
+
send_timeout: nil, recv_timeout: nil,
|
|
21
|
+
backend: nil, &block)
|
|
22
|
+
init_engine(:PAIR, send_hwm: send_hwm, recv_hwm: recv_hwm,
|
|
23
|
+
send_timeout: send_timeout, recv_timeout: recv_timeout,
|
|
24
|
+
backend: backend)
|
|
25
|
+
@options.linger = linger
|
|
16
26
|
attach_endpoints(endpoints, default: :connect)
|
|
27
|
+
finalize_init(&block)
|
|
17
28
|
end
|
|
29
|
+
|
|
18
30
|
end
|
|
31
|
+
|
|
19
32
|
end
|
data/lib/omq/pub_sub.rb
CHANGED
|
@@ -7,15 +7,23 @@ module OMQ
|
|
|
7
7
|
include Writable
|
|
8
8
|
|
|
9
9
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
10
|
-
# @param linger [
|
|
10
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
11
|
+
# @param send_hwm [Integer, nil] send high water mark
|
|
12
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
11
13
|
# @param on_mute [Symbol] mute strategy for slow subscribers
|
|
12
14
|
# @param conflate [Boolean] keep only latest message per topic
|
|
13
15
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
14
16
|
#
|
|
15
|
-
def initialize(endpoints = nil, linger:
|
|
16
|
-
|
|
17
|
+
def initialize(endpoints = nil, linger: Float::INFINITY,
|
|
18
|
+
send_hwm: nil, send_timeout: nil,
|
|
19
|
+
on_mute: :drop_newest, conflate: false, backend: nil, &block)
|
|
20
|
+
init_engine(:PUB, send_hwm: send_hwm, send_timeout: send_timeout,
|
|
21
|
+
on_mute: on_mute, conflate: conflate, backend: backend)
|
|
22
|
+
@options.linger = linger
|
|
17
23
|
attach_endpoints(endpoints, default: :bind)
|
|
24
|
+
finalize_init(&block)
|
|
18
25
|
end
|
|
26
|
+
|
|
19
27
|
end
|
|
20
28
|
|
|
21
29
|
|
|
@@ -29,16 +37,21 @@ module OMQ
|
|
|
29
37
|
EVERYTHING = ''
|
|
30
38
|
|
|
31
39
|
|
|
32
|
-
# @param endpoints [String, nil]
|
|
33
|
-
# @param
|
|
40
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
41
|
+
# @param recv_hwm [Integer, nil] receive high water mark
|
|
42
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
34
43
|
# @param subscribe [String, nil] subscription prefix; +nil+ (default)
|
|
35
44
|
# means no subscription — call {#subscribe} explicitly.
|
|
36
45
|
# @param on_mute [Symbol] :block (default), :drop_newest, or :drop_oldest
|
|
46
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
37
47
|
#
|
|
38
|
-
def initialize(endpoints = nil,
|
|
39
|
-
|
|
48
|
+
def initialize(endpoints = nil, recv_hwm: nil, recv_timeout: nil,
|
|
49
|
+
subscribe: nil, on_mute: :block, backend: nil, &block)
|
|
50
|
+
init_engine(:SUB, recv_hwm: recv_hwm, recv_timeout: recv_timeout,
|
|
51
|
+
on_mute: on_mute, backend: backend)
|
|
40
52
|
attach_endpoints(endpoints, default: :connect)
|
|
41
53
|
self.subscribe(subscribe) unless subscribe.nil?
|
|
54
|
+
finalize_init(&block)
|
|
42
55
|
end
|
|
43
56
|
|
|
44
57
|
|
|
@@ -60,6 +73,7 @@ module OMQ
|
|
|
60
73
|
def unsubscribe(prefix)
|
|
61
74
|
@engine.routing.unsubscribe(prefix)
|
|
62
75
|
end
|
|
76
|
+
|
|
63
77
|
end
|
|
64
78
|
|
|
65
79
|
|
|
@@ -70,14 +84,26 @@ module OMQ
|
|
|
70
84
|
include Writable
|
|
71
85
|
|
|
72
86
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
73
|
-
# @param linger [
|
|
87
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
88
|
+
# @param send_hwm [Integer, nil] send high water mark
|
|
89
|
+
# @param recv_hwm [Integer, nil] receive high water mark
|
|
90
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
91
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
74
92
|
# @param on_mute [Symbol] mute strategy for slow subscribers
|
|
75
93
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
76
94
|
#
|
|
77
|
-
def initialize(endpoints = nil, linger:
|
|
78
|
-
|
|
95
|
+
def initialize(endpoints = nil, linger: Float::INFINITY,
|
|
96
|
+
send_hwm: nil, recv_hwm: nil,
|
|
97
|
+
send_timeout: nil, recv_timeout: nil,
|
|
98
|
+
on_mute: :drop_newest, backend: nil, &block)
|
|
99
|
+
init_engine(:XPUB, send_hwm: send_hwm, recv_hwm: recv_hwm,
|
|
100
|
+
send_timeout: send_timeout, recv_timeout: recv_timeout,
|
|
101
|
+
on_mute: on_mute, backend: backend)
|
|
102
|
+
@options.linger = linger
|
|
79
103
|
attach_endpoints(endpoints, default: :bind)
|
|
104
|
+
finalize_init(&block)
|
|
80
105
|
end
|
|
106
|
+
|
|
81
107
|
end
|
|
82
108
|
|
|
83
109
|
|
|
@@ -87,17 +113,30 @@ module OMQ
|
|
|
87
113
|
include Readable
|
|
88
114
|
include Writable
|
|
89
115
|
|
|
90
|
-
# @param endpoints [String, nil]
|
|
91
|
-
# @param linger [
|
|
116
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
117
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
118
|
+
# @param send_hwm [Integer, nil] send high water mark
|
|
119
|
+
# @param recv_hwm [Integer, nil] receive high water mark
|
|
120
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
121
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
92
122
|
# @param subscribe [String, nil] subscription prefix; +nil+ (default)
|
|
93
123
|
# means no subscription — send a subscribe frame explicitly.
|
|
94
124
|
# @param on_mute [Symbol] mute strategy (:block, :drop_newest, :drop_oldest)
|
|
95
125
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
96
126
|
#
|
|
97
|
-
def initialize(endpoints = nil, linger:
|
|
98
|
-
|
|
127
|
+
def initialize(endpoints = nil, linger: Float::INFINITY,
|
|
128
|
+
send_hwm: nil, recv_hwm: nil,
|
|
129
|
+
send_timeout: nil, recv_timeout: nil,
|
|
130
|
+
subscribe: nil, on_mute: :block, backend: nil, &block)
|
|
131
|
+
init_engine(:XSUB, send_hwm: send_hwm, recv_hwm: recv_hwm,
|
|
132
|
+
send_timeout: send_timeout, recv_timeout: recv_timeout,
|
|
133
|
+
on_mute: on_mute, backend: backend)
|
|
134
|
+
@options.linger = linger
|
|
99
135
|
attach_endpoints(endpoints, default: :connect)
|
|
100
136
|
send("\x01#{subscribe}".b) unless subscribe.nil?
|
|
137
|
+
finalize_init(&block)
|
|
101
138
|
end
|
|
139
|
+
|
|
102
140
|
end
|
|
141
|
+
|
|
103
142
|
end
|
data/lib/omq/push_pull.rb
CHANGED
|
@@ -7,15 +7,18 @@ module OMQ
|
|
|
7
7
|
include Writable
|
|
8
8
|
|
|
9
9
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
10
|
-
# @param linger [
|
|
10
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
11
11
|
# @param send_hwm [Integer, nil] send high water mark (nil uses default)
|
|
12
12
|
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
13
13
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
14
14
|
#
|
|
15
|
-
def initialize(endpoints = nil, linger:
|
|
16
|
-
init_engine(:PUSH,
|
|
15
|
+
def initialize(endpoints = nil, linger: Float::INFINITY, send_hwm: nil, send_timeout: nil, backend: nil, &block)
|
|
16
|
+
init_engine(:PUSH, send_hwm: send_hwm, send_timeout: send_timeout, backend: backend)
|
|
17
|
+
@options.linger = linger
|
|
17
18
|
attach_endpoints(endpoints, default: :connect)
|
|
19
|
+
finalize_init(&block)
|
|
18
20
|
end
|
|
21
|
+
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
|
|
@@ -25,14 +28,16 @@ module OMQ
|
|
|
25
28
|
include Readable
|
|
26
29
|
|
|
27
30
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
28
|
-
# @param linger [Integer] linger period in seconds
|
|
29
31
|
# @param recv_hwm [Integer, nil] receive high water mark (nil uses default)
|
|
30
32
|
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
31
33
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
32
34
|
#
|
|
33
|
-
def initialize(endpoints = nil,
|
|
34
|
-
init_engine(:PULL,
|
|
35
|
+
def initialize(endpoints = nil, recv_hwm: nil, recv_timeout: nil, backend: nil, &block)
|
|
36
|
+
init_engine(:PULL, recv_hwm: recv_hwm, recv_timeout: recv_timeout, backend: backend)
|
|
35
37
|
attach_endpoints(endpoints, default: :bind)
|
|
38
|
+
finalize_init(&block)
|
|
36
39
|
end
|
|
40
|
+
|
|
37
41
|
end
|
|
42
|
+
|
|
38
43
|
end
|
data/lib/omq/req_rep.rb
CHANGED
|
@@ -8,13 +8,25 @@ module OMQ
|
|
|
8
8
|
include Writable
|
|
9
9
|
|
|
10
10
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
11
|
-
# @param linger [
|
|
11
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
12
|
+
# @param send_hwm [Integer, nil] send high water mark
|
|
13
|
+
# @param recv_hwm [Integer, nil] receive high water mark
|
|
14
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
15
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
12
16
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
13
17
|
#
|
|
14
|
-
def initialize(endpoints = nil, linger:
|
|
15
|
-
|
|
18
|
+
def initialize(endpoints = nil, linger: Float::INFINITY,
|
|
19
|
+
send_hwm: nil, recv_hwm: nil,
|
|
20
|
+
send_timeout: nil, recv_timeout: nil,
|
|
21
|
+
backend: nil, &block)
|
|
22
|
+
init_engine(:REQ, send_hwm: send_hwm, recv_hwm: recv_hwm,
|
|
23
|
+
send_timeout: send_timeout, recv_timeout: recv_timeout,
|
|
24
|
+
backend: backend)
|
|
25
|
+
@options.linger = linger
|
|
16
26
|
attach_endpoints(endpoints, default: :connect)
|
|
27
|
+
finalize_init(&block)
|
|
17
28
|
end
|
|
29
|
+
|
|
18
30
|
end
|
|
19
31
|
|
|
20
32
|
|
|
@@ -25,12 +37,25 @@ module OMQ
|
|
|
25
37
|
include Writable
|
|
26
38
|
|
|
27
39
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
28
|
-
# @param linger [
|
|
40
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
41
|
+
# @param send_hwm [Integer, nil] send high water mark
|
|
42
|
+
# @param recv_hwm [Integer, nil] receive high water mark
|
|
43
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
44
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
29
45
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
30
46
|
#
|
|
31
|
-
def initialize(endpoints = nil, linger:
|
|
32
|
-
|
|
47
|
+
def initialize(endpoints = nil, linger: Float::INFINITY,
|
|
48
|
+
send_hwm: nil, recv_hwm: nil,
|
|
49
|
+
send_timeout: nil, recv_timeout: nil,
|
|
50
|
+
backend: nil, &block)
|
|
51
|
+
init_engine(:REP, send_hwm: send_hwm, recv_hwm: recv_hwm,
|
|
52
|
+
send_timeout: send_timeout, recv_timeout: recv_timeout,
|
|
53
|
+
backend: backend)
|
|
54
|
+
@options.linger = linger
|
|
33
55
|
attach_endpoints(endpoints, default: :bind)
|
|
56
|
+
finalize_init(&block)
|
|
34
57
|
end
|
|
58
|
+
|
|
35
59
|
end
|
|
60
|
+
|
|
36
61
|
end
|
data/lib/omq/router_dealer.rb
CHANGED
|
@@ -8,13 +8,25 @@ module OMQ
|
|
|
8
8
|
include Writable
|
|
9
9
|
|
|
10
10
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
11
|
-
# @param linger [
|
|
11
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
12
|
+
# @param send_hwm [Integer, nil] send high water mark
|
|
13
|
+
# @param recv_hwm [Integer, nil] receive high water mark
|
|
14
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
15
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
12
16
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
13
17
|
#
|
|
14
|
-
def initialize(endpoints = nil, linger:
|
|
15
|
-
|
|
18
|
+
def initialize(endpoints = nil, linger: Float::INFINITY,
|
|
19
|
+
send_hwm: nil, recv_hwm: nil,
|
|
20
|
+
send_timeout: nil, recv_timeout: nil,
|
|
21
|
+
backend: nil, &block)
|
|
22
|
+
init_engine(:DEALER, send_hwm: send_hwm, recv_hwm: recv_hwm,
|
|
23
|
+
send_timeout: send_timeout, recv_timeout: recv_timeout,
|
|
24
|
+
backend: backend)
|
|
25
|
+
@options.linger = linger
|
|
16
26
|
attach_endpoints(endpoints, default: :connect)
|
|
27
|
+
finalize_init(&block)
|
|
17
28
|
end
|
|
29
|
+
|
|
18
30
|
end
|
|
19
31
|
|
|
20
32
|
|
|
@@ -25,12 +37,23 @@ module OMQ
|
|
|
25
37
|
include Writable
|
|
26
38
|
|
|
27
39
|
# @param endpoints [String, nil] endpoint to bind/connect
|
|
28
|
-
# @param linger [
|
|
40
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
41
|
+
# @param send_hwm [Integer, nil] send high water mark
|
|
42
|
+
# @param recv_hwm [Integer, nil] receive high water mark
|
|
43
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
44
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
29
45
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
30
46
|
#
|
|
31
|
-
def initialize(endpoints = nil, linger:
|
|
32
|
-
|
|
47
|
+
def initialize(endpoints = nil, linger: Float::INFINITY,
|
|
48
|
+
send_hwm: nil, recv_hwm: nil,
|
|
49
|
+
send_timeout: nil, recv_timeout: nil,
|
|
50
|
+
backend: nil, &block)
|
|
51
|
+
init_engine(:ROUTER, send_hwm: send_hwm, recv_hwm: recv_hwm,
|
|
52
|
+
send_timeout: send_timeout, recv_timeout: recv_timeout,
|
|
53
|
+
backend: backend)
|
|
54
|
+
@options.linger = linger
|
|
33
55
|
attach_endpoints(endpoints, default: :bind)
|
|
56
|
+
finalize_init(&block)
|
|
34
57
|
end
|
|
35
58
|
|
|
36
59
|
|
|
@@ -44,5 +67,7 @@ module OMQ
|
|
|
44
67
|
parts = message.is_a?(Array) ? message : [message]
|
|
45
68
|
send([receiver, '', *parts])
|
|
46
69
|
end
|
|
70
|
+
|
|
47
71
|
end
|
|
72
|
+
|
|
48
73
|
end
|
|
@@ -124,6 +124,12 @@ module OMQ
|
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
+
# Single-connection fast path: skip Enumerator#next entirely.
|
|
128
|
+
# The vast majority of sockets have exactly one peer.
|
|
129
|
+
if @queues.size == 1
|
|
130
|
+
return @queues.first.dequeue(timeout: 0)
|
|
131
|
+
end
|
|
132
|
+
|
|
127
133
|
@queues.size.times do
|
|
128
134
|
q = begin
|
|
129
135
|
@cycle.next
|
data/lib/omq/routing/pair.rb
CHANGED
|
@@ -158,7 +158,7 @@ module OMQ
|
|
|
158
158
|
|
|
159
159
|
|
|
160
160
|
def drain_send_queue_capped(batch)
|
|
161
|
-
bytes = batch_bytes(batch
|
|
161
|
+
bytes = batch_bytes(batch.first)
|
|
162
162
|
while batch.size < BATCH_MSG_CAP && bytes < BATCH_BYTE_CAP
|
|
163
163
|
msg = @send_queue.dequeue(timeout: 0)
|
|
164
164
|
break unless msg
|
|
@@ -174,13 +174,25 @@ module OMQ
|
|
|
174
174
|
# fairness cap rather than crashing on #bytesize.
|
|
175
175
|
#
|
|
176
176
|
def batch_bytes(parts)
|
|
177
|
-
parts.
|
|
177
|
+
if parts.size == 1
|
|
178
|
+
p = parts.first
|
|
179
|
+
p.respond_to?(:bytesize) ? p.bytesize : 0
|
|
180
|
+
else
|
|
181
|
+
total = 0
|
|
182
|
+
i, n = 0, parts.size
|
|
183
|
+
while i < n
|
|
184
|
+
p = parts[i]
|
|
185
|
+
total += p.bytesize if p.respond_to?(:bytesize)
|
|
186
|
+
i += 1
|
|
187
|
+
end
|
|
188
|
+
total
|
|
189
|
+
end
|
|
178
190
|
end
|
|
179
191
|
|
|
180
192
|
|
|
181
193
|
def write_batch(conn, batch)
|
|
182
194
|
if batch.size == 1
|
|
183
|
-
conn.send_message(transform_send(batch
|
|
195
|
+
conn.send_message(transform_send(batch.first))
|
|
184
196
|
else
|
|
185
197
|
conn.write_messages(batch)
|
|
186
198
|
conn.flush
|
data/lib/omq/socket.rb
CHANGED
|
@@ -73,9 +73,28 @@ module OMQ
|
|
|
73
73
|
|
|
74
74
|
# @param endpoints [String, nil] optional endpoint with prefix convention
|
|
75
75
|
# (+@+ for bind, +>+ for connect, plain uses subclass default)
|
|
76
|
-
# @param linger [Integer] linger period in seconds (default 0)
|
|
77
76
|
#
|
|
78
|
-
|
|
77
|
+
# @yieldparam [self] the socket, when a block is passed; the socket
|
|
78
|
+
# is {#close}d when the block returns (or raises).
|
|
79
|
+
#
|
|
80
|
+
# Use option accessors (e.g. +socket.linger = 0+) to configure
|
|
81
|
+
# post-construction.
|
|
82
|
+
#
|
|
83
|
+
def initialize(endpoints = nil)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Yields +self+ if a block was given, then closes. Called by every
|
|
88
|
+
# subclass initializer so +OMQ::PUSH.new { |p| ... }+ behaves like
|
|
89
|
+
# +File.open+: configure, use, auto-close.
|
|
90
|
+
#
|
|
91
|
+
def finalize_init
|
|
92
|
+
return unless block_given?
|
|
93
|
+
begin
|
|
94
|
+
yield self
|
|
95
|
+
ensure
|
|
96
|
+
close
|
|
97
|
+
end
|
|
79
98
|
end
|
|
80
99
|
|
|
81
100
|
|
|
@@ -294,12 +313,11 @@ module OMQ
|
|
|
294
313
|
# subclass initializers (including out-of-tree socket types).
|
|
295
314
|
#
|
|
296
315
|
# @param socket_type [Symbol]
|
|
297
|
-
# @param linger [Integer]
|
|
298
316
|
#
|
|
299
|
-
def init_engine(socket_type,
|
|
317
|
+
def init_engine(socket_type, send_hwm: nil, recv_hwm: nil,
|
|
300
318
|
send_timeout: nil, recv_timeout: nil, conflate: false,
|
|
301
319
|
on_mute: nil, backend: nil)
|
|
302
|
-
@options = Options.new
|
|
320
|
+
@options = Options.new
|
|
303
321
|
@options.send_hwm = send_hwm if send_hwm
|
|
304
322
|
@options.recv_hwm = recv_hwm if recv_hwm
|
|
305
323
|
@options.send_timeout = send_timeout if send_timeout
|
data/lib/omq/version.rb
CHANGED