sbmt-outbox 6.11.0 → 6.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e410e7a4507b5470d10986ac8d9949c4c9460a6773a97063b0287d9e777c4d9
4
- data.tar.gz: ef17da6564faeb5f17cee5073cbecebeca083a555a9c046d0794f210f1a1b414
3
+ metadata.gz: 47ab689de26112df54d99c701fad3c6f492096173de831fc134318afa91c6662
4
+ data.tar.gz: b11e20111becd1d58cfca72a98d62adeb1b7573c0c093081b086331d90cc7b04
5
5
  SHA512:
6
- metadata.gz: 1e5fa1c14bab5bee90e4f5c664c22cdc5e8bea8fcfca391a3eb967ddf1c3b57b96080ab147cf366e5cbc9d2add8d486b8b2ca5ebfe8c9e96d0e989ab27ae685d
7
- data.tar.gz: c47678da77b2f59787dd64cd162974b7cd4f6954fc2fafae54614b930fcf44068498491aaddae77d99b86f50dc67637b2090ae7d155a844ca193b9f011cf7189
6
+ metadata.gz: 6df07282a29bf4468399747b2443965c35bcbc8590bcb5b6613fdad8c255cad9296dfdf9662399617f15ce39cd589e6f022a4da3c8611ee9d712714a10d36fcf
7
+ data.tar.gz: 0f14e1ed6c00ed9fd2c4fd4e9ff225330b507f0a9fbef4c39a32e86eeb0a7bebbf194a2b4040f5b4e2813b8438f27bb78a9a024b16784397ff0fe712143b505f
data/README.md CHANGED
@@ -267,7 +267,8 @@ default: &default
267
267
  outbox_items: # outbox items section
268
268
  my_outbox_item: # underscored model class name
269
269
  owner: my_outbox_item_team # optional, used in Yabeda metrics
270
- retention: P1W # retention period, https://en.wikipedia.org/wiki/ISO_8601#Durations
270
+ retention: P1W # for statuses: failed and discarded, retention period, https://en.wikipedia.org/wiki/ISO_8601#Durations
271
+ retention_delivered_items: PT6H # for statuses: delivered, retention period for delivered items, https://en.wikipedia.org/wiki/ISO_8601#Durations
271
272
  max_retries: 3 # default 0, the number of retries before the item will be marked as failed
272
273
  strict_order: false # optional, default
273
274
  transports: # transports section
@@ -342,7 +343,8 @@ end
342
343
  inbox_items: # inbox items section
343
344
  my_inbox_item: # underscored model class name
344
345
  owner: my_inbox_item_team # optional, used in Yabeda metrics
345
- retention: P1W # retention period, https://en.wikipedia.org/wiki/ISO_8601#Durations
346
+ retention: P1W # for statuses: failed and discarded, retention period, https://en.wikipedia.org/wiki/ISO_8601#Durations
347
+ retention_delivered_items: PT6H # for statuses: delivered, retention period for delivered items, https://en.wikipedia.org/wiki/ISO_8601#Durations
346
348
  max_retries: 3 # default 0, the number of retries before the item will be marked as failed
347
349
  transports: # transports section
348
350
  import_order: # underscored transport class name
@@ -13,7 +13,8 @@ module Sbmt
13
13
  class << self
14
14
  def enqueue
15
15
  item_classes.each do |item_class|
16
- perform_later(item_class.to_s)
16
+ delay = rand(15).seconds
17
+ set(wait: delay).perform_later(item_class.to_s)
17
18
  end
18
19
  end
19
20
 
@@ -41,12 +42,13 @@ module Sbmt
41
42
 
42
43
  lock_manager.lock("#{self.class.name}:#{item_class_name}:lock", LOCK_TTL) do |locked|
43
44
  if locked
44
- duration = item_class.config.retention
45
+ duration_failed = item_class.config.retention
46
+ duration_delivered = item_class.config.retention_delivered_items
45
47
 
46
- validate_retention!(duration)
48
+ validate_retention!(duration_failed)
47
49
 
48
50
  logger.with_tags(box_type: box_type, box_name: box_name) do
