cosmonats 0.1.1 → 0.1.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: c0995753e33b278f5a0d2d6455faf07f5c3c604595fa93c6a40260c68b69c160
4
- data.tar.gz: c78c109d014f863079c3ee53171a852241b5a0b3082f9a29b1438b5708782436
3
+ metadata.gz: baa2c1820f8c3b420b3ef7411ad74bd8989f5a4d198df6bfe18b5596959d5464
4
+ data.tar.gz: 282eb6f3dcfc3945596426b3ba39e31cf68d84e73692d0bd92165485b69654ae
5
5
  SHA512:
6
- metadata.gz: 7947f18a6ab2830fa726d9903eb8ee926bf4bbf897ee71e28c262adc4aa664828c1ad758776aa61caac2964e52c83370cd4d4090c3ce05f399988c79917676e6
7
- data.tar.gz: 12a7f6f7e3a715bdda711fa29acbba7db822d17cd4441b008942d7cac0eb66581f2412a4df16811580792af0913c0ffbe1370b0465478e577a6a3a45b714364b
6
+ metadata.gz: 35c534343db619b2fe9bc314b9517bb67adb808b92c45ae64fc9fb8d3fa533d625d5e1e64149d7ff0ebf8158cd400188c9601ab8b9fe4622f34dbd73dd2a95fc
7
+ data.tar.gz: '08e9b894165cd0e6254fccca8d73c1cef88bde32e8345de21f812195573add70847c95a0a2cd4c87cb3afc90824028665de2a4a71118aa67c966c056c2680960'
data/lib/cosmo/cli.rb CHANGED
@@ -15,7 +15,7 @@ module Cosmo
15
15
 
16
16
  def run
17
17
  flags, command, options = parse
18
- load_config(flags[:config_file])
18
+ load_config(flags)
19
19
  puts self.class.banner
20
20
  boot_application
21
21
  require_path(flags[:require])
@@ -37,7 +37,8 @@ module Cosmo
37
37
  [flags, command, options]
38
38
  end
39
39
 
40
- def load_config(path)
40
+ def load_config(flags)
41
+ path = flags[:config_file]
41
42
  raise ConfigNotFoundError, path if path && !File.exist?(path)
42
43
 
43
44
  unless path
@@ -47,6 +48,8 @@ module Cosmo
47
48
  end
48
49
 
49
50
  Config.load(path)
51
+ Config.set(:concurrency, flags[:concurrency]) if flags[:concurrency]
52
+ Config.set(:timeout, flags[:timeout]) if flags[:timeout]
50
53
  end
51
54
 
52
55
  def boot_application
@@ -96,7 +99,8 @@ module Cosmo
96
99
  end
97
100
 
98
101
  o.on "-S", "--setup", "Load config, create streams and exit" do
99
- load_config(flags[:config_file])
102
+ load_config(flags)
103
+ boot_application
100
104
 
101
105
  Config[:streams].each do |name, config|
102
106
  Client.instance.stream_info(name)
@@ -22,18 +22,23 @@ module Cosmo
22
22
  subject = config.delete(:subject)
23
23
  priority = config.delete(:priority)
24
24
  @weights += ([stream_name] * priority.to_i) if priority
25
- @consumers[stream_name] = client.subscribe(subject, consumer_name, config)
25
+ subscription = client.subscribe(subject, consumer_name, config)
26
+ @consumers << [subscription, stream_name]
26
27
  end
27
28
  end
28
29
 
29
- def work_loop
30
+ def work_loop # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
30
31
  while running?
31
32
  @weights.shuffle.each do |stream_name|
32
33
  break unless running?
33
34
 
34
35
  begin
35
36
  timeout = ENV.fetch("COSMO_JOBS_FETCH_TIMEOUT", 0.1).to_f
36
- @pool.post { fetch_messages(stream_name, batch_size: 1, timeout:) }
37
+ @pool.post do
38
+ subscription = @consumers.find { |(_, sn)| sn == stream_name }&.first
39
+ messages = fetch_messages(subscription, batch_size: 1, timeout:)
40
+ process(messages) if messages&.any?
41
+ end
37
42
  rescue Concurrent::RejectedExecutionError
38
43
  break # pool doesn't accept new jobs, we are shutting down
39
44
  end
@@ -43,26 +48,26 @@ module Cosmo
43
48
  end
44
49
  end
45
50
 
46
- def schedule_loop # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
51
+ def schedule_loop # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
47
52
  while running?
48
53
  break unless running?
49
54
 
55
+ now = Time.now.to_i
50
56
  timeout = ENV.fetch("COSMO_JOBS_SCHEDULER_FETCH_TIMEOUT", 5).to_f
