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 +4 -4
- data/app/jobs/sbmt/outbox/base_delete_stale_items_job.rb +1 -1
- data/app/models/sbmt/outbox/base_item.rb +2 -2
- data/lib/sbmt/outbox/error_tracker.rb +2 -4
- data/lib/sbmt/outbox/v2/box_processor.rb +50 -5
- data/lib/sbmt/outbox/v2/processor.rb +1 -0
- data/lib/sbmt/outbox/v2/thread_pool.rb +37 -7
- data/lib/sbmt/outbox/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 751fcef19c53ff5a38d589d0dac8d5a1007335faec5fd710b1fb1e69f2ea25ef
|
|
4
|
+
data.tar.gz: a12ec44b3c6774b0ff72503f60a645ba3b2a8d52ed28a70d85e51041ef17e2d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ecfeecc1840db0694c89a950e35f30fe917b7ce7dbf7ad522f65c27ac1f0d3dceda550ff879b246683d795d5c6add96e9ea785cab89e0ee2dbbdec32f59c8746
|
|
7
|
+
data.tar.gz: 66c2b31e030041587f6e88ee2e35ae960f4e0a274128d10f770856b44302bdb805c5f89c0bc159d75b5349e9ecb75c0f54a71e676ee082fc5d09223dc060a78c
|
|
@@ -5,10 +5,8 @@ module Sbmt
|
|
|
5
5
|
class ErrorTracker
|
|
6
6
|
class << self
|
|
7
7
|
def error(message, params = {})
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
logger.log_debug("#{worker_name}: checking if alive with timeout #{timeout}")
|
|
81
110
|
|
|
82
|
-
|
|
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
|
|
|
@@ -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}:
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
82
|
+
alive_threads = threads.select do |thread|
|
|
83
|
+
next false unless thread.alive?
|
|
84
|
+
|
|
74
85
|
last_active_at = last_active_at(thread)
|
|
75
|
-
|
|
86
|
+
if last_active_at
|
|
87
|
+
deadline < last_active_at
|
|
88
|
+
else
|
|
89
|
+
false
|
|
90
|
+
end
|
|
91
|
+
end
|
|
76
92
|
|
|
77
|
-
|
|
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.
|
|
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)
|
data/lib/sbmt/outbox/version.rb
CHANGED
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.
|
|
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:
|
|
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.
|
|
630
|
+
rubygems_version: 3.3.7
|
|
631
631
|
signing_key:
|
|
632
632
|
specification_version: 4
|
|
633
633
|
summary: Outbox service
|