49
- delete_stale_items(Time.current - duration)
51
+ delete_stale_items(Time.current - duration_failed, Time.current - duration_delivered)
50
52
  end
51
53
  else
52
54
  logger.log_info("Failed to acquire lock #{self.class.name}:#{item_class_name}")
@@ -58,25 +60,25 @@ module Sbmt
58
60
 
59
61
  private
60
62
 
61
- def validate_retention!(duration)
62
- return if duration >= MIN_RETENTION_PERIOD
63
+ def validate_retention!(duration_failed)
64
+ return if duration_failed >= MIN_RETENTION_PERIOD
63
65
 
64
66
  raise "Retention period for #{box_name} must be longer than #{MIN_RETENTION_PERIOD.inspect}"
65
67
  end
66
68
 
67
- def delete_stale_items(waterline)
68
- logger.log_info("Start deleting #{box_type} items for #{box_name} older than #{waterline}")
69
+ def delete_stale_items(waterline_failed, waterline_delivered)
70
+ logger.log_info("Start deleting #{box_type} items for #{box_name} older than: failed and discarded items #{waterline_failed} and delivered items #{waterline_delivered}")
69
71
 
70
72
  case database_type
71
73
  when :postgresql
72
- postgres_delete_in_batches(waterline)
74
+ postgres_delete_in_batches(waterline_failed, waterline_delivered)
73
75
  when :mysql
74
- mysql_delete_in_batches(waterline)
76
+ mysql_delete_in_batches(waterline_failed, waterline_delivered)
75
77
  else
76
78
  raise "Unsupported database type"
77
79
  end
78
80
 
79
- logger.log_info("Successfully deleted #{box_type} items for #{box_name} older than #{waterline}")
81
+ logger.log_info("Successfully deleted #{box_type} items for #{box_name} older than: failed and discarded items #{waterline_failed} and delivered items #{waterline_delivered}")
80
82
  end
81
83
 
82
84
  # Deletes stale items from PostgreSQL database in batches
@@ -90,12 +92,22 @@ module Sbmt
90
92
  # WHERE "items"."id" IN (
91
93
  # SELECT "items"."id"
92
94
  # FROM "items"
93
- # WHERE "items"."created_at" < '2023-05-01 00:00:00'
95
+ # WHERE (
96
+ # "items"."status" = 1 AND "items"."created_at" < '2023-05-01 00:00:00'
97
+ # )
94
98
  # LIMIT 1000
95
99
  # )
96
- def postgres_delete_in_batches(waterline)
100
+ def postgres_delete_in_batches(waterline_failed, waterline_delivered)
97
101
  table = item_class.arel_table
98
- condition = table[:created_at].lt(waterline)
102
+
103
+ status_delivered = item_class.statuses[:delivered]
104
+ status_failed_discarded = item_class.statuses.values_at(:failed, :discarded)
105
+
106
+ delete_items_in_batches(table, table[:status].eq(status_delivered).and(table[:created_at].lt(waterline_delivered)))
107
+ delete_items_in_batches(table, table[:status].in(status_failed_discarded).and(table[:created_at].lt(waterline_failed)))
108
+ end
109
+
110
+ def delete_items_in_batches(table, condition)
99
111
  subquery = table
100
112
  .project(table[:id])
101
113
  .where(condition)
@@ -104,12 +116,17 @@ module Sbmt
104
116
  delete_statement = Arel::Nodes::DeleteStatement.new
105
117
  delete_statement.relation = table
106
118
  delete_statement.wheres = [table[:id].in(subquery)]
119
+ deleted_count = nil
107
120
 
108
121
  loop do
109
- deleted_count = item_class
110
- .connection
111
- .execute(delete_statement.to_sql)
112
- .cmd_tuples
122
+ track_deleted_latency do
123
+ deleted_count = item_class
124
+ .connection
125
+ .execute(delete_statement.to_sql)
126
+ .cmd_tuples
127
+ end
128
+
129
+ track_deleted_counter(deleted_count)
113
130
 