51
- fetch_messages(:scheduled, batch_size: 100, timeout:) do |messages|
52
- now = Time.now.to_i
53
- messages.each do |message|
54
- headers = message.header.except("X-Stream", "X-Subject", "X-Execute-At", "Nats-Expected-Stream")
55
- stream, subject, execute_at = message.header.values_at("X-Stream", "X-Subject", "X-Execute-At")
56
- headers["Nats-Expected-Stream"] = stream
57
- execute_at = execute_at.to_i
58
-
59
- if now >= execute_at
60
- client.publish(subject, message.data, headers: headers)
61
- message.ack
62
- else
63
- delay_ns = (execute_at - now) * 1_000_000_000
64
- message.nak(delay: delay_ns)
65
- end
57
+ subscription = @consumers.find { |(_, sn)| sn == :scheduled }&.first
58
+ messages = fetch_messages(subscription, batch_size: 100, timeout:)
59
+ messages&.each do |message|
60
+ headers = message.header.except("X-Stream", "X-Subject", "X-Execute-At", "Nats-Expected-Stream")
61
+ stream, subject, execute_at = message.header.values_at("X-Stream", "X-Subject", "X-Execute-At")
62
+ headers["Nats-Expected-Stream"] = stream
63
+ execute_at = execute_at.to_i
64
+
65
+ if now >= execute_at
66
+ client.publish(subject, message.data, headers: headers)
67
+ message.ack
68
+ else
69
+ delay_ns = (execute_at - now) * 1_000_000_000
70
+ message.nak(delay: delay_ns)
66
71
  end
67
72
  end
68
73
 
@@ -10,7 +10,7 @@ module Cosmo
10
10
  @pool = pool
11
11
  @running = running
12
12
  @options = options
13
- @consumers = {}
13
+ @consumers = []
14
14
  end
15
15
 
16
16
  def run
@@ -39,9 +39,8 @@ module Cosmo
39
39
  @running.true?
40
40
  end
41
41
 
42
- def fetch_messages(stream_name, batch_size:, timeout:)
43
- messages = @consumers[stream_name].fetch(batch_size, timeout:)
44
- block_given? ? yield(messages) : process(stream_name, messages)
42
+ def fetch_messages(subscription, batch_size:, timeout:)
43
+ subscription.fetch(batch_size, timeout:)
45
44
  rescue NATS::Timeout
46
45
  # No messages, continue
47
46
  end
@@ -5,8 +5,7 @@ module Cosmo
5
5
  class Processor < ::Cosmo::Processor
6
6
  def initialize(pool, running, options)
7
7
  super
8
- @configs = {}
9
- @processors = {}
8
+ @configs = []
10
9
  end
11
10
 
12
11
  private
@@ -17,19 +16,20 @@ module Cosmo
17
16
 
18
17
  def setup
19
18
  setup_configs
20
- setup_processors
21
19
  setup_consumers
22
20
  end
23
21
 
24
- def work_loop
22
+ def work_loop # rubocop:disable Metrics/CyclomaticComplexity
25
23
  while running?
26
- @consumers.each_key do |stream_name|
24
+ @consumers.each do |(subscription, config, processor)|
27
25
  break unless running?
28
26
 
29
27
  begin
30
- batch_size = @configs[stream_name][:batch_size]
31
28
  timeout = ENV.fetch("COSMO_STREAMS_FETCH_TIMEOUT", 0.1).to_f
32
- @pool.post { fetch_messages(stream_name, batch_size:, timeout:) }
29
+ @pool.post do
30
+ messages = fetch_messages(subscription, batch_size: config[:batch_size], timeout:)
31
+ process(messages, processor) if messages&.any?
32
+ end
33
33
  rescue Concurrent::RejectedExecutionError
34
34
  break # pool doesn't accept new jobs, we are shutting down
35
35
  end
@@ -39,9 +39,8 @@ module Cosmo
39
39
  end
40
40
  end
41
41
 
42
- def process(stream_name, messages) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
42
+ def process(messages, processor) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
43
43
  metadata = messages.last.metadata
44
- processor = @processors[stream_name]
45
44
  serializer = processor.class.default_options.dig(:publisher, :serializer)
46
45
  messages = messages.map { Message.new(_1, serializer:) }
47
46
 
@@ -64,21 +63,21 @@ module Cosmo
64
63
  end
65
64
 
66
65
  def setup_configs
67
- configs = static_config.merge(dynamic_config)
68
- configs = configs.select { |_, c| @options[:processors].include?(c[:class].name) } if @options[:processors]
69
- @configs.merge!(configs)
70
- end
66
+ @configs = static_config + dynamic_config
67
+ return unless @options[:processors]
71
68
 
72
- def setup_processors
73
- @configs.each { |s, c| @processors[s] = c[:class].new }
69
+ pattern = Regexp.new(@options[:processors].map { "\\b#{_1}\\b" }.join("|"))
70
+ @configs.select! { _1[:class].name.match?(pattern) }
74
71
  end
75
72
 
76
73
  def setup_consumers
77
- @configs.each do |stream_name, config|
74
+ @configs.each do |config|
75
+ processor = config[:class].new
78
76
  subjects = config.dig(:consumer, :subjects)
79
77
  deliver_policy = Config.deliver_policy(config[:start_position])
