Skip to content

Commit 4868db8

Browse files
committed
Merge branch 'feat/DEX-2721/configure-BaseDeleteStaleItemsJob' into 'master'
[DEX-2721] feat: add options for configure BaseDeleteStaleItemsJob Closes DEX-2721 See merge request nstmrt/rubygems/outbox!112
2 parents 1e636f0 + 08787c1 commit 4868db8

File tree

8 files changed

+100
-35
lines changed

8 files changed

+100
-35
lines changed

Appraisals

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ versions_map.each do |rails_version, ruby_versions|
1919

2020
appraise "rails-#{rails_version}" do
2121
gem "rails", "~> #{rails_version}.0"
22+
gem "concurrent-ruby", "1.3.4" if rails_version.to_f < 7.1
2223
end
2324
end
2425
end

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1313

1414
### Fixed
1515

16+
## [6.14.0] - 2025-01-20
17+
18+
### Added
19+
20+
- Added options for configuring jobs to remove old items:
21+
- `deletion_batch_size` - default 1_000
22+
- `deletion_sleep_time` - pauses between `batch_size`
23+
- `min_retention_period` - for items with statuses: `failed` and `discarded`
24+
- `delivered_min_retention_period` - for items with statuses: `delivered`
25+
1626
## [6.13.1] - 2025-01-15
1727

1828
### Fixed

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,12 @@ default: &default
267267
outbox_items: # outbox items section
268268
my_outbox_item: # underscored model class name
269269
owner: my_outbox_item_team # optional, used in Yabeda metrics
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
270+
retention: P1W #optional, default: P1W, for statuses: failed and discarded, retention period, https://en.wikipedia.org/wiki/ISO_8601#Durations
271+
min_retention_period: P1D #optional, default: P1D, for statuses: failed and discarded, retention period, https://en.wikipedia.org/wiki/ISO_8601#Durations
272+
retention_delivered_items: PT6H #optional, default: P1W, for statuses: delivered, retention period for delivered items, https://en.wikipedia.org/wiki/ISO_8601#Durations
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+
deletion_batch_size: 1_000 #optional, default: 1_000
275+
deletion_sleep_time: 0.5 #optional, default: 0.5
272276
max_retries: 3 # default 0, the number of retries before the item will be marked as failed
273277
strict_order: false # optional, default
274278
transports: # transports section
@@ -343,8 +347,12 @@ end
343347
inbox_items: # inbox items section
344348
my_inbox_item: # underscored model class name
345349
owner: my_inbox_item_team # optional, used in Yabeda metrics
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
350+
retention: P1W #optional, default: P1W, for statuses: failed and discarded, retention period, https://en.wikipedia.org/wiki/ISO_8601#Durations
351+
min_retention_period: P1D #optional, default: P1D, for statuses: failed and discarded, retention period, https://en.wikipedia.org/wiki/ISO_8601#Durations
352+
retention_delivered_items: PT6H #optional, default: P1W, for statuses: delivered, retention period for delivered items, https://en.wikipedia.org/wiki/ISO_8601#Durations
353+
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+
deletion_batch_size: 1_000 #optional, default: 1_000
355+
deletion_sleep_time: 0.5 #optional, default: 0.5
348356
max_retries: 3 # default 0, the number of retries before the item will be marked as failed
349357
transports: # transports section
350358
import_order: # underscored transport class name

app/jobs/sbmt/outbox/base_delete_stale_items_job.rb

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
module Sbmt
66
module Outbox
77
class BaseDeleteStaleItemsJob < Outbox.active_job_base_class
8-
MIN_RETENTION_PERIOD = 1.day
98
LOCK_TTL = 10_800_000
10-
BATCH_SIZE = 1_000
11-
SLEEP_TIME = 0.5
129

1310
class << self
1411
def enqueue
@@ -45,7 +42,7 @@ def perform(item_class_name)
4542
duration_failed = item_class.config.retention
4643
duration_delivered = item_class.config.retention_delivered_items
4744

48-
validate_retention!(duration_failed)
45+
validate_retention!(duration_delivered, duration_failed)
4946

5047
logger.with_tags(box_type: box_type, box_name: box_name) do
5148
delete_stale_items(Time.current - duration_failed, Time.current - duration_delivered)
@@ -60,10 +57,22 @@ def perform(item_class_name)
6057

6158
private
6259

63-
def validate_retention!(duration_failed)
64-
return if duration_failed >= MIN_RETENTION_PERIOD
60+
def validate_retention!(duration_delivered, duration_failed)
61+
validate_retention_for!(
62+
duration: duration_delivered,
63+
min_period: item_class.config.delivered_min_retention_period,
64+
error_message: "Retention period for #{box_name} must be longer than #{item_class.config.delivered_min_retention_period.inspect}"
65+
)
66+
67+
validate_retention_for!(
68+
duration: duration_failed,
69+
min_period: item_class.config.min_retention_period,
70+
error_message: "Retention period for #{box_name} must be longer than #{item_class.config.min_retention_period.inspect}"
71+
)
72+
end
6573

66-
raise "Retention period for #{box_name} must be longer than #{MIN_RETENTION_PERIOD.inspect}"
74+
def validate_retention_for!(duration:, min_period:, error_message:)
75+
raise error_message if duration < min_period
6776
end
6877

6978
def delete_stale_items(waterline_failed, waterline_delivered)
@@ -111,7 +120,7 @@ def delete_items_in_batches(table, condition)
111120
subquery = table
112121
.project(table[:id])
113122
.where(condition)
114-
.take(BATCH_SIZE)
123+
.take(item_class.config.deletion_batch_size)
115124

116125
delete_statement = Arel::Nodes::DeleteStatement.new
117126
delete_statement.relation = table
@@ -131,7 +140,7 @@ def delete_items_in_batches(table, condition)
131140
logger.log_info("Deleted #{deleted_count} #{box_type} items for #{box_name} items")
132141
break if deleted_count == 0
133142
lock_timer.checkpoint!
134-
sleep(SLEEP_TIME)
143+
sleep(item_class.config.deletion_sleep_time)
135144
end
136145
end
137146

@@ -167,15 +176,15 @@ def delete_items_in_batches_mysql(query)
167176

168177
loop do
169178
track_deleted_latency do
170-
deleted_count = query.limit(BATCH_SIZE).delete_all
179+
deleted_count = query.limit(item_class.config.deletion_batch_size).delete_all
171180
end
172181

173182
track_deleted_counter(deleted_count)
174183

175184
logger.log_info("Deleted #{deleted_count} #{box_type} items for #{box_name} items")
176185
break if deleted_count == 0
177186
lock_timer.checkpoint!
178-
sleep(SLEEP_TIME)
187+
sleep(item_class.config.deletion_sleep_time)
179188
end
180189
end
181190

app/models/sbmt/outbox/base_item_config.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@ def retention_delivered_items
4444
end
4545
end
4646

47+
def deletion_batch_size
48+
@deletion_batch_size ||= (options[:deletion_batch_size] || 1_000).to_i
49+
end
50+
51+
def deletion_sleep_time
52+
@deletion_sleep_time ||= (options[:deletion_sleep_time] || 0.5).to_f
53+
end
54+
55+
def min_retention_period
56+
@min_retention_period ||= ActiveSupport::Duration.parse(options[:min_retention_period] || "P1D")
57+
end
58+
59+
def delivered_min_retention_period
60+
@delivered_min_retention_period ||= ActiveSupport::Duration.parse(options[:delivered_min_retention_period] || "PT1H")
61+
end
62+
4763
def max_retries
4864
@max_retries ||= (options[:max_retries] || 0).to_i
4965
end

lib/generators/outbox/install/templates/outbox.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ default: &default
1717
# partition_size: 2
1818
# partition_strategy: number
1919
# retention: P1W
20+
# min_retention_period: P1D
21+
# retention_delivered_items: PT6H
22+
# delivered_min_retention_period: PT1H
23+
# deletion_batch_size: 1_000
24+
# deletion_sleep_time: 0.5
2025
# retry_strategies:
2126
# - exponential_backoff
2227
# - compacted_log
@@ -32,6 +37,11 @@ default: &default
3237
# partition_size: 2
3338
# partition_strategy: number
3439
# retention: P1W
40+
# min_retention_period: P1D
41+
# retention_delivered_items: PT6H
42+
# delivered_min_retention_period: PT1H
43+
# deletion_batch_size: 1_000
44+
# deletion_sleep_time: 0.5
3545
# retry_strategies:
3646
# - exponential_backoff
3747
# transports:

