summaryrefslogtreecommitdiff
path: root/app/workers/stuck_export_jobs_worker.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/workers/stuck_export_jobs_worker.rb')
-rw-r--r--app/workers/stuck_export_jobs_worker.rb54
1 files changed, 54 insertions, 0 deletions
diff --git a/app/workers/stuck_export_jobs_worker.rb b/app/workers/stuck_export_jobs_worker.rb
new file mode 100644
index 00000000000..6d8d60d2fc0
--- /dev/null
+++ b/app/workers/stuck_export_jobs_worker.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+# rubocop:disable Scalability/IdempotentWorker
+class StuckExportJobsWorker
+ include ApplicationWorker
+ # rubocop:disable Scalability/CronWorkerContext
+ # This worker updates export states inline and does not schedule
+ # other jobs.
+ include CronjobQueue
+ # rubocop:enable Scalability/CronWorkerContext
+
+ feature_category :importers
+ worker_resource_boundary :cpu
+
+ EXPORT_JOBS_EXPIRATION = 6.hours.to_i
+
+ def perform
+ failed_jobs_count = mark_stuck_jobs_as_failed!
+
+ Gitlab::Metrics.add_event(:stuck_export_jobs,
+ failed_jobs_count: failed_jobs_count)
+ end
+
+ private
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def mark_stuck_jobs_as_failed!
+ jids_and_ids = enqueued_exports.pluck(:jid, :id).to_h
+
+ completed_jids = Gitlab::SidekiqStatus.completed_jids(jids_and_ids.keys)
+ return unless completed_jids.any?
+
+ completed_ids = jids_and_ids.values_at(*completed_jids)
+
+ # We select the export states again, because they may have transitioned from
+ # started to finished while we were looking up their Sidekiq status.
+ completed_jobs = enqueued_exports.where(id: completed_ids)
+
+ Sidekiq.logger.info(
+ message: 'Marked stuck export jobs as failed',
+ job_ids: completed_jobs.map(&:jid)
+ )
+
+ completed_jobs.each do |job|
+ job.fail_op
+ end.count
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def enqueued_exports
+ ProjectExportJob.with_status([:started, :queued])
+ end
+end
+# rubocop:enable Scalability/IdempotentWorker