legion-transport 1.2.8 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 675cb607fc415c57ee0a7ed48d60caea0fc951a6ce1ac8a092e3f10bc40b229e
4
- data.tar.gz: 2e9f5410fca189ba407efebafc6b716289cf2890f914fefe456cb0cea5332fd8
3
+ metadata.gz: 131a06c178704a57af2c9baef440704001342842e02d15a090297c09572505b5
4
+ data.tar.gz: e30dd4bdaac3fdfb42cf588f9512f3a3b930ae840b79f34ea073a960276e7fbb
5
5
  SHA512:
6
- metadata.gz: c6610f64e0958da4ec59124c697c5ec611caabb3d6c08e296518d683c7212135e483d217c66a53b06ed38e6a9b03db3546f0efa7e18c71ca27d8dd35ea1249eb
7
- data.tar.gz: ecbcc22eca4559cf40169501810798b02dcb5e2302ff97da1266db9a390fa4d7247b79e74fcad49e972a496a19fb0b7ccfb6aac4403db01ccd414f1055f56cb8
6
+ metadata.gz: 89b5e05ec97fad24f0e191ac67bf4362afe758d1d4b1481629b6fa5575355e0ec56fa793d78d0f2cf3111213bf1087865d6c3791c234e41884adf3d13bf28568
7
+ data.tar.gz: 41bff2584db88fd03cc904bbe2996c628d4fdf689f6b793557f6207efb31fa808ad95e9e0aaa350026760d06c95e0411c62e75c9ca6087f21e6d1dd5a3f99acb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Legion::Transport ChangeLog
2
2
 
3
+ ## [1.3.2] - 2026-03-21
4
+
5
+ ### Added
6
+ - `Legion::Transport::Exchanges::Logging`: topic exchange (`legion.logging`) for structured log event publishing
7
+ - `Legion::Transport::Queues::RegionOutbound`: durable outbound queues for cross-region message routing (per-peer, skips current region)
8
+ - `Legion::Transport::Messages::RegionReRoute`: re-route message type for forwarding tasks to target regions
9
+
10
+ ## [1.3.0] - 2026-03-21
11
+
12
+ ### Added
13
+ - RabbitMQ cluster support: `cluster_nodes`, `connection_pool_size`, `region`, `management_port`, `quorum_queue_policy` settings
14
+ - Cluster node rotation: `cluster_nodes` merged into `resolved_hosts` with shuffle for load distribution
15
+ - Connection pool (`Helpers::Pool`): mutex-protected pool of Bunny sessions with configurable size and timeout
16
+ - Channel pool (`Helpers::ChannelPool`): per-connection ring buffer of channels with borrow/return
17
+ - Region header injection: `x-legion-region` and `x-legion-region-affinity` headers on published messages when region is configured
18
+ - Quorum queue policy helper (`Helpers::Policy`): idempotent HTTP PUT to RabbitMQ Management API, opt-in via `quorum_queue_policy.enabled`
19
+ - Connection failover: retry loop across all cluster nodes on TCPConnectionFailed/AuthFailure/ECONNREFUSED
20
+ - `Legion::Transport::PoolTimeout` and `Legion::Transport::ClusterUnavailable` error classes
21
+
22
+ ### Changed
23
+ - `connection_timeout` default 1 -> 10, `network_recovery_interval` default 1 -> 2
24
+ - `build_bunny_opts` now merges `cluster_nodes` into resolved hosts before building Bunny options
25
+
26
+ ## [1.2.9] - 2026-03-21
27
+
28
+ ### Changed
29
+ - `Connection::SSL` module refactored to use `Legion::Crypt::TLS.resolve` for TLS configuration
30
+ - Removed legacy `use_tls?`, `tls_cert`, `tls_key`, `ca_certs`, `verify_peer?` methods
31
+ - TLS options now merged into Bunny connection opts via `tls_options` method
32
+ - SSL module auto-required and included in `Connection` class
33
+
3
34
  ## [1.2.8] - 2026-03-21