80
- config, consumer_name = config.values_at(:consumer, :consumer_name)
81
- @consumers[stream_name] = client.subscribe(subjects, consumer_name, config.merge(deliver_policy))
78
+ consumer_config, consumer_name = config.values_at(:consumer, :consumer_name)
79
+ subscription = client.subscribe(subjects, consumer_name, consumer_config.merge(deliver_policy))
80
+ @consumers << [subscription, config, processor]
82
81
  end
83
82
  end
84
83
 
@@ -86,14 +85,12 @@ module Cosmo
86
85
  Config.dig(:consumers, :streams)&.filter_map do |config|
87
86
  next unless (klass = Utils::String.safe_constantize(config[:class]))
88
87
 
89
- [config[:stream].to_sym, config.merge(class: klass)]
90
- end.to_h
88
+ config.merge(class: klass)
89
+ end.to_a
91
90
  end
92
91
 
93
92
  def dynamic_config
94
- Config.system[:streams].to_h do |klass|
95
- [klass.default_options[:stream].to_sym, klass.default_options.merge(class: klass)]
96
- end
93
+ Config.system[:streams].map { _1.default_options.merge(class: _1) }
97
94
  end
98
95
  end
99
96
  end
data/lib/cosmo/stream.rb CHANGED
@@ -9,12 +9,12 @@ module Cosmo
9
9
  module Stream
10
10
  def self.included(base)
11
11
  base.extend(ClassMethods)
12
- base.register
13
12
  end
14
13
 
15
14
  module ClassMethods
16
15
  def options(stream: nil, consumer_name: nil, batch_size: nil, start_position: nil, consumer: nil, publisher: nil) # rubocop:disable Metrics/ParameterLists
17
- default_options.merge!({ stream: stream, consumer_name: consumer_name, batch_size:, start_position:, consumer:, publisher: }.compact)
16
+ register
17
+ default_options.merge!({ stream:, consumer_name:, batch_size:, start_position:, consumer:, publisher: }.compact)
18
18
  end
19
19
 
20
20
  def publish(data, subject: nil, **options)
data/lib/cosmo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cosmo
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
data/sig/cosmo/cli.rbs CHANGED
@@ -14,7 +14,7 @@ module Cosmo
14
14
 
15
15
  def parse: () -> [Hash[Symbol, untyped], ::String?, Hash[Symbol, untyped]]
16
16
 
17
- def load_config: (::String? path) -> void
17
+ def load_config: (Hash[Symbol, untyped] flags) -> void
18
18
 
19
19
  def boot_application: () -> void
20
20
 
@@ -3,7 +3,7 @@ module Cosmo
3
3
  class Processor < ::Cosmo::Processor
4
4
  @weights: Array[Symbol]
5
5
 
6
- def initialize: (Utils::ThreadPool pool, untyped running) -> void
6
+ def initialize: (Utils::ThreadPool pool, untyped running, Hash[Symbol, untyped] options) -> void
7
7
 
8
8
  private
9
9
 
@@ -2,7 +2,7 @@ module Cosmo
2
2
  class Processor
3
3
  @pool: Utils::ThreadPool
4
4
  @running: untyped
5
- @consumers: Hash[Symbol, untyped]
5
+ @consumers: Array[untyped]
6
6
  @options: Hash[Symbol, untyped]
7
7
 
8
8
  def self.run: (*untyped) -> Processor
@@ -21,7 +21,7 @@ module Cosmo
21
21
 
22
22
  def running?: () -> bool
23
23
 
24
- def fetch_messages: (Symbol stream_name, batch_size: Integer, timeout: Float) ?{ (Array[untyped]) -> void } -> void
24
+ def fetch_messages: (untyped subscription, batch_size: Integer, timeout: Float) ?{ (Array[untyped]) -> void } -> void
25
25
 
26
26
  def client: () -> Client
27
27
 
@@ -1,10 +1,9 @@
1
1
  module Cosmo
2
2
  module Stream
3
3
  class Processor < ::Cosmo::Processor
4
- @configs: Hash[Symbol, Hash[Symbol, untyped]]
5
- @processors: Hash[Symbol, untyped]
4
+ @configs: Array[Hash[Symbol, untyped]]
6
5
 
7
- def initialize: (Utils::ThreadPool pool, untyped running) -> void
6
+ def initialize: (Utils::ThreadPool pool, untyped running, Hash[Symbol, untyped] options) -> void
8
7
 
9
8
  private
10
9
 
@@ -14,17 +13,15 @@ module Cosmo
14
13
 
15
14
  def work_loop: () -> void
16
15
 
17
- def process: (Symbol stream_name, Array[untyped] messages) -> void
16
+ def process: (Array[untyped] messages, untyped processor) -> void
18
17
 
19
18
  def setup_configs: () -> void
20
19
 
21
- def setup_processors: () -> void
22
-
23
20
  def setup_consumers: () -> void
24
21
 
25
- def static_config: -> Hash[Symbol, untyped]
22
+ def static_config: -> Array[Hash[Symbol, untyped]]
26
23
 
27
- def dynamic_config: -> Hash[Symbol, untyped]
24
+ def dynamic_config: -> Array[Hash[Symbol, untyped]]
28
25
  end
29
26
  end
30
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cosmonats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Vorotilin