Skip to content
This repository was archived by the owner on Oct 27, 2022. It is now read-only.

Commit 8252bb4

Browse files
authored
Merge pull request #1049 from Duke-Translational-Bioinformatics/develop
Merge v1.3.26 to ua_test
2 parents 031d25e + a6ea0a0 commit 8252bb4

File tree

7 files changed

+125
-84
lines changed

7 files changed

+125
-84
lines changed

api_design/DDS-872-eventual_consistency_status.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,16 @@ We will change the success response to [`202: Accepted`](https://httpstatuses.co
7676
## Process View
7777

7878
Add notes about performance, scalability, throughput, etc. here. These can inform future proposals to change the implementation.
79+
80+
#### Deployment
81+
82+
Environment Variable | Default | Description
83+
--- | --- | ---
84+
**ACTIVE_JOB_QUEUE_ADAPTER** | sneakers | this sets how activejob runs jobs. Jobs can be `inline` (jobs run immediately instead of in the background), or `sneakers` (jobs run in the background using rabbitmq)
85+
**CLOUDAMQP_URL** | | this is the base url to the rabbitmq server. For heroku managed applications, this is set automatically when you provision the Cloud AMQP service. You can change this to point to another rabbitmq server. This is set in rabbitmq.client.env for the local docker-compose managed instance.
86+
**WORKERS_ALL_RUN_EXCEPT** | | comma-separated list of workers that are not meant to run. This is used by the `rake workers:all:run` task.
87+
88+
**Notes**:
89+
When using the CloudAMQP plans that are not dedicated (little-lemur, tough-tiger), queues that are not accessed by workers for *Max idle queue time* (this setting can be viewed in the the CloudAMQP dashboard, accessed from heroku application dashboard using the browser) are deleted. Going forward we should consider the use of dedicated plans, and/or monitoring and alerts on job workers to notify us when jobs stop running.
90+
91+
We use the heroku scheduler to run `rake queue:message_log:index_messages MESSAGE_LOG_WORK_DURATION=300` to drain the events in the message_log queue into an elasticsearch index. MESSAGE_LOG_WORK_DUR sets the length of time that this job runs. We may want to watch the message_log queue to ensure that it is being drained during each run of this task. If it is not, we can increase the work duration.

app/jobs/child_deletion_job.rb

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
class ChildDeletionJob < ApplicationJob
22
queue_as :child_deletion
33

4-
def perform(job_transaction, parent)
4+
def perform(job_transaction, parent, page)
55
self.class.start_job(job_transaction)
6-
parent.children.each do |child|
7-
child.current_transaction = job_transaction if child.class.include? ChildMinder
8-
child.update(is_deleted: true)
9-
end
6+
parent.current_transaction = job_transaction
7+
parent.delete_children(page)
108
self.class.complete_job(job_transaction)
119
end
1210
end

app/models/concerns/child_minder.rb

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,30 @@ def manage_children
99
newly_deleted = is_deleted_changed? && is_deleted?
1010
yield
1111
if has_children? && newly_deleted
12-
ChildDeletionJob.perform_later(
13-
ChildDeletionJob.initialize_job(self),
14-
self
15-
)
12+
(1..paginated_children.total_pages).each do |page|
13+
ChildDeletionJob.perform_later(
14+
ChildDeletionJob.initialize_job(self),
15+
self,
16+
page
17+
)
18+
end
1619
end
1720
end
1821

19-
def delete_children
20-
children.each do |child|
22+
def has_children?
23+
children.count > 0
24+
end
25+
26+
def delete_children(page)
27+
paginated_children(page).each do |child|
28+
child.current_transaction = current_transaction if child.class.include? ChildMinder
2129
child.update(is_deleted: true)
2230
end
2331
end
2432

25-
def has_children?
26-
children.count > 0
33+
private
34+
35+
def paginated_children(page=1)
36+
children.page(page).per(Rails.application.config.max_children_per_job)
2737
end
2838
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Rails.application.config.max_children_per_job = ENV['MAX_CHILDREN_PER_JOB'] ? ENV['MAX_CHILDREN_PER_JOB'].to_i : 100

spec/jobs/child_deletion_job_spec.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,48 @@
11
require 'rails_helper'
22

33
RSpec.describe ChildDeletionJob, type: :job do
4+
5+
shared_examples 'a ChildDeletionJob' do |
6+
parent_sym,
7+
child_folder_sym,
8+
child_file_sym
9+
|
10+
let(:parent) { send(parent_sym) }
11+
let(:job_transaction) { described_class.initialize_job(parent) }
12+
let(:child_folder) { send(child_folder_sym) }
13+
let(:child_file) { send(child_file_sym) }
14+
let(:prefix) { Rails.application.config.active_job.queue_name_prefix }
15+
let(:prefix_delimiter) { Rails.application.config.active_job.queue_name_delimiter }
16+
include_context 'with job runner', described_class
17+
18+
it { is_expected.to be_an ApplicationJob }
19+
it { expect(prefix).not_to be_nil }
20+
it { expect(prefix_delimiter).not_to be_nil }
21+
it { expect(described_class.queue_name).to eq("#{prefix}#{prefix_delimiter}child_deletion") }
22+
it {
23+
expect {
24+
described_class.perform_now
25+
}.to raise_error(ArgumentError)
26+
expect {
27+
described_class.perform_now(job_transaction)
28+
}.to raise_error(ArgumentError)
29+
expect {
30+
described_class.perform_now(job_transaction, parent)
31+
}.to raise_error(ArgumentError)
32+
}
33+
34+
context 'perform_now' do
35+
let(:page) { 1 }
36+
37+
it {
38+
expect(child_folder).to be_persisted
39+
expect(child_file).to be_persisted
40+
expect(parent).to receive(:delete_children).with(page)
41+
described_class.perform_now(job_transaction, parent, page)
42+
}
43+
end
44+
end
45+
446
before do
547
expect(folder_child).to be_persisted
648
end

spec/support/concerns/child_minder_shared_examples.rb

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
ChildDeletionJob.initialize_job(subject)
6666
}
6767
before do
68+
@old_max = Rails.application.config.max_children_per_job
69+
Rails.application.config.max_children_per_job = 1
6870
expect(child_folder).to be_persisted
6971
expect(child_folder.is_deleted?).to be_falsey
7072
expect(valid_child_file).to be_persisted
@@ -73,13 +75,19 @@
7375
expect(invalid_child_file.is_deleted?).to be_falsey
7476
end
7577