4
35
 
5
36
  ### Added
data/CLAUDE.md CHANGED
@@ -25,7 +25,8 @@ Legion::Transport
25
25
  │ ├── Agent # Agent communication exchange (identity-bound: GAIA frames, preferences, proactive)
26
26
  │ ├── Crypt # Encryption exchange
27
27
  │ ├── Extensions # Extension exchange
28
- └── Lex # LEX exchange (inherits Extensions)
28
+ ├── Lex # LEX exchange (inherits Extensions)
29
+ │ └── Logging # Log event exchange (legion.logging) for structured log event publishing
29
30
  ├── Queue # Base queue class (extends Bunny::Queue)
30
31
  │ └── Queues/
31
32
  │ ├── Node # Node queue
data/CODEOWNERS ADDED
@@ -0,0 +1 @@
1
+ * @Esity
@@ -4,32 +4,40 @@ module Legion
4
4
  module Transport
5
5
  module Connection
6
6
  module SSL
7
- def settings
8
- Legion::Settings[:transport][:tls] || {}
9
- end
7
+ def tls_options(tls_config: nil, port: nil)
8
+ return {} unless defined?(Legion::Crypt::TLS)
10
9
 
11
- def use_vault_pki?
12
- settings[:use_vault_pki] && Legion::Settings[:crypt][:vault][:connected]
13
- end
10
+ tls_config ||= tls_settings
11
+ port ||= transport_port
14
12
 
15
- def use_tls?
16
- settings[:use_tls] || Legion::Settings[:transport][:connection][:port].to_i == 5671
17
- end
13
+ tls = Legion::Crypt::TLS.resolve(tls_config, port: port)
14
+ return {} unless tls[:enabled]
18
15
 
19
- def tls_cert
20
- settings[:tls_cert]
16
+ {
17
+ tls: true,
18
+ tls_cert: tls[:cert],
19
+ tls_key: tls[:key],
20
+ tls_ca_certificates: [tls[:ca]].compact,
21
+ verify_peer: tls[:verify] != :none
22
+ }
21
23
  end
22
24
 
23
- def tls_key
24
- settings[:tls_key]
25
- end
25
+ private
26
+
27
+ def tls_settings
28
+ return {} unless defined?(Legion::Settings)
26
29
 
27
- def ca_certs
28
- settings[:ca_certs]
30
+ Legion::Settings[:transport][:tls] || {}
31
+ rescue StandardError
32
+ {}
29
33
  end
30
34
 
31
- def verify_peer?
32
- settings[:verify_peer] || false
35
+ def transport_port
36
+ return nil unless defined?(Legion::Settings)
37
+
38
+ Legion::Settings[:transport][:connection][:port]
39
+ rescue StandardError
40
+ nil
33
41
  end
34
42
  end
35
43
  end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'concurrent-ruby'
4
+ require_relative 'connection/ssl'
4
5
 
5
6
  module Legion
6
7
  module Transport
7
8
  module Connection
8
9
  class << self
10
+ include Legion::Transport::Connection::SSL
11
+
9
12
  def settings
10
13
  Legion::Settings[:transport]
11
14
  end
@@ -33,7 +36,7 @@ module Legion
33
36
  nil
34
37
  else
35
38
  @session ||= Concurrent::AtomicReference.new(
36
- connector.new(build_bunny_opts(connection_name: connection_name))
39
+ create_session_with_failover(connection_name: connection_name)
37
40
  )
38
41
  @channel_thread = Concurrent::ThreadLocalVar.new(nil)
39
42
  session.start
@@ -42,20 +45,8 @@ module Legion
42
45
  Legion::Settings[:transport][:connected] = true
43
46
  end
44
47
 
