sbmt-outbox 7.0.1 → 7.0.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: f929127e2a4e45dd88bde6fe1115b13651532fe5f9fa1a0c0112dbeba2111036
4
- data.tar.gz: f9368bb61d59199f5d73ccd0634dbad6c2a7b06817d94bbdfe0ea50fd5357db7
3
+ metadata.gz: 751fcef19c53ff5a38d589d0dac8d5a1007335faec5fd710b1fb1e69f2ea25ef
4
+ data.tar.gz: a12ec44b3c6774b0ff72503f60a645ba3b2a8d52ed28a70d85e51041ef17e2d8
5
5
  SHA512:
6
- metadata.gz: b4e3b29dc6495d129aa8beac3e99d5f5bff4fd07891775192c761556ea25f24fbf2471f88ab173ec2efc3106210a8b036545ce26c6c552ecc8699a0464a1d6f1
7
- data.tar.gz: ada7866bbef15b69b85b5fc7572a305f0ab0d9b603a3d8cf97ca91f4e6802e54d69c841f6ae414d1080eca7f7c890979fc6c0e8f2009874e2608322706af5af8
6
+ metadata.gz: ecfeecc1840db0694c89a950e35f30fe917b7ce7dbf7ad522f65c27ac1f0d3dceda550ff879b246683d795d5c6add96e9ea785cab89e0ee2dbbdec32f59c8746
7
+ data.tar.gz: 66c2b31e030041587f6e88ee2e35ae960f4e0a274128d10f770856b44302bdb805c5f89c0bc159d75b5349e9ecb75c0f54a71e676ee082fc5d09223dc060a78c
@@ -234,7 +234,7 @@ module Sbmt
234
234
  .outbox
235
235
  .delete_latency
236
236
  .measure({box_type: box_type, box_name: box_name}) do
237
- yield
237
+ yield
238
238
  end
239
239
  end
240
240
  end
@@ -34,8 +34,8 @@ module Sbmt
34
34
  def calc_bucket_partitions(count)
35
35
  (0...count).to_a
36
36
  .index_with do |x|
37
- (0...config.bucket_size).to_a
38
- .select { |p| p % count == x }
37
+ (0...config.bucket_size).to_a
38
+ .select { |p| p % count == x }
39
39
  end
40
40
  end
41
41
 
@@ -5,10 +5,8 @@ module Sbmt
5
5
  class ErrorTracker
6
6
  class << self
7
7
  def error(message, params = {})
8
- unless defined?(Sentry)
9
- Outbox.logger.log_error(message, **params)
10
- return
11
- end
8
+ Outbox.logger.log_error(message, **params)
9
+ return unless defined?(Sentry)
12
10
 
13
11
  Sentry.with_scope do |scope|
14
12
  scope.set_contexts(contexts: params)
@@ -21,7 +21,11 @@ module Sbmt
21
21
  concurrency: threads_count,
22
22
  name: "#{name}_thread_pool"
23
23
  ) do
24
- queue.pop
24
+ logger.log_debug("#{name}_thread_pool: requesting next task from queue")
25
+ task = queue.pop
26
+ logger.log_debug("#{name}_thread_pool: received task #{task&.item_class&.box_name}")
27
+
28
+ task
25
29
  end
26
30
 
27
31
  @started = false
@@ -30,10 +34,15 @@ module Sbmt
30
34
  end
31
35
 
32
36
  def start
37
+ logger.log_info("#{worker_name}: starting with #{@threads_count} threads")
38
+
33
39
  raise "#{worker_name} is already started" if started
34
40
  @started = true
35
41
 
42
+ logger.log_info("#{worker_name}: starting thread pool")
36
43
  thread_pool.start do |worker_number, scheduled_task|
44
+ logger.log_debug("#{worker_name}: worker #{worker_number} processing scheduled task for box #{scheduled_task&.item_class&.box_name}")
45
+
37
46
  result = ThreadPool::PROCESSED
38
47
  last_result = Thread.current[:last_polling_result]
39
48
 
@@ -47,7 +56,9 @@ module Sbmt
47
56
  box_worker.job_execution_runtime.measure(labels) do
48
57
  ::Rails.application.executor.wrap do
49
58
  logger.with_tags(**locked_task.log_tags) do
59
+ logger.log_debug("#{worker_name}: worker #{worker_number} processing locked task")
50
60
  result = safe_process_task(worker_number, locked_task)
61
+ logger.log_debug("#{worker_name}: worker #{worker_number} completed locked task")
51
62
  end
52
63
  end
53
64
  end
@@ -58,31 +69,62 @@ module Sbmt
58
69
  box_worker.job_counter.increment(base_labels.merge(state: locked_task ? "processed" : "skipped"), by: 1)
59
70
  end
60
71
 
72
+ logger.log_debug("#{worker_name}: worker #{worker_number} finished processing")
61
73
  Thread.current[:last_polling_result] = result || ThreadPool::PROCESSED
62
74
  ensure
75
+ logger.log_debug("#{worker_name}: returning task to queue")
63
76
  queue << scheduled_task
64
77
  end
65
78
  rescue => e
79
+ logger.log_error("#{worker_name}: thread pool encountered error during start: #{e.inspect}")
66
80
  Outbox.error_tracker.error(e)
67
81
  raise
68
82
  end
69
83
 
70
84
  def stop
85
+ logger.log_info("#{worker_name}: stopping worker")
71
86
  @started = false
72
87
  @thread_pool.stop
88
+ logger.log_info("#{worker_name}: worker stopped")
73
89
  end
74
90
 
75
91
  def ready?
76
- started && @thread_pool.running?
92
+ logger.log_debug("#{worker_name}: checking if ready")
93
+ unless started
94
+ logger.log_debug("#{worker_name}: checking if ready: not started")
95
+ return false
96
+ end
97
+
98
+ result = @thread_pool.running?
99
+ unless result
100
+ logger.log_debug("#{worker_name}: checking if ready: thread_pool is not running")
101
+ return false
102
+ end
103
+
104
+ logger.log_debug("#{worker_name}: ready? #{result}")
105
+ result
77
106
  end
78
107
 
79
108
  def alive?(timeout)
80
- return false unless ready?
109
+ logger.log_debug("#{worker_name}: checking if alive with timeout #{timeout}")
81
110
 
82
- @thread_pool.alive?(timeout)
111
+ unless ready?
112
+ logger.log_debug("#{worker_name}: checking if alive: not ready")
113
+ return false
114
+ end
115
+
116
+ result = @thread_pool.alive?(timeout)
117
+ unless result
118
+ logger.log_info("#{worker_name}: checking if alive: thread_pool is not alive")
119
+ return false
120
+ end
121
+
122
+ logger.log_debug("#{worker_name}: alive? #{result}")
123
+ result
83
124
  end
84
125
 
85
126
  def safe_process_task(worker_number, task)
127
+ logger.log_debug("#{worker_name}: safely processing task for worker #{worker_number}")
86
128
  process_task(worker_number, task)
87
129
  rescue => e
88
130
  log_fatal(e, task)
@@ -103,6 +145,8 @@ module Sbmt
103
145
  attr_accessor :queue, :thread_pool, :redis, :lock_manager
104
146
 
105
147
  def init_redis(redis)
148
+ logger.log_info("#{worker_name}: initializing Redis connection")
149
+
106
150
  self.redis = redis || ConnectionPool::Wrapper.new(size: threads_count) { RedisClientFactory.build(config.redis) }
107
151
 
108
152
  client = if Gem::Version.new(Redlock::VERSION) >= Gem::Version.new("2.0.0")
@@ -112,6 +156,7 @@ module Sbmt
112
156
  end
113
157
 
114
158
  self.lock_manager = Redlock::Client.new([client], retry_count: 0)
159
+ logger.log_info("#{worker_name}: Redis initialized")
115
160
  end
116
161
 
117
162
  def lock_task(scheduled_task)
@@ -124,8 +169,8 @@ module Sbmt
124
169
  Tasks::Default.new(item_class: item_class, worker_name: worker_name)
125
170
  end
126
171
 
172
+ logger.log_debug("#{worker_name}: building task queue with #{scheduled_tasks.length} tasks")
127
173
  scheduled_tasks.shuffle!
128
-
129
174
  Queue.new.tap { |queue| scheduled_tasks.each { |task| queue << task } }
130
175
  end
131
176
 
@@ -105,6 +105,7 @@ module Sbmt
105
105
  RedisJob.deserialize!(result)
106
106
  rescue => ex
107
107
  logger.log_error("error while fetching redis job: #{ex.message}")
108
+ nil
108
109
  end
109
110
 
110
111
  def redis_block_timeout
@@ -28,6 +28,7 @@ module Sbmt
28
28
 
29
29
  if task == BREAK
30
30
  self.stopped = true
31
+ logger.log_debug("#{name}: received BREAK signal, stopping thread pool")
31
32
  return
