railway-ipc 2.0.3 → 3.0.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/.gitignore +3 -0
- data/CHANGELOG.md +26 -1
- data/README.md +20 -0
- data/lib/railway_ipc.rb +9 -3
- data/lib/railway_ipc/connection_manager.rb +52 -0
- data/lib/railway_ipc/consumer/consumer.rb +24 -7
- data/lib/railway_ipc/logger.rb +3 -3
- data/lib/railway_ipc/publisher.rb +10 -11
- data/lib/railway_ipc/rabbitmq/adapter.rb +1 -1
- data/lib/railway_ipc/rpc/concerns/message_observation_configurable.rb +1 -1
- data/lib/railway_ipc/tasks/start_consumers.rake +6 -0
- data/lib/railway_ipc/version.rb +1 -1
- data/railway_ipc.gemspec +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5002df116daa50da3bfcefc3a966901a49c04671e869f23a645482ef9cf4c619
|
4
|
+
data.tar.gz: de49b0064f5b57f77893f46701007928a3e1c0fa4644a34041de50c6c4113f18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ed5098e317e039d3e4ed0305aca7f571be35f1ace0d88a513ef702ec381f10fe8291378b00ca181a6d23cfcbc7882c367dacedfb68d458ff71f9c8f808128d3
|
7
|
+
data.tar.gz: a30c8d4795fcd839588a8e9fc375c027b4a36e48b64eee9800a659fa4380f2d89d66f89c838c80fb70bf2072917146bcb6bd4ad4917141fa4dcfc0cfdf89db14
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -10,6 +10,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
10
10
|
### Removed
|
11
11
|
### Fixed
|
12
12
|
|
13
|
+
## [3.0.0] - 2020-12-07
|
14
|
+
### Changed
|
15
|
+
* Consumers _will no longer crash_ when an exception is raised. Instead, consumers will move the message that caused the exception to a single dead-letter exchange called 'ipc:errors'. Railway will configure the dead-letter exchange automatically.
|
16
|
+
|
17
|
+
## [2.2.2] - 2020-11-20
|
18
|
+
### Fixed
|
19
|
+
* Fixed Publisher class channel leak. Channels were being created on each instantiation of a Publisher instead of being re-used.
|
20
|
+
|
21
|
+
## [2.2.1] - 2020-10-20
|
22
|
+
### Added
|
23
|
+
* Logging to indicate when options passed via `listen_to`/`from_queue` are overriding the defaults.
|
24
|
+
|
25
|
+
## [2.2.0] - 2020-10-20
|
26
|
+
### Added
|
27
|
+
* The ability to configure workers to handle different workloads via `rake railway_ipc::consumers:spawn`
|
28
|
+
|
29
|
+
## [2.1.0] - 2020-10-19
|
30
|
+
### Added
|
31
|
+
* :options parameter to `listen_to` which passes keys along to Sneaker's `from_queue` method.
|
32
|
+
|
13
33
|
## [2.0.3] - 2020-09-02
|
14
34
|
### Fixed
|
15
35
|
* Fix RPC server. RPC servers need to conform to the Sneaker worker API (i.e. their initializers need to be able to accept queue name / pool and they require a `stop` method.
|
@@ -66,7 +86,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
66
86
|
### Added
|
67
87
|
- Correlation ID and message UUID are auto generated for messages for IDs are not passed in [#23](https://github.com/learn-co/railway_ipc_gem/pull/23)
|
68
88
|
|
69
|
-
[Unreleased]: https://github.com/learn-co/railway_ipc_gem/compare/
|
89
|
+
[Unreleased]: https://github.com/learn-co/railway_ipc_gem/compare/v30.0.0...HEAD
|
90
|
+
[3.0.0]: https://github.com/learn-co/railway_ipc_gem/compare/v2.2.2...v3.0.0
|
91
|
+
[2.2.2]: https://github.com/learn-co/railway_ipc_gem/compare/v2.2.1...v2.2.2
|
92
|
+
[2.2.1]: https://github.com/learn-co/railway_ipc_gem/compare/v2.2.0...v2.2.1
|
93
|
+
[2.2.0]: https://github.com/learn-co/railway_ipc_gem/compare/v2.1.0...v2.2.0
|
94
|
+
[2.1.0]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.3...v2.1.0
|
70
95
|
[2.0.3]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.2...v2.0.3
|
71
96
|
[2.0.2]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.1...v2.0.2
|
72
97
|
[2.0.1]: https://github.com/learn-co/railway_ipc_gem/compare/v2.0.0...v2.0.1
|
data/README.md
CHANGED
@@ -52,6 +52,26 @@ Then, run your consumers
|
|
52
52
|
bundle exec rake railway_ipc:consumers:start CONSUMERS=YourConsumer,YourOtherConsumer
|
53
53
|
```
|
54
54
|
|
55
|
+
You may also configure your consumers more granularly using:
|
56
|
+
|
57
|
+
`./config/sneaker_worker_groups.yml`
|
58
|
+
```yaml
|
59
|
+
HighPriority:
|
60
|
+
classes: YourConsumer,YourOtherConsumer
|
61
|
+
workers: 5
|
62
|
+
LowPriority:
|
63
|
+
classes: YourThirdConsumer
|
64
|
+
workers: 1
|
65
|
+
```
|
66
|
+
|
67
|
+
```bash
|
68
|
+
bundle exec rake railway_ipc:consumers:spawn
|
69
|
+
```
|
70
|
+
|
71
|
+
By default, `spawn` will map to `./config/sneaker_worker_groups.yml` but you can override it by using `WORKER_GROUP_CONFIG`.
|
72
|
+
|
73
|
+
See the [Sneaker Documentation](https://github.com/jondot/sneakers/wiki/Handling-different-workloads) for more information.
|
74
|
+
|
55
75
|
# Request/Response
|
56
76
|
|
57
77
|
Define your server, client and responder. Docs coming soon.
|
data/lib/railway_ipc.rb
CHANGED
@@ -2,9 +2,10 @@
|
|
2
2
|
|
3
3
|
require 'railway_ipc/version'
|
4
4
|
require 'sneakers'
|
5
|
+
require 'sneakers/spawner'
|
5
6
|
require 'bunny'
|
6
7
|
require 'active_record'
|
7
|
-
require '
|
8
|
+
require 'singleton'
|
8
9
|
require 'railway_ipc/logger'
|
9
10
|
require 'railway_ipc/unhandled_message_error'
|
10
11
|
require 'railway_ipc/response'
|
@@ -14,6 +15,7 @@ require 'railway_ipc/rabbitmq/adapter'
|
|
14
15
|
require 'railway_ipc/handler'
|
15
16
|
require 'railway_ipc/handler_store'
|
16
17
|
require 'railway_ipc/incoming_message'
|
18
|
+
require 'railway_ipc/connection_manager'
|
17
19
|
require 'railway_ipc/publisher'
|
18
20
|
require 'railway_ipc/responder'
|
19
21
|
require 'railway_ipc/rpc/rpc'
|
@@ -29,12 +31,16 @@ module RailwayIpc
|
|
29
31
|
Rake::Task['sneakers:run'].invoke
|
30
32
|
end
|
31
33
|
|
32
|
-
def self.
|
34
|
+
def self.spawn
|
35
|
+
Sneakers::Spawner.spawn
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.configure(log_device=$stdout, level=::Logger::INFO, log_formatter=nil)
|
33
39
|
@logger = RailwayIpc::Logger.new(log_device, level, log_formatter)
|
34
40
|
end
|
35
41
|
|
36
42
|
def self.logger
|
37
|
-
@logger || RailwayIpc::Logger.new(
|
43
|
+
@logger || RailwayIpc::Logger.new($stdout)
|
38
44
|
end
|
39
45
|
|
40
46
|
def self.bunny_connection
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module RailwayIpc
|
6
|
+
# RabbitMQ connection manager. Ensures there is a single RabbitMQ
|
7
|
+
# connection and channel per thread, which prevents channel leaks.
|
8
|
+
#
|
9
|
+
class ConnectionManager
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
establish_connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def establish_connection
|
17
|
+
@connection = Bunny.new(
|
18
|
+
host: settings[:host],
|
19
|
+
user: settings[:user],
|
20
|
+
pass: settings[:pass],
|
21
|
+
port: settings[:port],
|
22
|
+
vhost: settings[:vhost] || '/',
|
23
|
+
logger: RailwayIpc.logger
|
24
|
+
)
|
25
|
+
@connection.start
|
26
|
+
@channel = @connection.create_channel
|
27
|
+
|
28
|
+
@connection
|
29
|
+
end
|
30
|
+
|
31
|
+
def channel
|
32
|
+
return @channel if connected?
|
33
|
+
|
34
|
+
establish_connection
|
35
|
+
@channel
|
36
|
+
end
|
37
|
+
|
38
|
+
def connected?
|
39
|
+
@connection&.connected? && @channel&.open?
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def amqp_url
|
45
|
+
@amqp_url ||= ENV.fetch('RAILWAY_RABBITMQ_CONNECTION_URL', 'amqp://guest:guest@localhost:5672')
|
46
|
+
end
|
47
|
+
|
48
|
+
def settings
|
49
|
+
@settings ||= AMQ::Settings.parse_amqp_url(amqp_url)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -3,7 +3,10 @@
|
|
3
3
|
module RailwayIpc
|
4
4
|
class Consumer
|
5
5
|
include Sneakers::Worker
|
6
|
+
|
6
7
|
def self.inherited(base)
|
8
|
+
super
|
9
|
+
|
7
10
|
base.instance_eval do
|
8
11
|
def handlers
|
9
12
|
@handlers ||= RailwayIpc::HandlerStore.new
|
@@ -11,13 +14,27 @@ module RailwayIpc
|
|
11
14
|
end
|
12
15
|
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
# rubocop:disable Metrics/MethodLength
|
18
|
+
def self.listen_to(queue:, exchange:, options: {})
|
19
|
+
unless options.empty?
|
20
|
+
RailwayIpc.logger.info(
|
21
|
+
"Overriding configuration for #{queue} with new options",
|
22
|
+
feature: 'railway_ipc_consumer',
|
23
|
+
options: options
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
from_queue queue, {
|
28
|
+
exchange: exchange,
|
29
|
+
durable: true,
|
30
|
+
exchange_type: :fanout,
|
31
|
+
arguments: {
|
32
|
+
'x-dead-letter-exchange' => 'ipc:errors'
|
33
|
+
},
|
34
|
+
connection: RailwayIpc.bunny_connection
|
35
|
+
}.merge(options)
|
20
36
|
end
|
37
|
+
# rubocop:enable Metrics/MethodLength
|
21
38
|
|
22
39
|
def self.handle(message_type, with:)
|
23
40
|
handlers.register(message: message_type, handler: with)
|
@@ -52,7 +69,7 @@ module RailwayIpc
|
|
52
69
|
error: e.class,
|
53
70
|
payload: payload
|
54
71
|
)
|
55
|
-
|
72
|
+
reject!
|
56
73
|
end
|
57
74
|
|
58
75
|
def get_handler(type)
|
data/lib/railway_ipc/logger.rb
CHANGED
@@ -21,7 +21,7 @@ module RailwayIpc
|
|
21
21
|
# logger = RailwayIpc::Logger.new(STDOUT, Logger::INFO, OjFormatter)
|
22
22
|
#
|
23
23
|
class Logger
|
24
|
-
def initialize(device
|
24
|
+
def initialize(device=$stdout, level=::Logger::INFO, formatter=nil)
|
25
25
|
@logger = ::Logger.new(device)
|
26
26
|
@logger.level = level
|
27
27
|
@logger.formatter = formatter if formatter
|
@@ -32,7 +32,7 @@ module RailwayIpc
|
|
32
32
|
data.merge!(feature: 'railway_ipc') unless data.key?(:feature)
|
33
33
|
return logger.send(level, data.merge(message: message)) unless block
|
34
34
|
|
35
|
-
data = message.merge(data) if message
|
35
|
+
data = message.merge(data) if message.is_a?(Hash)
|
36
36
|
data.merge!(message: block.call)
|
37
37
|
|
38
38
|
# This is for compatability w/ Ruby's Logger. Ruby's Logger class
|
@@ -41,7 +41,7 @@ module RailwayIpc
|
|
41
41
|
# is assumed to be the `progname`.
|
42
42
|
#
|
43
43
|
# https://github.com/ruby/logger/blob/master/lib/logger.rb#L471
|
44
|
-
data.merge!(progname: message) if message
|
44
|
+
data.merge!(progname: message) if message.is_a?(String)
|
45
45
|
logger.send(level, data)
|
46
46
|
end
|
47
47
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'singleton'
|
4
|
-
|
5
3
|
module RailwayIpc
|
6
4
|
class SingletonPublisher < Sneakers::Publisher
|
7
5
|
include ::Singleton
|
@@ -53,19 +51,12 @@ module RailwayIpc
|
|
53
51
|
end
|
54
52
|
|
55
53
|
module RailwayIpc
|
56
|
-
class Publisher
|
54
|
+
class Publisher
|
57
55
|
attr_reader :exchange_name, :message_store
|
58
56
|
|
59
57
|
def initialize(opts={})
|
60
58
|
@exchange_name = opts.fetch(:exchange_name)
|
61
59
|
@message_store = opts.fetch(:message_store, RailwayIpc::PublishedMessage)
|
62
|
-
connection = opts.fetch(:connection, nil)
|
63
|
-
options = {
|
64
|
-
exchange: exchange_name,
|
65
|
-
connection: connection,
|
66
|
-
exchange_type: :fanout
|
67
|
-
}.compact
|
68
|
-
super(options)
|
69
60
|
end
|
70
61
|
|
71
62
|
# rubocop:disable Metrics/AbcSize
|
@@ -75,7 +66,7 @@ module RailwayIpc
|
|
75
66
|
RailwayIpc.logger.info('Publishing message', log_message_options(message))
|
76
67
|
|
77
68
|
stored_message = message_store.store_message(exchange_name, message)
|
78
|
-
|
69
|
+
exchange.publish(RailwayIpc::Rabbitmq::Payload.encode(message))
|
79
70
|
rescue RailwayIpc::InvalidProtobuf => e
|
80
71
|
RailwayIpc.logger.error('Invalid protobuf', log_message_options(message))
|
81
72
|
raise e
|
@@ -88,8 +79,16 @@ module RailwayIpc
|
|
88
79
|
end
|
89
80
|
# rubocop:enable Metrics/AbcSize
|
90
81
|
|
82
|
+
def exchange
|
83
|
+
@exchange ||= channel.exchange(exchange_name, type: :fanout, durable: true, auto_delete: false, arguments: {})
|
84
|
+
end
|
85
|
+
|
91
86
|
private
|
92
87
|
|
88
|
+
def channel
|
89
|
+
RailwayIpc::ConnectionManager.instance.channel
|
90
|
+
end
|
91
|
+
|
93
92
|
def log_message_options(message)
|
94
93
|
{
|
95
94
|
feature: 'railway_ipc_publisher',
|
@@ -16,7 +16,7 @@ module RailwayIpc
|
|
16
16
|
:port,
|
17
17
|
:user
|
18
18
|
|
19
|
-
def initialize(amqp_url: ENV['RAILWAY_RABBITMQ_CONNECTION_URL'],
|
19
|
+
def initialize(exchange_name:, amqp_url: ENV['RAILWAY_RABBITMQ_CONNECTION_URL'], queue_name: '', options: {})
|
20
20
|
@queue_name = queue_name
|
21
21
|
@exchange_name = exchange_name
|
22
22
|
settings = AMQ::Settings.parse_amqp_url(amqp_url)
|
@@ -2,9 +2,15 @@
|
|
2
2
|
|
3
3
|
namespace :railway_ipc do
|
4
4
|
namespace :consumers do
|
5
|
+
desc 'Start consumers via explicitly defining them'
|
5
6
|
task :start do
|
6
7
|
ENV['WORKERS'] = ENV['CONSUMERS']
|
7
8
|
RailwayIpc.start
|
8
9
|
end
|
10
|
+
|
11
|
+
desc 'Start consumers via ./config/sneaker_worker_groups.yml'
|
12
|
+
task :spawn do
|
13
|
+
RailwayIpc.spawn
|
14
|
+
end
|
9
15
|
end
|
10
16
|
end
|
data/lib/railway_ipc/version.rb
CHANGED
data/railway_ipc.gemspec
CHANGED
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
35
|
spec.require_paths = ['lib']
|
36
36
|
|
37
|
-
spec.add_development_dependency 'bundler', '2.
|
37
|
+
spec.add_development_dependency 'bundler', '2.1.4'
|
38
38
|
spec.add_development_dependency 'factory_bot', '~> 5.1'
|
39
39
|
spec.add_development_dependency 'google-protobuf', '~> 3.9'
|
40
40
|
spec.add_development_dependency 'rake', '>= 10.0.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: railway-ipc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ''
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.
|
19
|
+
version: 2.1.4
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.
|
26
|
+
version: 2.1.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: factory_bot
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -225,6 +225,7 @@ files:
|
|
225
225
|
- bin/setup
|
226
226
|
- lib/railway_ipc.rb
|
227
227
|
- lib/railway_ipc/Rakefile
|
228
|
+
- lib/railway_ipc/connection_manager.rb
|
228
229
|
- lib/railway_ipc/consumer/consumer.rb
|
229
230
|
- lib/railway_ipc/consumer/process_incoming_message.rb
|
230
231
|
- lib/railway_ipc/errors.rb
|
@@ -278,8 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
278
279
|
- !ruby/object:Gem::Version
|
279
280
|
version: '0'
|
280
281
|
requirements: []
|
281
|
-
|
282
|
-
rubygems_version: 2.7.6
|
282
|
+
rubygems_version: 3.0.3
|
283
283
|
signing_key:
|
284
284
|
specification_version: 4
|
285
285
|
summary: IPC components for Rails
|