45
- session.on_blocked { Legion::Transport.logger.warn('Legion::Transport is being blocked by RabbitMQ!') } if session.respond_to? :on_blocked
46
-
47
- if session.respond_to? :on_unblocked
48
- session.on_unblocked do
49
- Legion::Transport.logger.info('Legion::Transport is no longer being blocked by RabbitMQ')
50
- end
51
- end
52
-
53
- if session.respond_to? :after_recovery_completed
54
- session.after_recovery_completed do
55
- Legion::Transport.logger.info('Legion::Transport has completed recovery')
56
- end
57
- end
58
-
48
+ register_session_callbacks
49
+ apply_quorum_policy_if_enabled
59
50
  true
60
51
  end
61
52
 
@@ -95,22 +86,76 @@ module Legion
95
86
 
96
87
  private
97
88
 
89
+ def create_session_with_failover(connection_name:)
90
+ opts = build_bunny_opts(connection_name: connection_name)
91
+ hosts = opts[:hosts] || [{ host: opts[:host] || '127.0.0.1', port: opts[:port] || 5672 }]
92
+ last_error = nil
93
+
94
+ hosts.each do |host_entry|
95
+ attempt_opts = opts.dup
96
+ if host_entry.is_a?(Hash)
97
+ attempt_opts[:host] = host_entry[:host]
98
+ attempt_opts[:port] = host_entry[:port]
99
+ end
100
+ attempt_opts.delete(:hosts)
101
+
102
+ return connector.new(attempt_opts)
103
+ rescue Bunny::TCPConnectionFailed, Bunny::PossibleAuthenticationFailureError, Errno::ECONNREFUSED => e
104
+ last_error = e
105
+ host_desc = host_entry.is_a?(Hash) ? "#{host_entry[:host]}:#{host_entry[:port]}" : host_entry
106
+ Legion::Transport.logger.warn("Connection failed to #{host_desc}: #{e.message}")
107
+ end
108
+
109
+ raise Legion::Transport::ClusterUnavailable, "All cluster nodes exhausted: #{last_error&.message}" if defined?(Legion::Transport::ClusterUnavailable)
110
+
111
+ raise last_error || StandardError.new('No cluster nodes available')
112
+ end
113
+
114
+ def register_session_callbacks
115
+ session.on_blocked { Legion::Transport.logger.warn('Legion::Transport is being blocked by RabbitMQ!') } if session.respond_to?(:on_blocked)
116
+
117
+ if session.respond_to?(:on_unblocked)
118
+ session.on_unblocked do
119
+ Legion::Transport.logger.info('Legion::Transport is no longer being blocked by RabbitMQ')
120
+ end
121
+ end
122
+
123
+ return unless session.respond_to?(:after_recovery_completed)
124
+
125
+ session.after_recovery_completed do
126
+ Legion::Transport.logger.info('Legion::Transport has completed recovery')
127
+ end
128
+ end
129
+
130
+ def apply_quorum_policy_if_enabled
131
+ return unless defined?(Legion::Transport::Helpers::Policy)
132
+
133
+ Legion::Transport::Helpers::Policy.apply_quorum_policy!
134
+ rescue StandardError
135
+ nil
136
+ end
137
+
98
138
  def build_bunny_opts(connection_name:)
99
139
  conn_settings = Legion::Settings[:transport][:connection].dup
100
140
  resolved = conn_settings.delete(:resolved_hosts) || []
101
141
 
142
+ cluster_nodes = Array(Legion::Settings[:transport][:cluster_nodes])
143
+ all_hosts = (resolved + cluster_nodes).uniq
144
+ all_hosts.shuffle! if all_hosts.length > 1
145
+
102
146
  opts = conn_settings.merge(
103
147
  connection_name: connection_name,
104
148
  logger: Legion::Transport.logger,
105
149
  log_level: :warn
106
150
  )
107
151
 
108
- if resolved.length > 1
109
- opts[:hosts] = resolved.map { |h| { host: h.split(':').first, port: h.split(':').last.to_i } }
152
+ if all_hosts.length > 1
153
+ opts[:hosts] = all_hosts.map { |h| { host: h.split(':').first, port: h.split(':').last.to_i } }
110
154
  opts.delete(:host)
111
155
  opts.delete(:port)
112
156
  end