114
131
  logger.log_info("Deleted #{deleted_count} #{box_type} items for #{box_name} items")
115
132
  break if deleted_count == 0
@@ -129,14 +146,31 @@ module Sbmt
129
146
  #
130
147
  # Example SQL generated for deletion:
131
148
  # DELETE FROM `items`
132
- # WHERE `items`.`created_at` < '2023-05-01 00:00:00'
149
+ # WHERE (
150
+ # `items`.`status` = 1 AND `items`.`created_at` < '2023-05-01 00:00:00'
151
+ # )
133
152
  # LIMIT 1000
134
- def mysql_delete_in_batches(waterline)
153
+ def mysql_delete_in_batches(waterline_failed, waterline_delivered)
154
+ status_delivered = item_class.statuses[:delivered]
155
+ status_failed_discarded = [item_class.statuses.values_at(:failed, :discarded)]
156
+
157
+ delete_items_in_batches_mysql(
158
+ item_class.where(status: status_delivered, created_at: ...waterline_delivered)
159
+ )
160
+ delete_items_in_batches_mysql(
161
+ item_class.where(status: status_failed_discarded).where(created_at: ...waterline_failed)
162
+ )
163
+ end
164
+
165
+ def delete_items_in_batches_mysql(query)
166
+ deleted_count = nil
167
+
135
168
  loop do
136
- deleted_count = item_class
137
- .where(created_at: ...waterline)
138
- .limit(BATCH_SIZE)
139
- .delete_all
169
+ track_deleted_latency do
170
+ deleted_count = query.limit(BATCH_SIZE).delete_all
171
+ end
172
+
173
+ track_deleted_counter(deleted_count)
140
174
 
141
175
  logger.log_info("Deleted #{deleted_count} #{box_type} items for #{box_name} items")
142
176
  break if deleted_count == 0
@@ -156,6 +190,22 @@ module Sbmt
156
190
  :unknown
157
191
  end
158
192
  end
193
+
194
+ def track_deleted_counter(deleted_count)
195
+ ::Yabeda
196
+ .outbox
197
+ .deleted_counter
198
+ .increment({box_type: box_type, box_name: box_name}, by: deleted_count)
199
+ end
200
+
201
+ def track_deleted_latency
202
+ ::Yabeda
203
+ .outbox
204
+ .delete_latency
205
+ .measure({box_type: box_type, box_name: box_name}) do
206
+ yield
207
+ end
208
+ end
159
209
  end
160
210
  end
161
211
  end
@@ -37,6 +37,13 @@ module Sbmt
37
37
  @retention ||= ActiveSupport::Duration.parse(options[:retention] || "P1W")
38
38
  end
39
39
 
40
+ def retention_delivered_items
41
+ @retention_delivered_items ||= begin
42
+ value = options[:retention_delivered_items] || retention
43
+ value.is_a?(String) ? ActiveSupport::Duration.parse(value) : value
44
+ end
45
+ end
46
+
40
47
  def max_retries
41
48
  @max_retries ||= (options[:max_retries] || 0).to_i
42
49
  end
@@ -43,6 +43,16 @@ Yabeda.configure do
43
43
  unit: :seconds,
44
44
  buckets: [0.5, 1, 2.5, 5, 10, 15, 20, 30, 45, 60, 300].freeze,
45
45
  comment: "A histogram outbox process latency"
46
+
47
+ histogram :delete_latency,
48
+ tags: %i[box_type box_name],
49
+ unit: :seconds,
50
+ buckets: [0.005, 0.01, 0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 20, 30].freeze,
51
+ comment: "A histogram for outbox/inbox deletion latency"
52
+
53
+ counter :deleted_counter,
54
+ tags: %i[box_type box_name],
55
+ comment: "A counter for the number of deleted outbox/inbox items"
46
56
  end
47
57
 
48
58
  group :box_worker do
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sbmt
4
4
  module Outbox
5
- VERSION = "6.11.0"
5
+ VERSION = "6.13.0"
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: 6.11.0
4
+ version: 6.13.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: 2024-12-27 00:00:00.000000000 Z
11
+ date: 2025-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool