Skip to content

Commit 81c395c

Browse files
authored
Merge pull request #3745 from cloudfoundry/update-task-stopped-events
Only emit TASK_STOPPED usage events when necessary Fixes #3757
2 parents 8143e5a + c698e3a commit 81c395c

File tree

5 files changed

+93
-10
lines changed

5 files changed

+93
-10
lines changed

app/models/runtime/task_model.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def after_update
3535
if running_state?
3636
create_start_event
3737
elsif terminal_state?
38-
create_stop_event
38+
create_stop_event_if_needed
3939
end
4040
end
4141

@@ -104,6 +104,16 @@ def create_start_event
104104
Repositories::AppUsageEventRepository.new.create_from_task(self, 'TASK_STARTED')
105105
end
106106

107+
def create_stop_event_if_needed
108+
app_usage_repo = Repositories::AppUsageEventRepository.new
109+
110+
start_event = app_usage_repo.find_by_task_and_state(task: self, state: 'TASK_STARTED')
111+
existing_stop_event = app_usage_repo.find_by_task_and_state(task: self, state: 'TASK_STOPPED')
112+
return if start_event.nil? || existing_stop_event.present?
113+
114+
create_stop_event
115+
end
116+
107117
def create_stop_event
108118
Repositories::AppUsageEventRepository.new.create_from_task(self, 'TASK_STOPPED')
109119
end

app/repositories/app_usage_event_repository.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ def find(guid)
88
AppUsageEvent.find(guid:)
99
end
1010

