Skip to content

Commit 5baaac2

Browse files
committed
[DEX-2703] feat: add task for update deleted items
1 parent 4868db8 commit 5baaac2

File tree

8 files changed

+265
-1
lines changed

8 files changed

+265
-1
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1313

1414
### Fixed
1515

16+
## [6.15.0] - 2025-01-23
17+
18+
### Added
19+
20+
- Add rake tasks:
21+
- rake outbox:delete_items
22+
- rake outbox:update_status_items
23+
24+
1625
## [6.14.0] - 2025-01-20
1726

1827
### Added

README.md

+36
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,42 @@ outbox_items:
439439
partition_strategy: hash
440440
```
441441

442+
## Rake tasks
443+
444+
```shell
445+
rake outbox:delete_items
446+
rake outbox:update_status_items
447+
```
448+
449+
Example run:
450+
```shell
451+
rake outbox:delete_items[OutboxItem,1] # Mandatory parameters box class and status
452+
rake outbox:update_status_items[OutboxItem,0,3] # Mandatory parameters box class, current status and new status
453+
454+
```
455+
456+
Both tasks have optional parameters:
457+
```ruby
458+
- start_time # boxes are younger than the specified time, by default nil, time is specified in the format "2025-01-05T23:59:59"
459+
- end_time # boxes are older than the specified time, by default 6.hours.ago, time is specified in the format "2025-01-05T23:59:59"
460+
- batch_size # batch size, by default 1_000
461+
- sleep_time # sleep time between batches, by default 0.5
462+
```
463+
464+
Example with optional parameters:
465+
- format optional parameters:
466+
```shell
467+
rake outbox:delete_items[klass_name,status,start_time,end_time,batch_size,sleep_time]
468+
469+
rake outbox:update_status_items[klass_name,status,new_status,start_time,end_time,batch_size,sleep_time]
470+
```
471+
- example:
472+
```shell
473+
rake outbox:delete_items[OutboxItem,1,"2025-01-05T23:59:59","2025-01-05T00:00:00",10_000,5]
474+
475+
rake outbox:update_status_items[OutboxItem,0,3,"2025-01-05T23:59:59","2025-01-05T00:00:00",10_000,5]
476+
```
477+
442478
## Concurrency
443479

444480
The worker process consists of a poller and a processor, each of which has its own thread pool.

lib/sbmt/outbox/engine.rb

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class Engine < Rails::Engine
7979
rake_tasks do
8080
load "sbmt/outbox/tasks/retry_failed_items.rake"
8181
load "sbmt/outbox/tasks/delete_failed_items.rake"
82+
load "sbmt/outbox/tasks/delete_items.rake"
83+
load "sbmt/outbox/tasks/update_status_items.rake"
8284
end
8385
end
8486
end
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# frozen_string_literal: true
2+
3+
namespace :outbox do
4+
desc "Delete outbox/inbox items"
5+
task :delete_items, [:klass_name, :status, :start_time, :end_time, :batch_size, :sleep_time] => :environment do |_, args|
6+
args.with_defaults(start_time: nil, end_time: 6.hours.ago, batch_size: 1000, sleep_time: 0.5)
7+
8+
klass_name = args[:klass_name]
9+
status = args[:status]
10+
start_time = args[:start_time]
11+
end_time = args[:end_time]
12+
batch_size = args[:batch_size]
13+
sleep_time = args[:sleep_time]
14+
15+
unless klass_name && status
16+
raise "Error: Class and status must be specified. Example: rake outbox:delete_items[OutboxItem,1]"
17+
end
18+
19+
klass_name = klass_name.constantize
20+
query = klass_name.where(status: status)
21+
22+
if start_time && end_time
23+
query = query.where(created_at: start_time..end_time)
24+
elsif start_time
25+
query = query.where(created_at: start_time..)
26+
elsif end_time
27+
query = query.where(created_at: ..end_time)
28+
end
29+
30+
total_deleted = 0
31+
query.in_batches(of: batch_size) do |batch|
32+
deleted_count = batch.delete_all
33+
34+
Rails.logger.info("Batch items deleted: #{deleted_count}")
35+
36+
total_deleted += deleted_count
37+
38+
sleep sleep_time
39+
end
40+
41+
Rails.logger.info("Total items deleted: #{total_deleted}")
42+
end
43+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# frozen_string_literal: true
2+
3+
namespace :outbox do
4+
desc "Update status of outbox/inbox items"
5+
task :update_status_items, [:klass_name, :status, :new_status, :start_time, :end_time, :batch_size, :sleep_time] => :environment do |_, args|
6+
args.with_defaults(start_time: nil, end_time: 6.hours.ago, batch_size: 1000, sleep_time: 0.5)
7+
8+
klass_name = args[:klass_name]
9+
status = args[:status]
10+
new_status = args[:new_status]
11+
start_time = args[:start_time]
12+
end_time = args[:end_time]
13+
batch_size = args[:batch_size]
14+
sleep_time = args[:sleep_time]
15+
16+
unless klass_name && status && new_status
17+
raise "Error: Class, current status, and new status must be specified. Example: rake outbox:update_status_items[OutboxItem,0,3]"
18+
end
19+
20+
klass_name = klass_name.constantize
21+
query = klass_name.where(status: status)
22+
23+
if start_time && end_time
24+
query = query.where(created_at: start_time..end_time)
25+
elsif start_time
26+
query = query.where(created_at: start_time..)
27+
elsif end_time
28+
query = query.where(created_at: ..end_time)
29+
end
30+
31+
total_updated = 0
32+
query.in_batches(of: batch_size) do |batch|
33+
updated_count = batch.update_all(status: new_status)
34+
35+
Rails.logger.info("Batch items updated: #{updated_count}")
36+
37+
total_updated += updated_count
38+
sleep sleep_time
39+
end
40+
41+
Rails.logger.info("Total items updated: #{total_updated}")
42+
end
43+
end

lib/sbmt/outbox/version.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
module Sbmt
44
module Outbox
5-
VERSION = "6.14.0"
5+
VERSION = "6.15.0"
66
end
77
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# frozen_string_literal: true
2+
3+
describe "rake outbox:delete_items" do
4+
subject(:task) { Rake::Task["outbox:delete_items"] }
5+
6+
let(:klass) { "OutboxItem" }
7+
let(:status) { 1 }
8+
9+
let(:created_at_a) { 6.hours.ago }
10+
let(:created_at_b) { 8.hours.ago }
11+
let(:created_at_c) { 4.hours.ago }
12+
13+
let!(:outbox_item_a) { create(:outbox_item, status: :failed, errors_count: 1, created_at: created_at_a) }
14+
let!(:outbox_item_b) { create(:outbox_item, status: :failed, errors_count: 1, created_at: created_at_b) }
15+
let!(:outbox_item_c) { create(:outbox_item, status: :delivered, errors_count: 0, created_at: created_at_c) }
16+
17+
before do
18+
task.reenable
19+
allow(Rails.logger).to receive(:info)
20+
end
21+
22+
context "when filtering records by status" do
23+
let(:created_at_a) { Time.zone.now }
24+
let(:created_at_b) { 6.hours.ago }
25+
let(:created_at_c) { Time.zone.now }
26+
27+
it "deletes records matching the given status" do
28+
expect {
29+
task.invoke(klass, status)
30+
}.to change(OutboxItem, :count).by(-1)
31+
32+
expect(Rails.logger).to have_received(:info).with(/Batch items deleted: 1/)
33+
expect(Rails.logger).to have_received(:info).with(/Total items deleted: 1/)
34+
end
35+
end
36+
37+
context "when filtering records by time range" do
38+
let(:start_time) { 7.hours.ago }
39+
let(:end_time) { 5.hours.ago }
40+
41+
it "deletes records within the specified time range" do
42+
expect {
43+
task.invoke(klass, status, start_time, end_time)
44+
}.to change(OutboxItem, :count).by(-1)
45+
46+
expect(Rails.logger).to have_received(:info).with(/Batch items deleted: 1/)
47+
expect(Rails.logger).to have_received(:info).with(/Total items deleted: 1/)
48+
end
49+
end
50+
51+
context "when required parameters are missing" do
52+
it "raises an error" do
53+
expect {
54+
task.invoke(nil, status)
55+
}.to raise_error("Error: Class and status must be specified. Example: rake outbox:delete_items[OutboxItem,1]")
56+
57+
expect(Rails.logger).not_to have_received(:info)
58+
end
59+
end
60+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# frozen_string_literal: true
2+
3+
describe "rake outbox:update_status_items" do
4+
subject(:task) { Rake::Task["outbox:update_status_items"] }
5+
6+
let(:klass) { "OutboxItem" }
7+
let(:status) { 1 }
8+
let(:new_status) { 3 }
9+
10+
let(:created_at_a) { 6.hours.ago }
11+
let(:created_at_b) { 8.hours.ago }
12+
let(:created_at_c) { 4.hours.ago }
13+
14+
let!(:outbox_item_a) { create(:outbox_item, status: :failed, errors_count: 1, created_at: created_at_a) }
15+
let!(:outbox_item_b) { create(:outbox_item, status: :failed, errors_count: 1, created_at: created_at_b) }
16+
let!(:outbox_item_c) { create(:outbox_item, status: :delivered, errors_count: 0, created_at: created_at_c) }
17+
18+
before do
19+
task.reenable
20+
allow(Rails.logger).to receive(:info)
21+
end
22+
23+
context "when filtering records by status" do
24+
let(:created_at_a) { Time.zone.now }
25+
let(:created_at_b) { 6.hours.ago }
26+
let(:created_at_c) { Time.zone.now }
27+
28+
it "updates records matching the given status" do
29+
expect {
30+
task.invoke(klass, status, new_status)
31+
outbox_item_a.reload
32+
outbox_item_b.reload
33+
outbox_item_c.reload
34+
}.to change(outbox_item_b, :status).from("failed").to("discarded")
35+
.and not_change { outbox_item_a.status }
36+
.and not_change { outbox_item_c.status }
37+
38+
expect(Rails.logger).to have_received(:info).with(/Batch items updated: 1/)
39+
expect(Rails.logger).to have_received(:info).with(/Total items updated: 1/)
40+
end
41+
end
42+
43+
context "when filtering records by time range" do
44+
let(:start_time) { 7.hours.ago }
45+
let(:end_time) { 5.hours.ago }
46+
47+
it "updates records within the specified time range" do
48+
expect {
49+
task.invoke(klass, status, new_status, start_time, end_time)
50+
outbox_item_a.reload
51+
outbox_item_b.reload
52+
outbox_item_c.reload
53+
}.to change(outbox_item_a, :status).from("failed").to("discarded")
54+
.and not_change { outbox_item_b.status }
55+
.and not_change { outbox_item_c.status }
56+
57+
expect(Rails.logger).to have_received(:info).with(/Batch items updated: 1/)
58+
expect(Rails.logger).to have_received(:info).with(/Total items updated: 1/)
59+
end
60+
end
61+
62+
context "when required parameters are missing" do
63+
it "raises an error" do
64+
expect {
65+
task.invoke(nil, status, new_status)
66+
}.to raise_error("Error: Class, current status, and new status must be specified. Example: rake outbox:update_status_items[OutboxItem,0,3]")
67+
68+
expect(Rails.logger).not_to have_received(:info)
69+
end
70+
end
71+
end

0 commit comments

Comments
 (0)