113
157
 
158
+ opts.merge!(tls_options)
114
159
  opts
115
160
  end
116
161
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Transport
5
+ class PoolTimeout < StandardError; end
6
+ class ClusterUnavailable < StandardError; end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Transport
5
+ module Exchanges
6
+ class Logging < Legion::Transport::Exchange
7
+ def exchange_name
8
+ 'legion.logging'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Transport
5
+ module Helpers
6
+ class ChannelPool
7
+ def initialize(connection:, size: 10, prefetch: 2)
8
+ @connection = connection
9
+ @size = size
10
+ @prefetch = prefetch
11
+ @available = []
12
+ @in_use = []
13
+ @mutex = Mutex.new
14
+ end
15
+
16
+ def borrow
17
+ @mutex.synchronize do
18
+ purge_closed_unsafe
19
+
20
+ if (ch = @available.pop)
21
+ @in_use << ch
22
+ return ch
23
+ end
24
+
25
+ total = @available.size + @in_use.size
26
+ return nil if total >= @size
27
+
28
+ ch = @connection.create_channel
29
+ ch.prefetch(@prefetch) if ch.respond_to?(:prefetch)
30
+ @in_use << ch
31
+ ch
32
+ end
33
+ end
34
+
35
+ def return(channel)
36
+ @mutex.synchronize do
37
+ @in_use.delete(channel)
38
+ return unless channel.respond_to?(:open?) && channel.open?
39
+ return if (@available.size + @in_use.size) >= @size
40
+
41
+ @available << channel
42
+ end
43
+ end
44
+
45
+ def purge_closed
46
+ @mutex.synchronize { purge_closed_unsafe }
47
+ end
48
+
49
+ def close_all
50
+ @mutex.synchronize do
51
+ (@available + @in_use).each do |ch|
52
+ ch.close rescue nil # rubocop:disable Style/RescueModifier
53
+ end
54
+ @available.clear
55
+ @in_use.clear
56
+ end
57
+ end
58
+
59
+ def size
60
+ @mutex.synchronize { @available.size + @in_use.size }
61
+ end
62
+
63
+ private
64
+
65
+ def purge_closed_unsafe
66
+ @available.reject! { |c| !c.respond_to?(:open?) || !c.open? }
67
+ @in_use.reject! { |c| !c.respond_to?(:open?) || !c.open? }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+
6
+ module Legion
7
+ module Transport
8
+ module Helpers
9
+ module Policy
10
+ module_function
11
+
12
+ def apply_quorum_policy!(settings: nil)
13
+ settings ||= Legion::Settings[:transport]
14
+ policy = settings[:quorum_queue_policy]
15
+ return false unless policy && policy[:enabled]
16
+
17
+ conn = settings[:connection]
18
+ host = conn[:host] || '127.0.0.1'
19
+ port = settings[:management_port] || 15_672
20
+ user = conn[:user] || 'guest'
21
+ pass = conn[:password] || 'guest'
22
+ vhost = conn[:vhost] || '/'
23
+
24
+ encoded_vhost = URI.encode_www_form_component(vhost)
25
+ uri = URI("http://#{host}:#{port}/api/policies/#{encoded_vhost}/legion-quorum")
26
+
27
+ body = {
28
+ pattern: policy[:pattern] || '^legion\\.',
29
+ definition: {
30
+ 'x-queue-type': 'quorum',
31
+ 'x-delivery-limit': policy[:delivery_limit] || 5
32
+ },
33
+ 'apply-to': 'queues',
34
+ priority: 0
35
+ }
36
+
37
+ req = Net::HTTP::Put.new(uri)
38
+ req.basic_auth(user, pass)
39
+ req.content_type = 'application/json'
40
+ req.body = ::JSON.dump(body)
41
+
42
+ http = Net::HTTP.new(uri.host, uri.port)
43
+ http.open_timeout = 5
44
+ http.read_timeout = 5
45
+ response = http.request(req)
46
+
47
+ response.code.start_with?('2')
48
+ rescue StandardError => e
49
+ Legion::Transport.logger.warn("Quorum policy apply failed: #{e.message}") if defined?(Legion::Transport)
50
+ false
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Transport
5
+ module Helpers
6
+ class Pool
7
+ def initialize(size: 1, timeout: 5, &block)
8
+ @size = size
9
+ @timeout = timeout
10
+ @factory = block
11
+ @available = []
12
+ @in_use = []
13
+ @mutex = Mutex.new
14
+ @condition = ConditionVariable.new
15
+ end
16
+
17
+ def checkout
18
+ deadline = Time.now + @timeout
19
+
20
+ @mutex.synchronize do
21
+ loop do
22
+ @available.reject! { |c| c.respond_to?(:closed?) && c.closed? }
23
+
24
+ if (conn = @available.pop)
25
+ @in_use << conn
26
+ return conn
27
+ end
28
+
29
+ total = @available.size + @in_use.size
30
+ if total < @size
31
+ conn = @factory.call
32
+ @in_use << conn
33
+ return conn
34
+ end
35
+
36
+ remaining = deadline - Time.now
37
+ raise Legion::Transport::PoolTimeout, 'timed out waiting for available connection' if remaining <= 0
38
+
39
+ @condition.wait(@mutex, remaining)
40
+ end
41
+ end
42
+ end
43
+
44
+ def checkin(connection)
45
+ @mutex.synchronize do
46
+ @in_use.delete(connection)
47
+ @available << connection if connection.respond_to?(:open?) && connection.open?
48
+ @condition.signal
49
+ end
50
+ end
51
+
52
+ def size
53
+ @mutex.synchronize { @available.size + @in_use.size }
54
+ end
55
+
56
+ def shutdown
57
+ @mutex.synchronize do
58
+ (@available + @in_use).each do |conn|
59
+ conn.close rescue nil # rubocop:disable Style/RescueModifier
60
+ end
61
+ @available.clear
62
+ @in_use.clear
63
+ end
64
+ end
65
+
66
+ def connected?
67
+ @mutex.synchronize do
68
+ (@available + @in_use).any? { |c| c.respond_to?(:open?) && c.open? }
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -124,6 +124,8 @@ module Legion
124
124
  def headers
125
125
  @options[:headers] ||= Concurrent::Hash.new
126
126
  @options[:headers]['legion_protocol_version'] ||= '2.0'
127
+ inject_region_header
128
+ inject_legion_region_header
127
129
  %i[task_id relationship_id trigger_namespace_id trigger_function_id parent_id master_id runner_namespace runner_class namespace_id function_id function
128
130
  chain_id debug].each do |header|
129
131
  next unless @options.key? header
@@ -172,6 +174,26 @@ module Legion
172
174
 
173
175
  private
174
176
 
177
+ def inject_region_header
178
+ region = Legion::Settings[:transport][:region] rescue nil # rubocop:disable Style/RescueModifier
179
+ return if region.nil?
180
+
181
+ @options[:headers]['x-legion-region'] = region
182
+ affinity = @options[:region_affinity] || 'prefer_local'
183
+ @options[:headers]['x-legion-region-affinity'] = affinity
184
+ end
185
+
186
+ def inject_legion_region_header
187
+ return unless defined?(Legion::Region) &&
188
+ Legion::Region.respond_to?(:current) &&
189
+ Legion::Region.current
190
+
191
+ @options[:headers]['region'] = Legion::Region.current
192
+ @options[:headers]['region_affinity'] = @options[:region_affinity] ||
193
+ (defined?(Legion::Settings) && Legion::Settings.dig(:region, :default_affinity)) ||
194
+ 'prefer_local'
195
+ end
196
+
175
197
  def spool_message(error)
176
198
  return unless defined?(Legion::Transport::Spool)
