nnq 0.6.1 → 0.8.1

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.
@@ -48,6 +48,16 @@ module NNQ
48
48
  end
49
49
 
50
50
 
51
+ # Inproc fast-path hook.
52
+ def direct_recv_for(conn)
53
+ transform = lambda do |wire_bytes|
54
+ header, payload = parse_backtrace(wire_bytes)
55
+ header ? [conn, header, payload] : nil
56
+ end
57
+ [@recv_queue, transform]
58
+ end
59
+
60
+
51
61
  def close
52
62
  @recv_queue.enqueue(nil)
53
63
  end
@@ -71,6 +71,16 @@ module NNQ
71
71
  end
72
72
 
73
73
 
74
+ # Inproc fast-path hook.
75
+ def direct_recv_for(conn)
76
+ transform = lambda do |body|
77
+ btrace, payload = parse_backtrace(body)
78
+ btrace ? [conn, btrace, payload] : nil
79
+ end
80
+ [@recv_queue, transform]
81
+ end
82
+
83
+
74
84
  def connection_removed(conn)
75
85
  @mutex.synchronize do
76
86
  @pending = nil if @pending && @pending[0] == conn
@@ -47,6 +47,16 @@ module NNQ
47
47
  end
48
48
 
49
49
 
50
+ # Inproc fast-path hook.
51
+ def direct_recv_for(conn)
52
+ transform = lambda do |wire_bytes|
53
+ header, payload = parse_backtrace(wire_bytes)
54
+ header ? [conn, header, payload] : nil
55
+ end
56
+ [@recv_queue, transform]
57
+ end
58
+
59
+
50
60
  def close
51
61
  @recv_queue.enqueue(nil)
52
62
  end
@@ -38,6 +38,13 @@ module NNQ
38
38
  end
39
39
 
40
40
 
41
+ # Inproc fast-path hook: filter via the subscription list in the
42
+ # transform, then enqueue only matching bodies.
43
+ def direct_recv_for(_conn)
44
+ [@queue, ->(body) { matches?(body) ? body : nil }]
45
+ end
46
+
47
+
41
48
  # @return [String, nil]
42
49
  def receive
43
50
  @queue.dequeue
@@ -24,7 +24,6 @@ module NNQ
24
24
  def initialize(engine)
25
25
  @engine = engine
26
26
  @queues = {} # conn => Async::LimitedQueue
27
- @pump_tasks = {} # conn => Async::Task
28
27
  @recv_queue = Async::Queue.new
29
28
  @current_id = nil
30
29
  @mutex = Mutex.new
@@ -68,7 +67,7 @@ module NNQ
68
67
  return if body.bytesize < 4
69
68
 
70
69
  id = body.unpack1("N")
71
- payload = body.byteslice(4..)
70
+ payload = body.byteslice(4..).freeze
72
71
 
73
72
  @mutex.synchronize do
74
73
  return unless @current_id == id
@@ -78,6 +77,21 @@ module NNQ
78
77
  end
79
78
 
80
79
 
80
+ # Inproc fast-path hook. Transform filters replies by current
81
+ # survey id and strips the 4-byte header, mirroring #enqueue.
82
+ def direct_recv_for(_conn)
83
+ mutex = @mutex
84
+ transform = lambda do |body|
85
+ next nil if body.bytesize < 4
86
+ id = body.unpack1("N")
87
+ payload = body.byteslice(4..).freeze
88
+ match = mutex.synchronize { @current_id == id }
89
+ match ? payload : nil
90
+ end
91
+ [@recv_queue, transform]
92
+ end
93
+
94
+
81
95
  # Strips the 4-byte survey id for verbose trace previews.
82
96
  def preview_body(wire)
83
97
  wire.byteslice(4..) || wire
@@ -85,21 +99,14 @@ module NNQ
85
99
 
86
100
 
87
101
  def connection_added(conn)
88
- queue = Async::LimitedQueue.new(@engine.options.send_hwm)
89
- @queues[conn] = queue
90
- @pump_tasks[conn] = spawn_pump(conn, queue)
102
+ queue = Async::LimitedQueue.new(@engine.options.send_hwm)
103
+ @queues[conn] = queue
104
+ spawn_pump(conn, queue)
91
105
  end
92
106
 
93
107
 
94
108
  def connection_removed(conn)
95
109
  @queues.delete(conn)
96
- task = @pump_tasks.delete(conn)
97
-
98
- return unless task
99
- return if task == Async::Task.current
100
-
101
- task.stop
102
- rescue IOError, Errno::EPIPE
103
110
  end
104
111
 
105
112
 
@@ -109,8 +116,6 @@ module NNQ
109
116
 
110
117
 
111
118
  def close
112
- @pump_tasks.each_value(&:stop)
113
- @pump_tasks.clear
114
119
  @queues.clear
115
120
  @recv_queue.enqueue(nil)
116
121
  end
@@ -23,7 +23,6 @@ module NNQ
23
23
  def initialize(engine)
24
24
  @engine = engine
25
25
  @queues = {} # conn => Async::LimitedQueue
26
- @pump_tasks = {} # conn => Async::Task
27
26
  @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
28
27
  end
29
28
 
@@ -47,6 +46,16 @@ module NNQ
47
46
  end
48
47
 
49
48
 
49
+ # Inproc fast-path hook.
50
+ def direct_recv_for(conn)
51
+ transform = lambda do |wire_bytes|
52
+ header, payload = parse_backtrace(wire_bytes)
53
+ header ? [conn, header, payload] : nil
54
+ end
55
+ [@recv_queue, transform]
56
+ end
57
+
58
+
50
59
  def preview_body(wire)
51
60
  _, payload = parse_backtrace(wire)
52
61
  payload || wire
@@ -54,21 +63,14 @@ module NNQ
54
63
 
55
64
 
56
65
  def connection_added(conn)
57
- queue = Async::LimitedQueue.new(@engine.options.send_hwm)
58
- @queues[conn] = queue
59
- @pump_tasks[conn] = spawn_pump(conn, queue)
66
+ queue = Async::LimitedQueue.new(@engine.options.send_hwm)
67
+ @queues[conn] = queue
68
+ spawn_pump(conn, queue)
60
69
  end
61
70
 
62
71
 
63
72
  def connection_removed(conn)
64
73
  @queues.delete(conn)
65
- task = @pump_tasks.delete(conn)
66
-
67
- return unless task
68
- return if task == Async::Task.current
69
-
70
- task.stop
71
- rescue IOError, Errno::EPIPE
72
74
  end
73
75
 
74
76
 
@@ -78,8 +80,6 @@ module NNQ
78
80
 
79
81
 
80
82
  def close
81
- @pump_tasks.each_value(&:stop)
82
- @pump_tasks.clear
83
83
  @queues.clear
84
84
  @recv_queue.enqueue(nil)
85
85
  end
data/lib/nnq/socket.rb CHANGED
@@ -2,11 +2,6 @@
2
2
 
3
3
  require "async/queue"
4
4
 
5
- require_relative "options"
6
- require_relative "engine"
7
- require_relative "monitor_event"
8
- require_relative "reactor"
9
-
10
5
  module NNQ
11
6
  # Socket base class. Subclasses (PUSH, PULL, ...) wire up a routing
12
7
  # strategy and the SP protocol id.
@@ -50,15 +45,15 @@ module NNQ
50
45
  end
51
46
 
52
47
 
53
- def bind(endpoint)
48
+ def bind(endpoint, **opts)
54
49
  ensure_parent_task
55
- Reactor.run { @engine.bind(endpoint) }
50
+ Reactor.run { @engine.bind(endpoint, **opts) }
56
51
  end
57
52
 
58
53
 
59
- def connect(endpoint)
54
+ def connect(endpoint, **opts)
60
55
  ensure_parent_task
61
- Reactor.run { @engine.connect(endpoint) }
56
+ Reactor.run { @engine.connect(endpoint, **opts) }
62
57
  end
63
58
 
64
59
 
@@ -140,16 +135,21 @@ module NNQ
140
135
  end
141
136
 
142
137
 
143
- # Coerces +body+ to a frozen binary string. Called by every send
144
- # method so a caller can't mutate the string after it's been
145
- # enqueued (the body sits in a send queue or per-peer queue until
146
- # the pump writes it, and an unfrozen caller-owned buffer could be
147
- # appended to mid-flight).
138
+ # Coerces +body+ to a frozen `Encoding::BINARY`-tagged String and
139
+ # returns it. Every send method runs its body through this so the
140
+ # receiver sees a uniform frozen+BINARY contract across transports
141
+ # (mutation bugs raise `FrozenError` instead of silently corrupting
142
+ # a shared reference on the inproc fast path).
148
143
  #
149
- # Fast-path: already frozen + binary returned as-is.
150
- def frozen_binary(body)
151
- return body if body.frozen? && body.encoding == Encoding::BINARY
152
- body.b.freeze
144
+ # Fast-path: unfrozen non-BINARY strings are re-tagged in place
145
+ # (force_encoding is a flag flip, no copy). The pathological case
146
+ # of a frozen non-BINARY body (e.g. a `# frozen_string_literal: true`
147
+ # literal) can't be re-tagged in place — the inproc {Pipe} handles
148
+ # that with a copy so the receive contract stays uniform.
149
+ def coerce_binary(body)
150
+ body = body.to_str unless body.is_a?(String)
151
+ body.force_encoding(Encoding::BINARY) unless body.frozen? || body.encoding == Encoding::BINARY
152
+ body.freeze
153
153
  end
154
154
 
155
155
 
@@ -16,7 +16,7 @@ module NNQ
16
16
  # Cooked: broadcasts +body+ as a survey to all connected respondents.
17
17
  def send_survey(body)
18
18
  raise Error, "SURVEYOR#send_survey not available in raw mode" if raw?
19
- body = frozen_binary(body)
19
+ body = coerce_binary(body)
20
20
  Reactor.run { @engine.routing.send_survey(body) }
21
21
  end
22
22
 
@@ -26,7 +26,7 @@ module NNQ
26
26
  # protocol-sp header kwarg — no concat). Raises in cooked mode.
27
27
  def send(body, header:)
28
28
  raise Error, "SURVEYOR#send not available in cooked mode" unless raw?
29
- body = frozen_binary(body)
29
+ body = coerce_binary(body)
30
30
  Reactor.run { @engine.routing.send(body, header: header) }
31
31
  end
32
32
 
@@ -70,7 +70,7 @@ module NNQ
70
70
  # recent survey. Raises in raw mode.
71
71
  def send_reply(body)
72
72
  raise Error, "RESPONDENT#send_reply not available in raw mode" if raw?
73
- body = frozen_binary(body)
73
+ body = coerce_binary(body)
74
74
  Reactor.run { @engine.routing.send_reply(body) }
75
75
  end
76
76
 
@@ -78,7 +78,7 @@ module NNQ
78
78
  # Raw: writes +body+ with +header+ back to +to+. Raises in cooked mode.
79
79
  def send(body, to:, header:)
80
80
  raise Error, "RESPONDENT#send not available in cooked mode" unless raw?
81
- body = frozen_binary(body)
81
+ body = coerce_binary(body)
82
82
  Reactor.run { @engine.routing.send(body, to: to, header: header) }
83
83
  end
84
84
 
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNQ
4
+ module Transport
5
+ module Inproc
6
+ # Queue-based in-process pipe. Duck-types {NNQ::Connection} so
7
+ # routing strategies, the recv loop, and the send pump work
8
+ # against it unchanged.
9
+ #
10
+ # No wire framing: bodies are transferred as frozen Strings
11
+ # through a pair of {Async::Queue} (one per direction). When an
12
+ # SP backtrace header is supplied (REQ/REP/SURVEYOR paths), it's
13
+ # prepended before enqueue so {#receive_message} returns an
14
+ # already-prefixed body — matching the TCP/IPC framing semantic
15
+ # so routing's `parse_backtrace` parses the same layout either
16
+ # way.
17
+ #
18
+ # Direct-recv fast path: when a routing strategy calls
19
+ # {#wire_direct_recv} on the peer side of a pipe pair, subsequent
20
+ # {#send_message} calls enqueue straight into the consumer's
21
+ # recv queue — the intermediate pipe queue and the recv pump
22
+ # fiber are both skipped. Cuts three fiber hops to one and is
23
+ # what lets inproc PUSH/PULL clear 1M msg/s on YJIT.
24
+ #
25
+ # Wiring happens synchronously inside {Transport::Inproc.connect}
26
+ # (before the call returns to the caller), so there's no window
27
+ # in which a send can precede a wire — no pending buffer needed.
28
+ #
29
+ # Close protocol: {#close} enqueues a `nil` sentinel onto the
30
+ # send side (or the direct queue if wired). The peer's recv loop
31
+ # sees `nil`, raises `EOFError`, and unwinds via its connection
32
+ # supervisor.
33
+ class Pipe
34
+ # @return [String, nil] endpoint URI this pipe was established on
35
+ attr_reader :endpoint
36
+
37
+ # @return [Pipe, nil] the other end of the pair
38
+ attr_accessor :peer
39
+
40
+ # @return [Async::Queue, nil] when non-nil, {#send_message}
41
+ # enqueues here instead of into @send_queue.
42
+ attr_reader :direct_recv_queue
43
+
44
+
45
+ def initialize(send_queue:, recv_queue:, endpoint:)
46
+ @send_queue = send_queue
47
+ @recv_queue = recv_queue
48
+ @endpoint = endpoint
49
+ @closed = false
50
+ @peer = nil
51
+ @direct_recv_queue = nil
52
+ @direct_recv_transform = nil
53
+ end
54
+
55
+
56
+ # Wires the direct-recv fast path. After this call, messages
57
+ # sent on this pipe bypass the intermediate pipe queue and
58
+ # land directly in +queue+.
59
+ #
60
+ # @param queue [Async::Queue]
61
+ # @param transform [Proc, nil] optional per-message transform;
62
+ # return nil to drop the message (used by filter/parse
63
+ # strategies like SUB or REP).
64
+ def wire_direct_recv(queue, transform)
65
+ @direct_recv_transform = transform
66
+ @direct_recv_queue = queue
67
+ end
68
+
69
+
70
+ def send_message(body, header: nil)
71
+ raise ClosedError, "connection closed" if @closed
72
+
73
+ # Socket#coerce_binary tags mutable bodies BINARY in place;
74
+ # the pathological case of a frozen non-BINARY body (e.g. a
75
+ # `# frozen_string_literal: true` literal) can't be re-tagged
76
+ # in place, so copy it here to keep the receiver contract
77
+ # uniform with TCP/IPC.
78
+ body = body.b.freeze if body.encoding != Encoding::BINARY
79
+ wire = header ? (header + body).freeze : body
80
+
81
+ if (q = @direct_recv_queue)
82
+ item = @direct_recv_transform ? @direct_recv_transform.call(wire) : wire
83
+ q.enqueue(item) unless item.nil?
84
+ else
85
+ @send_queue.enqueue(wire)
86
+ end
87
+ end
88
+
89
+
90
+ alias write_message send_message
91
+
92
+
93
+ def write_messages(bodies)
94
+ raise ClosedError, "connection closed" if @closed
95
+
96
+ if bodies.any? { |b| b.encoding != Encoding::BINARY }
97
+ bodies = bodies.map { |b| b.encoding == Encoding::BINARY ? b : b.b.freeze }
98
+ end
99
+
100
+ if (q = @direct_recv_queue)
101
+ transform = @direct_recv_transform
102
+ bodies.each do |body|
103
+ item = transform ? transform.call(body) : body
104
+ q.enqueue(item) unless item.nil?
105
+ end
106
+ else
107
+ bodies.each { |body| @send_queue.enqueue(body) }
108
+ end
109
+ end
110
+
111
+
112
+ # No-op — Async::Queue has no IO buffer to flush.
113
+ def flush
114
+ nil
115
+ end
116
+
117
+
118
+ def receive_message
119
+ item = @recv_queue.dequeue
120
+ raise EOFError, "connection closed" if item.nil?
121
+ item
122
+ end
123
+
124
+
125
+ def closed?
126
+ @closed
127
+ end
128
+
129
+
130
+ def close
131
+ return if @closed
132
+ @closed = true
133
+ # Close sentinel goes on whichever queue the peer is reading.
134
+ # When direct-wired, @send_queue is unused; hit the direct
135
+ # queue so the consumer unblocks.
136
+ (@direct_recv_queue || @send_queue).enqueue(nil)
137
+ end
138
+
139
+ end
140
+ end
141
+ end
142
+ end
@@ -1,21 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "socket"
4
- require "io/stream"
3
+ require "async/queue"
4
+
5
+ require_relative "inproc/pipe"
5
6
 
6
7
  module NNQ
7
8
  module Transport
8
9
  # In-process transport. Both peers live in the same process and
9
- # exchange frames over a Unix socketpair — no network, no address.
10
+ # exchange frozen Strings through a pair of {Async::Queue}s — no
11
+ # wire framing, no socketpair, no SP handshake.
10
12
  #
11
- # Unlike omq's DirectPipe, inproc here still runs through
12
- # Protocol::SP: the socketpair just replaces TCP. Kernel buffering
13
- # across the pair is plenty to avoid contention for typical
14
- # in-process message sizes, and reusing the SP handshake + framing
15
- # keeps the transport ~40 LOC instead of a parallel Connection
16
- # implementation.
13
+ # The historical implementation ran through a Unix `socketpair(2)`
14
+ # and the full SP protocol, making inproc roughly as expensive as
15
+ # IPC. Swapping to {Inproc::Pipe} (duck-types {NNQ::Connection})
16
+ # drops the kernel buffer copy, the framing encode/decode, and the
17
+ # handshake inproc becomes a pure in-process queue transfer.
17
18
  #
18
19
  module Inproc
20
+ Engine.transports["inproc"] = self
21
+
19
22
  @registry = {}
20
23
  @mutex = Mutex.new
21
24
 
@@ -26,7 +29,7 @@ module NNQ
26
29
  # @param endpoint [String] e.g. "inproc://my-endpoint"
27
30
  # @param engine [Engine]
28
31
  # @return [Listener]
29
- def bind(endpoint, engine)
32
+ def bind(endpoint, engine, **)
30
33
  @mutex.synchronize do
31
34
  raise Error, "inproc endpoint already bound: #{endpoint}" if @registry.key?(endpoint)
32
35
  @registry[endpoint] = engine
@@ -36,28 +39,27 @@ module NNQ
36
39
  end
37
40
 
38
41
 
39
- # Connects +engine+ to a bound inproc endpoint. Creates a Unix
40
- # socketpair, hands one side to the bound engine (accepted),
41
- # the other to the connecting engine (connected). Both sides
42
- # run the normal SP handshake concurrently.
42
+ # Connects +engine+ to a bound inproc endpoint. Creates a Pipe
43
+ # pair one queue per direction and registers each side with
44
+ # its owning engine via {Engine#connection_ready}. No handshake
45
+ # runs; both ends are live as soon as the pipes are wired.
43
46
  #
44
47
  # @param endpoint [String]
45
48
  # @param engine [Engine]
46
49
  # @return [void]
47
- def connect(endpoint, engine)
50
+ def connect(endpoint, engine, **)
48
51
  bound = @mutex.synchronize { @registry[endpoint] }
49
52
  raise Error, "inproc endpoint not bound: #{endpoint}" unless bound
50
53
 
51
- a, b = UNIXSocket.pair
54
+ a_to_b = Async::Queue.new
55
+ b_to_a = Async::Queue.new
56
+ client = Pipe.new(send_queue: a_to_b, recv_queue: b_to_a, endpoint: endpoint)
57
+ server = Pipe.new(send_queue: b_to_a, recv_queue: a_to_b, endpoint: endpoint)
58
+ client.peer = server
59
+ server.peer = client
52
60
 
53
- # Handshake on the bound side must run concurrently with
54
- # ours — if we called bound.handle_accepted synchronously
55
- # it would block on reading our greeting before we've had
56
- # a chance to write it.
57
- bound.spawn_task(annotation: "nnq inproc accept #{endpoint}") do
58
- bound.handle_accepted(IO::Stream::Buffered.wrap(b), endpoint: endpoint)
59
- end
60
- engine.handle_connected(IO::Stream::Buffered.wrap(a), endpoint: endpoint)
61
+ bound.connection_ready(server, endpoint: endpoint)
62
+ engine.connection_ready(client, endpoint: endpoint)
61
63
  end
62
64
 
63
65
 
@@ -84,7 +86,7 @@ module NNQ
84
86
  end
85
87
 
86
88
 
87
- # No accept loop: inproc connects synchronously.
89
+ # No accept loop: inproc connects are fully synchronous.
88
90
  def start_accept_loop(_parent_task, &_on_accepted)
89
91
  end
90
92
 
@@ -13,13 +13,16 @@ module NNQ
13
13
  # verbatim.
14
14
  #
15
15
  module IPC
16
+ Engine.transports["ipc"] = self
17
+
18
+
16
19
  class << self
17
20
  # Binds an IPC server.
18
21
  #
19
22
  # @param endpoint [String] e.g. "ipc:///tmp/nnq.sock" or "ipc://@abstract"
20
23
  # @param engine [Engine]
21
24
  # @return [Listener]
22
- def bind(endpoint, engine)
25
+ def bind(endpoint, engine, **)
23
26
  path = parse_path(endpoint)
24
27
  sock_path = to_socket_path(path)
25
28
 
@@ -35,7 +38,7 @@ module NNQ
35
38
  # @param endpoint [String]
36
39
  # @param engine [Engine]
37
40
  # @return [void]
38
- def connect(endpoint, engine)
41
+ def connect(endpoint, engine, **)
39
42
  path = parse_path(endpoint)
40
43
  sock_path = to_socket_path(path)
41
44
  sock = UNIXSocket.new(sock_path)
@@ -11,13 +11,16 @@ module NNQ
11
11
  # accept inside an Async fiber.
12
12
  #
13
13
  module TCP
14
+ Engine.transports["tcp"] = self
15
+
16
+
14
17
  class << self
15
18
  # Binds a TCP server to +endpoint+.
16
19
  #
17
20
  # @param endpoint [String] e.g. "tcp://127.0.0.1:5570" or "tcp://127.0.0.1:0"
18
21
  # @param engine [Engine]
19
22
  # @return [Listener]
20
- def bind(endpoint, engine)
23
+ def bind(endpoint, engine, **)
21
24
  host, port = parse_endpoint(endpoint)
22
25
  host = "0.0.0.0" if host == "*"
23
26
  server = TCPServer.new(host, port)
@@ -34,7 +37,7 @@ module NNQ
34
37
  # @param endpoint [String]
35
38
  # @param engine [Engine]
36
39
  # @return [void]
37
- def connect(endpoint, engine)
40
+ def connect(endpoint, engine, **)
38
41
  host, port = parse_endpoint(endpoint)
39
42
  sock = ::Socket.tcp(host, port, connect_timeout: connect_timeout(engine.options))
40
43
 
data/lib/nnq/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NNQ
4
- VERSION = "0.6.1"
4
+ VERSION = "0.8.1"
5
5
  end
data/lib/nnq.rb CHANGED
@@ -1,23 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "protocol/sp"
4
+ require "io/stream"
4
5
 
5
- module NNQ
6
- # Freezes module-level state so NNQ sockets can be used inside Ractors.
7
- # Call this once before spawning any Ractors that create NNQ sockets.
8
- #
9
- def self.freeze_for_ractors!
10
- Engine::CONNECTION_FAILED.freeze
11
- Engine::CONNECTION_LOST.freeze
12
- Engine::TRANSPORTS.freeze
13
- end
14
- end
15
6
 
7
+ # Core
16
8
  require_relative "nnq/version"
17
- require_relative "nnq/error"
9
+ require_relative "nnq/constants"
10
+ require_relative "nnq/reactor"
18
11
  require_relative "nnq/options"
12
+ require_relative "nnq/error"
19
13
  require_relative "nnq/connection"
20
14
  require_relative "nnq/engine"
15
+
16
+ # Transport
17
+ require_relative "nnq/transport/inproc"
18
+ require_relative "nnq/transport/tcp"
19
+ require_relative "nnq/transport/ipc"
20
+
21
+ # Socket types
21
22
  require_relative "nnq/socket"
22
23
  require_relative "nnq/push_pull"
23
24
  require_relative "nnq/pair"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nnq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -41,16 +41,16 @@ dependencies:
41
41
  name: protocol-sp
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.3'
46
+ version: '0.4'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">="
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0.3'
53
+ version: '0.4'
54
54
  description: Pure Ruby implementation of nanomsg's Scalability Protocols (SP) on top
55
55
  of async + io-stream. Per-socket HWM, opportunistic batching, wire-compatible with
56
56
  libnng over inproc/ipc/tcp.
@@ -66,12 +66,12 @@ files:
66
66
  - lib/nnq.rb
67
67
  - lib/nnq/bus.rb
68
68
  - lib/nnq/connection.rb
69
+ - lib/nnq/constants.rb
69
70
  - lib/nnq/engine.rb
70
71
  - lib/nnq/engine/connection_lifecycle.rb
71
72
  - lib/nnq/engine/reconnect.rb
72
73
  - lib/nnq/engine/socket_lifecycle.rb
73
74
  - lib/nnq/error.rb
74
- - lib/nnq/monitor_event.rb
75
75
  - lib/nnq/options.rb
76
76
  - lib/nnq/pair.rb
77
77
  - lib/nnq/pub_sub.rb
@@ -97,6 +97,7 @@ files:
97
97
  - lib/nnq/socket.rb
98
98
  - lib/nnq/surveyor_respondent.rb
99
99
  - lib/nnq/transport/inproc.rb
100
+ - lib/nnq/transport/inproc/pipe.rb
100
101
  - lib/nnq/transport/ipc.rb
101
102
  - lib/nnq/transport/tcp.rb
102
103
  - lib/nnq/version.rb