78+
after do
79+
Rails.application.config.max_children_per_job = @old_max
80+
end
81+
7682
it {
7783
expect(subject).to be_has_children
7884
subject.is_deleted = true
7985
yield_called = false
80-
expect(ChildDeletionJob).to receive(:initialize_job)
81-
.with(subject).and_return(job_transaction)
82-
expect(ChildDeletionJob).to receive(:perform_later).with(job_transaction, subject)
86+
expect(ChildDeletionJob).to receive(:initialize_job).exactly(subject.children.count)
87+
.with(subject).times.and_return(job_transaction)
88+
(1..subject.children.count).each do |page|
89+
expect(ChildDeletionJob).to receive(:perform_later).with(job_transaction, subject, page)
90+
end
8391
subject.manage_children do
8492
yield_called = true
8593
end
@@ -133,23 +141,41 @@
133141
end
134142

135143
describe '#delete_children' do
136-
it {
137-
is_expected.to respond_to(:delete_children)
138-
}
139-
it {
140-
expect(child_folder).to be_persisted
141-
expect(child_folder.is_deleted?).to be_falsey
142-
expect(valid_child_file).to be_persisted
143-
expect(valid_child_file.is_deleted?).to be_falsey
144-
expect(invalid_child_file).to be_persisted
145-
expect(invalid_child_file.is_deleted?).to be_falsey
146-
subject.delete_children
147-
expect(child_folder.reload).to be_truthy
148-
expect(child_folder.is_deleted?).to be_truthy
149-
valid_child_file.reload
150-
expect(valid_child_file.is_deleted?).to be_truthy
151-
invalid_child_file.reload
152-
expect(invalid_child_file.is_deleted?).to be_truthy
153-
}
144+
it { is_expected.not_to respond_to(:delete_children).with(0).arguments }
145+
it { is_expected.to respond_to(:delete_children).with(1).argument }
146+
147+
context 'called', :vcr do
148+
let(:job_transaction) { ChildDeletionJob.initialize_job(subject) }
149+
let(:child_job_transaction) { ChildDeletionJob.initialize_job(child_folder) }
150+
let(:child_folder_file) { FactoryGirl.create(:data_file, parent: child_folder)}
151+
let(:page) { 1 }
152+
153+
before do
154+
expect(child_folder).to be_persisted
155+
expect(child_folder_file).to be_persisted
156+
expect(valid_child_file.is_deleted?).to be_falsey
157+
@old_max = Rails.application.config.max_children_per_job
158+
Rails.application.config.max_children_per_job = subject.children.count + child_folder.children.count
159+
end
160+
161+
after do
162+
Rails.application.config.max_children_per_job = @old_max
163+
end
164+
165+
it {
166+
subject.current_transaction = job_transaction
167+
expect(ChildDeletionJob).to receive(:initialize_job)
168+
.with(child_folder)
169+
.and_return(child_job_transaction)
170+
expect(ChildDeletionJob).to receive(:perform_later)
171+
.with(child_job_transaction, child_folder, page).and_call_original
172+
subject.delete_children(page)
173+
expect(child_folder.reload).to be_truthy
174+
expect(child_folder.is_deleted?).to be_truthy
175+
expect(valid_child_file.reload).to be_truthy
176+
expect(valid_child_file.is_deleted?).to be_truthy
177+
}
178+
end
179+
154180
end
155-
end
181+
end

spec/support/job_shared_examples.rb

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -163,52 +163,3 @@
163163
.and_call_original
164164
end
165165
end
166-
167-
shared_examples 'a ChildDeletionJob' do |
168-
parent_sym,
169-
child_folder_sym,
170-
child_file_sym
171-
|
172-
let(:parent) { send(parent_sym) }
173-
let(:job_transaction) { described_class.initialize_job(parent) }
174-
let(:child_folder) { send(child_folder_sym) }
175-
let(:child_file) { send(child_file_sym) }
176-
let(:prefix) { Rails.application.config.active_job.queue_name_prefix }
177-
let(:prefix_delimiter) { Rails.application.config.active_job.queue_name_delimiter }
178-
include_context 'with job runner', described_class
179-
180-
it { is_expected.to be_an ApplicationJob }
181-
it { expect(prefix).not_to be_nil }
182-
it { expect(prefix_delimiter).not_to be_nil }
183-
it { expect(described_class.queue_name).to eq("#{prefix}#{prefix_delimiter}child_deletion") }
184-
it {
185-
expect {
186-
described_class.perform_now
187-
}.to raise_error(ArgumentError)
188-
expect {
189-
described_class.perform_now(parent)
190-
}.to raise_error(ArgumentError)
191-
}
192-
193-
context 'perform_now', :vcr do
194-
let(:child_job_transaction) { described_class.initialize_job(child_folder) }
195-
include_context 'tracking job', :job_transaction
196-
197-
it {
198-
expect(child_folder).to be_persisted
199-
expect(child_file.is_deleted?).to be_falsey
200-
201-
expect(described_class).to receive(:initialize_job)
202-
.with(child_folder)
203-
.and_return(child_job_transaction)
204-
expect(described_class).to receive(:perform_later)
205-
.with(child_job_transaction, child_folder).and_call_original
206-
207-
described_class.perform_now(job_transaction, parent)
208-
expect(child_folder.reload).to be_truthy
209-
expect(child_folder.is_deleted?).to be_truthy
210-
expect(child_file.reload).to be_truthy
211-
expect(child_file.is_deleted?).to be_truthy
212-
}
213-
end
214-
end

0 commit comments

Comments
 (0)