Skip to content

Commit f2b0f83

Browse files
authored
Prevent DropletUpload from downgrading a staged droplet to failed (#5244)
If a worker is killed mid-job and restarts, it re-locks and re-runs its own job. When another worker already completed the job in the meantime, the droplet is already STAGED. The rescue block would unconditionally overwrite STAGED with FAILED in that case. Skip the state transition and log when the droplet is already staged.
1 parent fa25fc3 commit f2b0f83

2 files changed

Lines changed: 29 additions & 3 deletions

File tree

app/jobs/v3/droplet_upload.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ def perform
3535
if droplet
3636
droplet.db.transaction do
3737
droplet.lock!
38-
droplet.error_description = e.message
39-
droplet.state = DropletModel::FAILED_STATE
40-
droplet.save
38+
if droplet.staged?
39+
logger.info('droplet-upload.skipping-failed-state', droplet_guid: @droplet_guid, error: e.message)
40+
else
41+
droplet.error_description = e.message
42+
droplet.state = DropletModel::FAILED_STATE
43+
droplet.save
44+
end
4145
end
4246
end
4347
raise
@@ -66,6 +70,10 @@ def resource_type
6670
def blobstore
6771
@blobstore ||= CloudController::DependencyLocator.instance.droplet_blobstore
6872
end
73+
74+
def logger
75+
@logger ||= Steno.logger('cc.jobs.v3.droplet_upload')
76+
end
6977
end
7078
end
7179
end

spec/unit/jobs/v3/droplet_upload_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,24 @@ def reschedule_at(_, _=nil)
146146
}.from(true).to(false)
147147
end
148148
end
149+
150+
context 'when the droplet is already STAGED' do
151+
let(:staged_droplet) { create(:droplet_model, state: DropletModel::STAGED_STATE, droplet_hash: nil, sha256_checksum: nil, set_as_current_droplet: false) }
152+
153+
before do
154+
staged_job = DropletUpload.new(local_file.path, staged_droplet.guid, skip_state_transition:)
155+
Delayed::Job.enqueue(staged_job, queue: worker.name)
156+
worker.work_off 1
157+
end
158+
159+
it 'does not overwrite the droplet state' do
160+
expect(staged_droplet.refresh.state).to eq(DropletModel::STAGED_STATE)
161+
end
162+
163+
it 'does not set an error description' do
164+
expect(staged_droplet.refresh.error_description).to be_nil
165+
end
166+
end
149167
end
150168

151169
context 'if the file is missing' do

0 commit comments

Comments
 (0)