lib/sbmt/outbox/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
module Sbmt
44
module Outbox
5-
VERSION = "6.13.1"
5+
VERSION = "6.14.0"
66
end
77
end

spec/jobs/sbmt/outbox/base_delete_stale_items_job_spec.rb

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,46 @@ def item_classes
1111
end
1212
end
1313

14-
let!(:item_delivered) { create(:outbox_item, created_at: created_at, status: 2) }
15-
let!(:item_failed) { create(:outbox_item, created_at: created_at, status: 1) }
14+
let!(:item_delivered) { create(:outbox_item, created_at: created_at, status: :delivered) }
15+
let!(:item_failed) { create(:outbox_item, created_at: created_at, status: :failed) }
1616
let(:created_at) { 1.month.ago }
1717

18-
before do
19-
stub_const("Sbmt::Outbox::BaseDeleteStaleItemsJob::BATCH_SIZE", 1)
20-
end
21-
2218
describe ".enqueue" do
2319
it "enqueue all item classes" do
2420
expect { job_class.enqueue }.to have_enqueued_job(job_class).with("OutboxItem")
2521
end
2622
end
2723

28-
it "deletes items with status 2 and old items with status 1 and tracks metrics" do
29-
expect { job_class.perform_now("OutboxItem") }
30-
.to change(OutboxItem, :count).by(-2)
31-
.and increment_yabeda_counter(Yabeda.outbox.deleted_counter).with_tags(box_name: "outbox_item", box_type: :outbox).by(2)
32-
.and measure_yabeda_histogram(Yabeda.outbox.delete_latency).with_tags(box_name: "outbox_item", box_type: :outbox)
33-
end
24+
describe "#perform" do
25+
context "when all items exceed retention periods" do
26+
it "deletes items and tracks metrics" do
27+
expect { job_class.perform_now("OutboxItem") }
28+
.to change(OutboxItem, :count).by(-2)
29+
.and increment_yabeda_counter(Yabeda.outbox.deleted_counter).with_tags(box_name: "outbox_item", box_type: :outbox).by(2)
30+
.and measure_yabeda_histogram(Yabeda.outbox.delete_latency).with_tags(box_name: "outbox_item", box_type: :outbox)
31+
end
32+
end
3433

35-
context "when an element with status 1 does not retention" do
36-
let(:created_at) { 6.hours.ago }
34+
context "when items do not exceed the minimum retention period" do
35+
let(:created_at) { 6.hours.ago }
3736

38-
it "doesn't delete item with status 1 but deletes item with status 2 and tracks metrics" do
39-
expect { job_class.perform_now("OutboxItem") }
40-
.to change(OutboxItem, :count).by(-1)
41-
.and increment_yabeda_counter(Yabeda.outbox.deleted_counter).with_tags(box_name: "outbox_item", box_type: :outbox).by(1)
42-
.and measure_yabeda_histogram(Yabeda.outbox.delete_latency).with_tags(box_name: "outbox_item", box_type: :outbox)
37+
it "does not delete items below the retention period but deletes others and tracks metrics" do
38+
expect { job_class.perform_now("OutboxItem") }
39+
.to change(OutboxItem, :count).by(-1)
40+
.and increment_yabeda_counter(Yabeda.outbox.deleted_counter).with_tags(box_name: "outbox_item", box_type: :outbox).by(1)
41+
.and measure_yabeda_histogram(Yabeda.outbox.delete_latency).with_tags(box_name: "outbox_item", box_type: :outbox)
42+
end
43+
end
44+
45+
context "when retention period is invalid" do
46+
before do
47+
allow(OutboxItem.config).to receive_messages(retention: 6.hours, min_retention_period: 1.day)
48+
end
49+
50+
it "raises an error" do
51+
expect { job_class.perform_now("OutboxItem") }
52+
.to raise_error("Retention period for outbox_item must be longer than 1 day")
53+
end
4354
end
4455
end
4556
end

0 commit comments

Comments
 (0)