177
199
 
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Transport
5
+ module Messages
6
+ class RegionReRoute < Legion::Transport::Message
7
+ def exchange
8
+ Legion::Transport::Exchanges::Task
9
+ end
10
+
11
+ def routing_key
12
+ "region.reroute.#{target_region}"
13
+ end
14
+
15
+ def message
16
+ {
17
+ original_payload: @options[:original_payload] || @options.except(:target_region),
18
+ target_region: target_region,
19
+ source_region: source_region,
20
+ rerouted_at: Time.now.to_i
21
+ }
22
+ end
23
+
24
+ def validate
25
+ raise ArgumentError, 'target_region is required' unless @options[:target_region].is_a?(String) && !@options[:target_region].empty?
26
+
27
+ @valid = true
28
+ end
29
+
30
+ private
31
+
32
+ def target_region
33
+ @options[:target_region]
34
+ end
35
+
36
+ def source_region
37
+ @options[:source_region] ||
38
+ (defined?(Legion::Settings) && Legion::Settings.dig(:region, :current)) ||
39
+ 'unknown'
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -84,3 +84,4 @@ require_relative 'queues/node_status'
84
84
  require_relative 'queues/task_log'
85
85
  require_relative 'queues/task_update'
86
86
  require_relative 'queues/agent'
87
+ require_relative 'queues/region_outbound'
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Transport
5
+ module Queues
6
+ module RegionOutbound
7
+ module_function
8
+
9
+ def declare_all
10
+ peers = defined?(Legion::Settings) && Legion::Settings.dig(:region, :peers)
11
+ return [] unless peers.is_a?(Array) && !peers.empty?
12
+
13
+ current = defined?(Legion::Region) ? Legion::Region.current : nil
14
+ peers.reject { |p| p == current }.map do |peer|
15
+ declare_outbound(peer)
16
+ end
17
+ end
18
+
19
+ def declare_outbound(target_region)
20
+ queue_name = queue_name_for(target_region)
21
+ channel = Legion::Transport::Connection.channel
22
+ channel.queue(
23
+ queue_name,
24
+ durable: true,
25
+ arguments: { 'x-dead-letter-exchange' => 'tasks.dlx' }
26
+ )
27
+ rescue StandardError => e
28
+ Legion::Transport.logger.warn "RegionOutbound: failed to declare queue for #{target_region}: #{e.message}"
29
+ nil
30
+ end
31
+
32
+ def queue_name_for(target_region)
33
+ "legion.tasks.outbound.#{target_region}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -16,11 +16,11 @@ module Legion
16
16
 
17
17
  {
18
18
  read_timeout: 1,
19
- heartbeat: 30,
19
+ heartbeat: (ENV['transport.connection.heartbeat'] || 30).to_i,
20
20
  automatically_recover: true,
21
21
  continuation_timeout: 4000,
22
- network_recovery_interval: 1,
23
- connection_timeout: 1,
22
+ network_recovery_interval: (ENV['transport.connection.recovery_interval'] || 2).to_i,
23
+ connection_timeout: (ENV['transport.connection.connection_timeout'] || 10).to_i,
24
24
  frame_max: 65_536,
25
25
  user: ENV['transport.connection.user'] || 'guest',
26
26
  password: ENV['transport.connection.password'] || 'guest',
@@ -110,17 +110,27 @@ module Legion
110
110
  end
111
111
 
112
112
  def self.default
113
+ cluster_csv = ENV.fetch('transport.cluster_nodes', '')
113
114
  {
114
- type: 'rabbitmq',
115
- connected: false,
116
- logger_level: ENV['transport.logger_level'] || 'info',
117
- messages: messages,
118
- prefetch: ENV['transport.prefetch'].to_i,
119
- exchanges: exchanges,
120
- queues: queues,
121
- connection: connection,
122
- channel: channel,
123
- tenant_topology: tenant_topology
115
+ type: 'rabbitmq',
116
+ connected: false,
117
+ logger_level: ENV['transport.logger_level'] || 'info',
118
+ messages: messages,
119
+ prefetch: ENV['transport.prefetch'].to_i,
120
+ exchanges: exchanges,
121
+ queues: queues,
122
+ connection: connection,
123
+ channel: channel,
124
+ tenant_topology: tenant_topology,
125
+ cluster_nodes: cluster_csv.empty? ? [] : cluster_csv.split(',').map(&:strip),
126
+ connection_pool_size: (ENV['transport.connection_pool_size'] || 1).to_i,
127
+ region: ENV.fetch('transport.region', nil),
128
+ management_port: (ENV['transport.management_port'] || 15_672).to_i,
129
+ quorum_queue_policy: {
130
+ enabled: ENV['transport.quorum_queue_policy.enabled'] == 'true',
131
+ pattern: ENV['transport.quorum_queue_policy.pattern'] || '^legion\\.',
132
+ delivery_limit: (ENV['transport.quorum_queue_policy.delivery_limit'] || 5).to_i
133
+ }
124
134
  }
125
135
  end
126
136
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Transport
5
- VERSION = '1.2.8'
5
+ VERSION = '1.3.2'
6
6
  end
7
7
  end
@@ -3,6 +3,7 @@
3
3
  require 'legion/transport/version'
4
4
  require 'legion/settings'
5
5
  require 'legion/transport/settings'
6
+ require_relative 'transport/errors'
6
7
 
7
8
  module Legion
8
9
  module Transport
@@ -39,6 +40,9 @@ module Legion
39
40
  end
40
41
  end
41
42
 
43
+ require_relative 'transport/helpers/pool'
44
+ require_relative 'transport/helpers/channel_pool'
45
+ require_relative 'transport/helpers/policy'
42
46
  require_relative 'transport/common'
43
47
  require_relative 'transport/queue'
44
48
  require_relative 'transport/exchange'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-transport
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.8
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -94,6 +94,7 @@ files:
94
94
  - ".rubocop.yml"
95
95
  - CHANGELOG.md
96
96
  - CLAUDE.md
97
+ - CODEOWNERS
97
98
  - Gemfile
98
99
  - LICENSE
99
100
  - README.md
@@ -104,18 +105,24 @@ files:
104
105
  - lib/legion/transport/connection/ssl.rb
105
106
  - lib/legion/transport/connection/vault.rb
106
107
  - lib/legion/transport/consumer.rb
108
+ - lib/legion/transport/errors.rb
107
109
  - lib/legion/transport/exchange.rb
108
110
  - lib/legion/transport/exchanges/agent.rb
109
111
  - lib/legion/transport/exchanges/crypt.rb
110
112
  - lib/legion/transport/exchanges/extensions.rb
111
113
  - lib/legion/transport/exchanges/lex.rb
114
+ - lib/legion/transport/exchanges/logging.rb
112
115
  - lib/legion/transport/exchanges/node.rb
113
116
  - lib/legion/transport/exchanges/task.rb
117
+ - lib/legion/transport/helpers/channel_pool.rb
118
+ - lib/legion/transport/helpers/policy.rb
119
+ - lib/legion/transport/helpers/pool.rb
114
120
  - lib/legion/transport/local.rb
115
121
  - lib/legion/transport/message.rb
116
122
  - lib/legion/transport/messages/check_subtask.rb
117
123
  - lib/legion/transport/messages/dynamic.rb
118
124
  - lib/legion/transport/messages/lex_register.rb
125
+ - lib/legion/transport/messages/region_re_route.rb
119
126
  - lib/legion/transport/messages/request_cluster_secret.rb
120
127
  - lib/legion/transport/messages/subtask.rb
121
128
  - lib/legion/transport/messages/task.rb
@@ -126,6 +133,7 @@ files:
126
133
  - lib/legion/transport/queues/node.rb
127
134
  - lib/legion/transport/queues/node_crypt.rb
128
135
  - lib/legion/transport/queues/node_status.rb
136
+ - lib/legion/transport/queues/region_outbound.rb
129
137
  - lib/legion/transport/queues/task_log.rb
130
138
  - lib/legion/transport/queues/task_update.rb
131
139
  - lib/legion/transport/settings.rb