sbmt-outbox 6.15.0 → 6.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -0
- data/app/jobs/sbmt/outbox/base_delete_stale_items_job.rb +57 -46
- data/app/models/sbmt/outbox/base_item_config.rb +4 -0
- data/lib/sbmt/outbox/engine.rb +1 -0
- data/lib/sbmt/outbox/v2/poller.rb +4 -3
- data/lib/sbmt/outbox/version.rb +1 -1
- data/lib/sbmt/outbox.rb +4 -0
- 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: ad4860726f28975383df9bf2d522e994f80644dfe9a3d3476cf517ee90443bf4
|
4
|
+
data.tar.gz: 16925e325aeab7dc040a46940e34c7318f526bb39c633fb3dd7be53d187d6e96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4a2fb0a085133946fa0a741545ab76f173b096d2c817deaac12511207f695e93e15c40fdda475ac747ccaf79c8bb9895c73ce5594afc7e71db7fc0a4427ebb4
|
7
|
+
data.tar.gz: 6baa8c4f01d13859ba3879de9c8f9a749251331045b0df35aee4a0dc52660e136a46653db416e5d9eaf78711864a79da6e3f788711b5474b46be1f62cb505d15
|
data/README.md
CHANGED
@@ -273,6 +273,7 @@ default: &default
|
|
273
273
|
delivered_min_retention_period: PT1H #optional, default: PT1H, for statuses: delivered, retention period for delivered items, https://en.wikipedia.org/wiki/ISO_8601#Durations
|
274
274
|
deletion_batch_size: 1_000 #optional, default: 1_000
|
275
275
|
deletion_sleep_time: 0.5 #optional, default: 0.5
|
276
|
+
deletion_time_window: PT4H #optional, default: PT4H, for statuses: delivered, retention period for delivered items, https://en.wikipedia.org/wiki/ISO_8601#Durations
|
276
277
|
max_retries: 3 # default 0, the number of retries before the item will be marked as failed
|
277
278
|
strict_order: false # optional, default
|
278
279
|
transports: # transports section
|
@@ -353,6 +354,7 @@ inbox_items: # inbox items section
|
|
353
354
|
delivered_min_retention_period: PT1H #optional, default: PT1H, for statuses: delivered, retention period for delivered items, https://en.wikipedia.org/wiki/ISO_8601#Durations
|
354
355
|
deletion_batch_size: 1_000 #optional, default: 1_000
|
355
356
|
deletion_sleep_time: 0.5 #optional, default: 0.5
|
357
|
+
deletion_time_window: PT4H #optional, default: PT4H, for statuses: delivered, retention period for delivered items, https://en.wikipedia.org/wiki/ISO_8601#Durations
|
356
358
|
max_retries: 3 # default 0, the number of retries before the item will be marked as failed
|
357
359
|
transports: # transports section
|
358
360
|
import_order: # underscored transport class name
|
@@ -493,6 +495,7 @@ You can wrap item processing within middlewares. There are three types:
|
|
493
495
|
- server middlewares – triggered inside a daemon; divided into two types:
|
494
496
|
- batch middlewares – executed alongside a batch being fetched from the database
|
495
497
|
- item middlewares – execute alongside an item during processing
|
498
|
+
- polling middlewares - execute with element during pooling
|
496
499
|
|
497
500
|
The order of execution depends on the order specified in the outbox configuration:
|
498
501
|
|
@@ -580,6 +583,25 @@ class MyItemMiddleware
|
|
580
583
|
end
|
581
584
|
```
|
582
585
|
|
586
|
+
Example of an polling middleware:
|
587
|
+
|
588
|
+
```ruby
|
589
|
+
# config/initializers/outbox.rb
|
590
|
+
Rails.application.config.outbox.tap do |config|
|
591
|
+
config.polling_item_middlewares.push(
|
592
|
+
'MyItemMiddleware'
|
593
|
+
)
|
594
|
+
end
|
595
|
+
|
596
|
+
# my_create_polling_middleware.rb
|
597
|
+
class MyPollingItemMiddleware
|
598
|
+
def call(item)
|
599
|
+
# your code
|
600
|
+
yield
|
601
|
+
# your code
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
583
605
|
## Tracing
|
584
606
|
|
585
607
|
The gem is optionally integrated with OpenTelemetry. If your main application has `opentelemetry-*` gems, the tracing will be configured automatically.
|
@@ -102,45 +102,50 @@ module Sbmt
|
|
102
102
|
# SELECT "items"."id"
|
103
103
|
# FROM "items"
|
104
104
|
# WHERE (
|
105
|
-
# "items"."status"
|
105
|
+
# "items"."status" IN (2) AND "items"."created_at" BETWEEN "2025-01-29 12:18:32.917836" AND "2025-01-29 12:18:32.927596" LIMIT 1000
|
106
106
|
# )
|
107
|
-
# LIMIT 1000
|
108
107
|
# )
|
109
108
|
def postgres_delete_in_batches(waterline_failed, waterline_delivered)
|
110
|
-
table = item_class.arel_table
|
111
|
-
|
112
109
|
status_delivered = item_class.statuses[:delivered]
|
113
110
|
status_failed_discarded = item_class.statuses.values_at(:failed, :discarded)
|
114
111
|
|
115
|
-
|
116
|
-
|
112
|
+
delete_items_in_batches_with_between(waterline_delivered, status_delivered)
|
113
|
+
delete_items_in_batches_with_between(waterline_failed, status_failed_discarded)
|
117
114
|
end
|
118
115
|
|
119
|
-
def
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
delete_statement = Arel::Nodes::DeleteStatement.new
|
126
|
-
delete_statement.relation = table
|
127
|
-
delete_statement.wheres = [table[:id].in(subquery)]
|
116
|
+
def delete_items_in_batches_with_between(waterline, statuses)
|
117
|
+
table = item_class.arel_table
|
118
|
+
batch_size = item_class.config.deletion_batch_size
|
119
|
+
time_window = item_class.config.deletion_time_window
|
120
|
+
min_date = item_class.where(table[:status].in(statuses)).minimum(:created_at)
|
128
121
|
deleted_count = nil
|
129
122
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
.
|
136
|
-
|
123
|
+
while min_date && min_date < waterline
|
124
|
+
max_date = [min_date + time_window, waterline].min
|
125
|
+
|
126
|
+
loop do
|
127
|
+
subquery = table
|
128
|
+
.project(table[:id])
|
129
|
+
.where(table[:status].in(statuses))
|
130
|
+
.where(table[:created_at].between(min_date..max_date))
|
131
|
+
.take(batch_size)
|
132
|
+
|
133
|
+
delete_statement = Arel::Nodes::DeleteStatement.new
|
134
|
+
delete_statement.relation = table
|
135
|
+
delete_statement.wheres = [table[:id].in(subquery)]
|
136
|
+
|
137
|
+
track_deleted_latency do
|
138
|
+
deleted_count = item_class.connection.execute(delete_statement.to_sql).cmd_tuples
|
139
|
+
end
|
137
140
|
|
138
|
-
|
141
|
+
track_deleted_counter(deleted_count)
|
139
142
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
143
|
+
logger.log_info("Deleted #{deleted_count} #{box_type} items for #{box_name} between #{min_date} and #{max_date}")
|
144
|
+
break if deleted_count < batch_size
|
145
|
+
lock_timer.checkpoint!
|
146
|
+
sleep(item_class.config.deletion_sleep_time) if deleted_count > 0
|
147
|
+
end
|
148
|
+
min_date = max_date
|
144
149
|
end
|
145
150
|
end
|
146
151
|
|
@@ -154,37 +159,43 @@ module Sbmt
|
|
154
159
|
# This approach doesn't require a subquery, making it more straightforward.
|
155
160
|
#
|
156
161
|
# Example SQL generated for deletion:
|
157
|
-
# DELETE FROM
|
162
|
+
# DELETE FROM "items"
|
158
163
|
# WHERE (
|
159
|
-
#
|
164
|
+
# "items"."status" IN (2) AND "items"."created_at" BETWEEN "2024-12-29 18:34:25.369234" AND "2024-12-29 22:34:25.369234" LIMIT 1000
|
160
165
|
# )
|
161
|
-
# LIMIT 1000
|
162
166
|
def mysql_delete_in_batches(waterline_failed, waterline_delivered)
|
163
167
|
status_delivered = item_class.statuses[:delivered]
|
164
168
|
status_failed_discarded = [item_class.statuses.values_at(:failed, :discarded)]
|
165
169
|
|
166
|
-
|
167
|
-
|
168
|
-
)
|
169
|
-
delete_items_in_batches_mysql(
|
170
|
-
item_class.where(status: status_failed_discarded).where(created_at: ...waterline_failed)
|
171
|
-
)
|
170
|
+
delete_items_in_batches_with_between_mysql(waterline_delivered, status_delivered)
|
171
|
+
delete_items_in_batches_with_between_mysql(waterline_failed, status_failed_discarded)
|
172
172
|
end
|
173
173
|
|
174
|
-
def
|
174
|
+
def delete_items_in_batches_with_between_mysql(waterline, statuses)
|
175
|
+
batch_size = item_class.config.deletion_batch_size
|
176
|
+
time_window = item_class.config.deletion_time_window
|
177
|
+
min_date = item_class.where(status: statuses).minimum(:created_at)
|
175
178
|
deleted_count = nil
|
176
179
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
180
|
+
while min_date && min_date < waterline
|
181
|
+
max_date = [min_date + time_window, waterline].min
|
182
|
+
|
183
|
+
loop do
|
184
|
+
track_deleted_latency do
|
185
|
+
deleted_count = item_class
|
186
|
+
.where(status: statuses, created_at: min_date..max_date)
|
187
|
+
.limit(batch_size)
|
188
|
+
.delete_all
|
189
|
+
end
|
181
190
|
|
182
|
-
|
191
|
+
track_deleted_counter(deleted_count)
|
183
192
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
193
|
+
logger.log_info("Deleted #{deleted_count} #{box_type} items for #{box_name} between #{min_date} and #{max_date}")
|
194
|
+
break if deleted_count < batch_size
|
195
|
+
lock_timer.checkpoint!
|
196
|
+
sleep(item_class.config.deletion_sleep_time) if deleted_count > 0
|
197
|
+
end
|
198
|
+
min_date = max_date
|
188
199
|
end
|
189
200
|
end
|
190
201
|
|
@@ -60,6 +60,10 @@ module Sbmt
|
|
60
60
|
@delivered_min_retention_period ||= ActiveSupport::Duration.parse(options[:delivered_min_retention_period] || "PT1H")
|
61
61
|
end
|
62
62
|
|
63
|
+
def deletion_time_window
|
64
|
+
@deletion_time_window ||= ActiveSupport::Duration.parse(options[:deletion_time_window] || "PT4H")
|
65
|
+
end
|
66
|
+
|
63
67
|
def max_retries
|
64
68
|
@max_retries ||= (options[:max_retries] || 0).to_i
|
65
69
|
end
|
data/lib/sbmt/outbox/engine.rb
CHANGED
@@ -64,6 +64,7 @@ module Sbmt
|
|
64
64
|
c.item_process_middlewares = []
|
65
65
|
c.create_item_middlewares = []
|
66
66
|
c.create_batch_middlewares = []
|
67
|
+
c.polling_item_middlewares = []
|
67
68
|
|
68
69
|
if defined?(::Sentry)
|
69
70
|
c.batch_process_middlewares.push("Sbmt::Outbox::Middleware::Sentry::TracingBatchProcessMiddleware")
|
@@ -10,9 +10,9 @@ module Sbmt
|
|
10
10
|
module Outbox
|
11
11
|
module V2
|
12
12
|
class Poller < BoxProcessor
|
13
|
-
delegate :poller_config, :logger, to: "Sbmt::Outbox"
|
13
|
+
delegate :poller_config, :polling_item_middlewares, :logger, to: "Sbmt::Outbox"
|
14
14
|
delegate :box_worker, to: "Yabeda"
|
15
|
-
attr_reader :partitions_count, :lock_timeout, :regular_items_batch_size, :retryable_items_batch_size, :max_buffer_size, :max_batch_size, :throttler
|
15
|
+
attr_reader :partitions_count, :lock_timeout, :regular_items_batch_size, :retryable_items_batch_size, :max_buffer_size, :max_batch_size, :throttler, :middleware_builder
|
16
16
|
|
17
17
|
def initialize(
|
18
18
|
boxes,
|
@@ -35,6 +35,7 @@ module Sbmt
|
|
35
35
|
super(boxes: boxes, threads_count: threads_count || poller_config.threads_count, name: "poller", redis: redis)
|
36
36
|
|
37
37
|
@throttler = PollThrottler.build(throttler_tactic || poller_config.tactic || "default", self.redis, poller_config)
|
38
|
+
@middleware_builder = Middleware::Builder.new(polling_item_middlewares)
|
38
39
|
end
|
39
40
|
|
40
41
|
def throttle(worker_number, poll_task, result)
|
@@ -42,7 +43,7 @@ module Sbmt
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def process_task(_worker_number, task)
|
45
|
-
poll(task)
|
46
|
+
middleware_builder.call(task) { poll(task) }
|
46
47
|
end
|
47
48
|
|
48
49
|
private
|
data/lib/sbmt/outbox/version.rb
CHANGED
data/lib/sbmt/outbox.rb
CHANGED
@@ -170,5 +170,9 @@ module Sbmt
|
|
170
170
|
def create_batch_middlewares
|
171
171
|
@create_batch_middlewares ||= config.create_batch_middlewares.map(&:constantize)
|
172
172
|
end
|
173
|
+
|
174
|
+
def polling_item_middlewares
|
175
|
+
@polling_item_middlewares ||= config.polling_item_middlewares.map(&:constantize)
|
176
|
+
end
|
173
177
|
end
|
174
178
|
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: 6.
|
4
|
+
version: 6.17.0
|
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
|
+
date: 2025-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|
@@ -629,7 +629,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
629
629
|
- !ruby/object:Gem::Version
|
630
630
|
version: '0'
|
631
631
|
requirements: []
|
632
|
-
rubygems_version: 3.
|
632
|
+
rubygems_version: 3.5.21
|
633
633
|
signing_key:
|
634
634
|
specification_version: 4
|
635
635
|
summary: Outbox service
|