32
33
  end
33
34
 
@@ -47,35 +48,54 @@ module Sbmt
47
48
  end
48
49
  end
49
50
 
50
- logger.log_info("#{name}: threads started")
51
+ logger.log_info("#{name}: thread pool started with #{concurrency} threads")
51
52
 
52
53
  raise result if result.is_a?(Exception)
53
54
  end
54
55
 
55
56
  def stop
57
+ logger.log_info("#{name}: stopping thread pool")
56
58
  self.stopped = true
57
59
 
58
60
  threads.map(&:join) if start_async
59
61
  ensure
60
62
  stop_threads
63
+ logger.log_info("#{name}: thread pool stopped")
61
64
  end
62
65
 
63
66
  def running?
64
- return false if stopped
67
+ if stopped
68
+ logger.log_info("#{name}: checking if running: stopped")
69
+ return false
70
+ end
65
71
 
66
72
  true
67
73
  end
68
74
 
69
75
  def alive?(timeout)
70
- return false if stopped
76
+ if stopped
77
+ logger.log_info("#{name}: checking if alive: stopped")
78
+ return false
79
+ end
71
80
 
72
81
  deadline = Time.current - timeout
73
- threads.all? do |thread|
82
+ alive_threads = threads.select do |thread|
83
+ next false unless thread.alive?
84
+
74
85
  last_active_at = last_active_at(thread)
75
- return false unless last_active_at
86
+ if last_active_at
87
+ deadline < last_active_at
88
+ else
89
+ false
90
+ end
91
+ end
76
92
 
77
- deadline < last_active_at
93
+ unless alive_threads.length == concurrency
94
+ logger.log_info("#{name}: checking if alive: false, only #{alive_threads.length}/#{concurrency} threads alive")
95
+ return false
78
96
  end
97
+
98
+ true
79
99
  end
80
100
 
81
101
  private
@@ -98,6 +118,7 @@ module Sbmt
98
118
  exception = nil
99
119
 
100
120
  in_threads do |worker_num|
121
+ logger.log_debug("#{name}: worker #{worker_num} starting")
101
122
  self.worker_number = worker_num
102
123
  # We don't want to start all threads at the same time
103
124
  sleep(rand * (worker_num + 1)) if random_startup_delay
@@ -113,30 +134,39 @@ module Sbmt
113
134
  begin
114
135
  yield task
115
136
  rescue Exception => e # rubocop:disable Lint/RescueException
137
+ logger.log_error("#{name}: worker #{worker_num} caught exception in task: #{e.class} - #{e.message}")
116
138
  exception = e
117
139
  end
118
140
  end
119
141
  end
120
142
 
143
+ logger.log_info("#{name}: run_threads completed, exception: #{exception&.inspect}")
121
144
  exception
122
145
  end
123
146
 
124
147
  def in_threads
125
148
  Thread.handle_interrupt(Exception => :never) do
126
149
  Thread.handle_interrupt(Exception => :immediate) do
150
+ logger.log_info("#{name}: creating #{concurrency} threads")
127
151
  concurrency.times do |i|
128
152
  threads << Thread.new { yield(i) }
129
153
  end
130
154
  threads.map(&:value) unless start_async
131
155
  end
132
156
  ensure
157
+ logger.log_debug("#{name}: in_threads ensuring stop_threads")
133
158
  stop_threads unless start_async
134
159
  end
135
160
  end
136
161
 
137
162
  def stop_threads
138
- threads.each(&:kill)
163
+ logger.log_debug("#{name}: stop_threads called, clearing #{threads.length} threads")
164
+ threads.each do |thread|
165
+ logger.log_debug("#{name}: killing thread #{thread.object_id}")
166
+ thread.kill
167
+ end
139
168
  threads.clear
169
+ logger.log_debug("#{name}: stop_threads completed")
140
170
  end
141
171
 
142
172
  def worker_number=(num)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sbmt
4
4
  module Outbox
5
- VERSION = "7.0.1"
5
+ VERSION = "7.0.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sbmt-outbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.1
4
+ version: 7.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sbermarket Ruby-Platform Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-27 00:00:00.000000000 Z
11
+ date: 2026-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool
@@ -627,7 +627,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
627
627
  - !ruby/object:Gem::Version
628
628
  version: '0'
629
629
  requirements: []
630
- rubygems_version: 3.5.22
630
+ rubygems_version: 3.3.7
631
631
  signing_key:
632
632
  specification_version: 4
633
633
  summary: Outbox service