Skip to content

Commit a45adfe

Browse files
committed
Improve Diego task sync
- Only select required fields instead of all when batching CC tasks. - Process missing diego tasks in workpool (i.e. call to BBS, DB select + update). - Add helper methods to submit to workpool. - Adapt DatabaseIsolation: reset 'api' config is only needed for 'truncation' isolation (in reset_tables).
1 parent f9d9167 commit a45adfe

File tree

6 files changed

+59
-52
lines changed

6 files changed

+59
-52
lines changed

lib/cloud_controller/diego/tasks_sync.rb

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,25 @@ def sync
1919

2020
diego_tasks = bbs_task_client.fetch_tasks.index_by(&:task_guid)
2121

22-
batched_cc_tasks do |cc_tasks|
23-
tasks_to_fail = []
22+
to_update = []
23+
to_cancel = []
2424

25+
batched_cc_tasks do |cc_tasks|
2526
cc_tasks.each do |cc_task|
2627
diego_task = diego_tasks.delete(cc_task.guid)
2728
next unless [TaskModel::RUNNING_STATE, TaskModel::CANCELING_STATE].include? cc_task.state
2829

2930
if diego_task.nil?
30-
tasks_to_fail << cc_task.guid if diego_task_missing?(cc_task.guid) && !task_finished_while_iterating?(cc_task.guid)
31-
logger.info('missing-diego-task', task_guid: cc_task.guid)
31+
to_update << cc_task.guid
3232
elsif cc_task.state == TaskModel::CANCELING_STATE
33-
workpool.submit(cc_task.guid) do |guid|
34-
bbs_task_client.cancel_task(guid)
35-
logger.info('canceled-cc-task', task_guid: guid)
36-
end
37-
end
38-
end
39-
40-
unless tasks_to_fail.empty?
41-
TaskModel.where(guid: tasks_to_fail).each do |cc_task|
42-
cc_task.update(state: TaskModel::FAILED_STATE, failure_reason: BULKER_TASK_FAILURE)
33+
to_cancel << cc_task.guid
4334
end
4435
end
4536
end
4637

47-
diego_tasks.each_key do |task_guid|
48-
workpool.submit(task_guid) do |guid|
49-
bbs_task_client.cancel_task(guid)
50-
logger.info('missing-cc-task', task_guid: guid)
51-
end
52-
end
38+
update_missing_diego_tasks(to_update)
39+
cancel_cc_tasks(to_cancel)
40+
cancel_missing_cc_tasks(diego_tasks)
5341

5442
workpool.drain
5543

@@ -87,21 +75,44 @@ def formatted_backtrace_from_error(error)
8775
error.backtrace.present? ? error.backtrace.join("\n") + "\n..." : ''
8876
end
8977

90-
def diego_task_missing?(task_guid)
91-
bbs_task_client.fetch_task(task_guid).nil?
78+
def update_missing_diego_tasks(to_update)
79+
to_update.each do |task_guid|
80+
workpool.submit(task_guid) do |guid|
81+
diego_task_missing = bbs_task_client.fetch_task(guid).nil?
82+
if diego_task_missing
83+
# Mark the CC task as failed. Don't update tasks that are already in a terminal state.
84+
task = TaskModel.where(guid:).exclude(state: [TaskModel::FAILED_STATE, TaskModel::SUCCEEDED_STATE]).first
85+
task&.update(state: TaskModel::FAILED_STATE, failure_reason: BULKER_TASK_FAILURE) # invoke model's update method to create an event
86+
logger.info('missing-diego-task', task_guid: guid)
87+
end
88+
end
89+
end
90+
end
91+
92+
def cancel_cc_tasks(to_cancel)
93+
to_cancel.each do |task_guid|
94+
workpool.submit(task_guid) do |guid|
95+
bbs_task_client.cancel_task(guid)
96+
logger.info('canceled-cc-task', task_guid: guid)
97+
end
98+
end
9299
end
93100

94-
def task_finished_while_iterating?(task_guid)
95-
cc_task = TaskModel.find(guid: task_guid)
96-
[TaskModel::FAILED_STATE, TaskModel::SUCCEEDED_STATE].include?(cc_task.state)
101+
def cancel_missing_cc_tasks(to_cancel_missing)
102+
to_cancel_missing.each_key do |task_guid|
103+
workpool.submit(task_guid) do |guid|
104+
bbs_task_client.cancel_task(guid)
105+
logger.info('missing-cc-task', task_guid: guid)
106+
end
107+
end
97108
end
98109

99110
def batched_cc_tasks
100111
last_id = 0
101112
loop do
102113
tasks = TaskModel.where(
103114
Sequel.lit('tasks.id > ?', last_id)
104-
).order(:id).limit(BATCH_SIZE).all
115+
).order(:id).limit(BATCH_SIZE).select(:id, :guid, :state).all
105116

106117
yield tasks
107118
return if tasks.count < BATCH_SIZE

spec/db_spec_helper.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@
2626
end
2727

2828
rspec_config.around do |example|
29-
# DatabaseIsolation requires the api config context
30-
TestConfig.context = :api
31-
TestConfig.reset
32-
33-
isolation = DatabaseIsolation.choose(example.metadata[:isolation], TestConfig.config_instance, DbConfig.new.connection)
29+
isolation = DatabaseIsolation.choose(example.metadata[:isolation], DbConfig.new.connection)
3430
isolation.cleanly { example.run }
3531
end
3632
end

spec/performance/packages_controller_index_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
require 'spec_helper'
22
require 'rails_helper'
33

4-
RSpec.describe PackagesController, type: :controller do # , isolation: :truncation
4+
RSpec.describe PackagesController, type: :controller do
55
describe '#index' do
66
let(:user) { set_current_user(VCAP::CloudController::User.make) }
77
let(:app_model) { VCAP::CloudController::AppModel.make }

spec/spec_helper.rb

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@
166166
end
167167

168168
rspec_config.before do
169+
Delayed::Worker.destroy_failed_jobs = false
170+
Sequel::Deprecation.output = StringIO.new
171+
Sequel::Deprecation.backtrace_filter = 5
172+
173+
TestConfig.context = example.metadata[:job_context] || :api
174+
TestConfig.reset
175+
169176
Fog::Mock.reset
170177

171178
if Fog.mock?
@@ -175,13 +182,6 @@
175182
CloudController::DependencyLocator.instance.buildpack_blobstore.ensure_bucket_exists
176183
end
177184

178-
Delayed::Worker.destroy_failed_jobs = false
179-
Sequel::Deprecation.output = StringIO.new
180-
Sequel::Deprecation.backtrace_filter = 5
181-
182-
TestConfig.context = example.metadata[:job_context] || :api
183-
TestConfig.reset
184-
185185
VCAP::CloudController::SecurityContext.clear
186186
allow_any_instance_of(VCAP::CloudController::UaaTokenDecoder).to receive(:uaa_issuer).and_return(UAAIssuer::ISSUER)
187187

@@ -190,11 +190,7 @@
190190
end
191191

192192
rspec_config.around do |example|
193-
# DatabaseIsolation requires the api config context
194-
TestConfig.context = :api
195-
TestConfig.reset
196-
197-
isolation = DatabaseIsolation.choose(example.metadata[:isolation], TestConfig.config_instance, DbConfig.new.connection)
193+
isolation = DatabaseIsolation.choose(example.metadata[:isolation], DbConfig.new.connection)
198194
isolation.cleanly { example.run }
199195
end
200196

spec/support/database_isolation.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
module DatabaseIsolation
2-
def self.choose(isolation, config, db)
2+
def self.choose(isolation, db)
33
case isolation
44
when :truncation
5-
TruncateTables.new(config, db)
5+
TruncateTables.new(db)
66
else
77
RollbackTransaction.new
88
end
99
end
1010

1111
class TruncateTables
12-
def initialize(config, db)
13-
@config = config
12+
def initialize(db)
1413
@db = db
1514
end
1615

@@ -24,7 +23,10 @@ def reset_tables
2423
table_truncator = TableTruncator.new(db)
2524
table_truncator.truncate_tables
2625

27-
VCAP::CloudController::Seeds.write_seed_data(config)
26+
# VCAP::CloudController::Seeds requires the :api config
27+
TestConfig.context = :api
28+
TestConfig.reset
29+
VCAP::CloudController::Seeds.write_seed_data(TestConfig.config_instance)
2830
end
2931

3032
private

spec/unit/lib/cloud_controller/diego/tasks_sync_spec.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ module Diego
4545
end
4646
end
4747

48-
context 'when a running CC task is missing from BBS' do
48+
context 'when a running CC task is missing from BBS', isolation: :truncation do
49+
# Can't use transactions for isolation because we're using multiple threads
4950
let!(:running_task) { TaskModel.make(:running, created_at: 1.minute.ago) }
5051
let!(:canceling_task) { TaskModel.make(:canceling, created_at: 1.minute.ago) }
5152
let!(:start_event_for_running_task) { AppUsageEvent.make(task_guid: running_task.guid, state: 'TASK_STARTED') }
@@ -270,7 +271,7 @@ module Diego
270271
end
271272
end
272273

273-
context 'when a new task is created after cc initally fetches tasks from bbs' do
274+
context 'when a new task is created after cc initially fetches tasks from bbs' do
274275
context 'and the newly started task does not complete before checking to see if it should fail' do
275276
let!(:cc_task) { TaskModel.make(guid: 'some-task-guid', state: TaskModel::RUNNING_STATE) }
276277
let(:bbs_task) { ::Diego::Bbs::Models::Task.new(task_guid: 'some-task-guid', state: ::Diego::Bbs::Models::Task::State::Running) }
@@ -298,7 +299,8 @@ module Diego
298299
end
299300
end
300301

301-
context 'and the newly started task completes before the iteration completes' do
302+
context 'and the newly started task completes before the iteration completes', isolation: :truncation do
303+
# Can't use transactions for isolation because we're using multiple threads
302304
let!(:cc_task) { TaskModel.make(guid: 'some-task-guid', state: TaskModel::RUNNING_STATE) }
303305
let(:bbs_tasks) { [] }
304306

0 commit comments

Comments
 (0)