11+
def find_by_task_and_state(task:, state:)
12+
AppUsageEvent.find(task_guid: task.guid, state: state)
13+
end
14+
1115
def create_from_process(process, state_name=nil)
1216
AppUsageEvent.create(
1317
state: state_name || process.state,

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
module VCAP::CloudController
55
module Diego
66
RSpec.describe TaskCompletionHandler do
7-
let!(:task) { TaskModel.make }
7+
let!(:task) { TaskModel.make(:pending) }
88
let(:handler) { TaskCompletionHandler.new }
99
let(:logger) { instance_double(Steno::Logger, info: nil, error: nil, warn: nil) }
1010

@@ -13,6 +13,10 @@ module Diego
1313
end
1414

1515
describe '#complete_task' do
16+
before do
17+
task.update(state: TaskModel::RUNNING_STATE)
18+
end
19+
1620
context 'when the task succeeds' do
1721
let(:response) do
1822
{

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ module Diego
4242
context 'when a running CC task is missing from BBS' do
4343
let!(:running_task) { TaskModel.make(:running, created_at: 1.minute.ago) }
4444
let!(:canceling_task) { TaskModel.make(:canceling, created_at: 1.minute.ago) }
45+
let!(:start_event_for_running_task) { AppUsageEvent.make(task_guid: running_task.guid, state: 'TASK_STARTED') }
46+
let!(:start_event_for_canceling_task) { AppUsageEvent.make(task_guid: canceling_task.guid, state: 'TASK_STARTED') }
4547
let(:bbs_tasks) { [] }
4648

4749
it 'marks the tasks as failed' do

spec/unit/models/runtime/task_model_spec.rb

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ module VCAP::CloudController
1616
describe 'after update' do
1717
let(:task) { TaskModel.make(app: parent_app, state: TaskModel::PENDING_STATE) }
1818

19-
context 'when the task is moving to SUCCEEDED_STATE' do
19+
context 'when the task is moving to the SUCCEEDED_STATE' do
20+
let(:task) { TaskModel.make(app: parent_app, state: TaskModel::RUNNING_STATE) }
21+
let!(:start_event) { AppUsageEvent.make(task_guid: task.guid, state: 'TASK_STARTED') }
22+
2023
it 'creates a TASK_STOPPED event' do
2124
task.update(state: TaskModel::SUCCEEDED_STATE)
2225

@@ -27,14 +30,73 @@ module VCAP::CloudController
2730
end
2831
end
2932

30-
context 'when the task is moving to FAILED_STATE' do
31-
it 'creates a TASK_STOPPED event' do
32-
task.update(state: TaskModel::FAILED_STATE)
33+
context 'when the task is moving to the FAILED_STATE' do
34+
context 'when the task is moving from the RUNNING state' do
35+
let!(:start_event) { AppUsageEvent.make(task_guid: task.guid, state: 'TASK_STARTED') }
36+
let(:task) { TaskModel.make(app: parent_app, state: TaskModel::RUNNING_STATE) }
3337

34-
event = AppUsageEvent.find(task_guid: task.guid, state: 'TASK_STOPPED')
35-
expect(event).not_to be_nil
36-
expect(event.task_guid).to eq(task.guid)
37-
expect(event.parent_app_guid).to eq(task.app.guid)
38+
it 'creates a TASK_STOPPED event' do
39+
task.update(state: TaskModel::FAILED_STATE)
40+
41+
event = AppUsageEvent.find(task_guid: task.guid, state: 'TASK_STOPPED')
42+
expect(event).not_to be_nil
43+
expect(event.task_guid).to eq(task.guid)
44+
expect(event.parent_app_guid).to eq(task.app.guid)
45+
end
46+
end
47+
48+
context 'when the task is moving from the PENDING state' do
49+
let(:task) { TaskModel.make(app: parent_app, state: TaskModel::PENDING_STATE) }
50+
51+
it 'does not create a TASK_STOPPED event' do
52+
task.update(state: TaskModel::FAILED_STATE)
53+
54+
event = AppUsageEvent.find(task_guid: task.guid, state: 'TASK_STOPPED')
55+
expect(event).to be_nil
56+
end
57+
end
58+
59+
context 'when the task is moving from the CANCELING state' do
60+
let(:task) { TaskModel.make(app: parent_app, state: TaskModel::CANCELING_STATE) }
61+
62+
context 'when the task has a TASK_STARTED event' do
63+
let!(:start_event) { AppUsageEvent.make(task_guid: task.guid, state: 'TASK_STARTED') }
64+
65+
it 'creates a TASK_STOPPED event' do
66+
task.update(state: TaskModel::FAILED_STATE)
67+
68+
event = AppUsageEvent.find(task_guid: task.guid, state: 'TASK_STOPPED')
69+
expect(event).not_to be_nil
70+
expect(event.task_guid).to eq(task.guid)
71+
expect(event.parent_app_guid).to eq(task.app.guid)
72+
end
73+
end
74+
75+
context 'when the task does not have a TASK_STARTED event' do
76+
let!(:start_event) { nil }
77+
78+
it 'does not create a TASK_STOPPED event' do
79+
task.update(state: TaskModel::FAILED_STATE)
80+
81+
event = AppUsageEvent.find(task_guid: task.guid, state: 'TASK_STOPPED')
82+
expect(event).to be_nil
83+
end
84+
end
85+
end
86+
87+
context 'when the task already has a TASK_STOPPED event' do
88+
let!(:start_event) { AppUsageEvent.make(task_guid: task.guid, state: 'TASK_STARTED') }
89+
90+
before do
91+
AppUsageEvent.make(task_guid: task.guid, state: 'TASK_STOPPED')
92+
end
93+
94+
it 'does not create additional TASK_STOPPED event' do
95+
task.update(state: TaskModel::FAILED_STATE)
96+
97+
events = AppUsageEvent.where(task_guid: task.guid, state: 'TASK_STOPPED')
98+
expect(events.count).to equal(1)
99+
end
38100
end
39101
end
40102

@@ -57,6 +119,7 @@ module VCAP::CloudController
57119
end
58120

59121
context 'when the state is not changing' do
122+
let!(:start_event) { AppUsageEvent.make(task_guid: task.guid, state: 'TASK_STARTED') }
60123
let(:task) { TaskModel.make(app: parent_app, state: TaskModel::FAILED_STATE) }
61124

62125
it 'does not create a TASK_STOPPED event' do

0 commit comments

Comments
 (0)