Commit 63979ec0 authored by Sean McGivern's avatar Sean McGivern

Add worker_class argument to Sidekiq queues APIs

These APIs allow you to delete Sidekiq jobs from a queue. We now support
having multiple worker classes use the same queue, so we need an extra
argument to support that case - otherwise it would be possible to delete
jobs from a less-problematic worker when just meaning to target a
particular worker.

Changelog: changed
parent c123c457
......@@ -8,13 +8,18 @@ module Mutations
ADMIN_MESSAGE = 'You must be an admin to use this mutation'
Gitlab::ApplicationContext::KNOWN_KEYS.each do |key|
::Gitlab::ApplicationContext::KNOWN_KEYS.each do |key|
argument key,
GraphQL::Types::String,
required: false,
description: "Delete jobs matching #{key} in the context metadata"
description: "Delete jobs matching #{key} in the context metadata."
end
argument ::Gitlab::SidekiqQueue::WORKER_KEY,
GraphQL::Types::String,
required: false,
description: 'Delete jobs with the given worker class.'
argument :queue_name,
GraphQL::Types::String,
required: true,
......
......@@ -28,7 +28,7 @@ DELETE /admin/sidekiq/queues/:queue_name
```
| Attribute | Type | Required | Description |
| --------- | -------------- | -------- | ----------- |
|---------------------|--------|----------|----------------------------------------------------------------------------------------------------------------------------------------------|
| `queue_name` | string | yes | The name of the queue to delete jobs from |
| `user` | string | no | The username of the user who scheduled the jobs |
| `project` | string | no | The full path of the project where the jobs were scheduled from |
......@@ -36,6 +36,7 @@ DELETE /admin/sidekiq/queues/:queue_name
| `subscription_plan` | string | no | The subscription plan of the root namespace (GitLab.com only) |
| `caller_id` | string | no | The endpoint or background job that schedule the job (for example: `ProjectsController#create`, `/api/:version/projects/:id`, `PostReceive`) |
| `feature_category` | string | no | The feature category of the background job (for example: `issue_tracking` or `code_review`) |
| `worker_class` | string | no | The class of the background job worker (for example: `PostReceive` or `MergeWorker`) |
At least one attribute, other than `queue_name`, is required.
......
......@@ -563,6 +563,7 @@ Input type: `AdminSidekiqQueuesDeleteJobsInput`
| <a id="mutationadminsidekiqqueuesdeletejobsrootnamespace"></a>`rootNamespace` | [`String`](#string) | Delete jobs matching root_namespace in the context metadata. |
| <a id="mutationadminsidekiqqueuesdeletejobssubscriptionplan"></a>`subscriptionPlan` | [`String`](#string) | Delete jobs matching subscription_plan in the context metadata. |
| <a id="mutationadminsidekiqqueuesdeletejobsuser"></a>`user` | [`String`](#string) | Delete jobs matching user in the context metadata. |
| <a id="mutationadminsidekiqqueuesdeletejobsworkerclass"></a>`workerClass` | [`String`](#string) | Delete jobs with the given worker class. |
#### Fields
......
......@@ -12,11 +12,11 @@ module API
namespace 'queues' do
desc 'Drop jobs matching the given metadata from the Sidekiq queue'
params do
Gitlab::ApplicationContext::KNOWN_KEYS.each do |key|
Gitlab::SidekiqQueue::ALLOWED_KEYS.each do |key|
optional key, type: String, allow_blank: false
end
at_least_one_of(*Gitlab::ApplicationContext::KNOWN_KEYS)
at_least_one_of(*Gitlab::SidekiqQueue::ALLOWED_KEYS)
end
delete ':queue_name' do
result =
......
......@@ -7,6 +7,9 @@ module Gitlab
NoMetadataError = Class.new(StandardError)
InvalidQueueError = Class.new(StandardError)
WORKER_KEY = 'worker_class'
ALLOWED_KEYS = Gitlab::ApplicationContext::KNOWN_KEYS + [WORKER_KEY]
attr_reader :queue_name
def initialize(queue_name)
......@@ -21,8 +24,8 @@ module Gitlab
job_search_metadata =
search_metadata
.stringify_keys
.slice(*Gitlab::ApplicationContext::KNOWN_KEYS)
.transform_keys { |key| "meta.#{key}" }
.slice(*ALLOWED_KEYS)
.transform_keys(&method(:transform_key))
.compact
raise NoMetadataError if job_search_metadata.empty?
......@@ -49,6 +52,14 @@ module Gitlab
private
def transform_key(key)
if Gitlab::ApplicationContext::KNOWN_KEYS.include?(key)
"meta.#{key}"
elsif key == WORKER_KEY
'class'
end
end
def queue
strong_memoize(:queue) do
# Sidekiq::Queue.new always returns a queue, even if it doesn't
......
......@@ -4,15 +4,15 @@ require 'spec_helper'
RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
around do |example|
Sidekiq::Queue.new('authorized_projects').clear
Sidekiq::Queue.new('default').clear
Sidekiq::Testing.disable!(&example)
Sidekiq::Queue.new('authorized_projects').clear
Sidekiq::Queue.new('default').clear
end
def add_job(user, args)
def add_job(args, user:, klass: 'AuthorizedProjectsWorker')
Sidekiq::Client.push(
'class' => 'AuthorizedProjectsWorker',
'queue' => 'authorized_projects',
'class' => klass,
'queue' => 'default',
'args' => args,
'meta.user' => user.username
)
......@@ -20,13 +20,13 @@ RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
describe '#drop_jobs!' do
shared_examples 'queue processing' do
let(:sidekiq_queue) { described_class.new('authorized_projects') }
let(:sidekiq_queue) { described_class.new('default') }
let_it_be(:sidekiq_queue_user) { create(:user) }
before do
add_job(create(:user), [1])
add_job(sidekiq_queue_user, [2])
add_job(sidekiq_queue_user, [3])
add_job([1], user: create(:user))
add_job([2], user: sidekiq_queue_user, klass: 'MergeWorker')
add_job([3], user: sidekiq_queue_user)
end
context 'when the queue is not processed in time' do
......@@ -68,11 +68,19 @@ RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
end
end
context 'when there are jobs matching the class name' do
include_examples 'queue processing' do
let(:search_metadata) { { user: sidekiq_queue_user.username, worker_class: 'AuthorizedProjectsWorker' } }
let(:timeout_deleted) { 1 }
let(:no_timeout_deleted) { 1 }
end
end
context 'when there are no valid metadata keys passed' do
it 'raises NoMetadataError' do
add_job(create(:user), [1])
add_job([1], user: create(:user))
expect { described_class.new('authorized_projects').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) }
expect { described_class.new('default').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) }
.to raise_error(described_class::NoMetadataError)
end
end
......
......@@ -36,7 +36,7 @@ RSpec.describe API::Admin::Sidekiq, :clean_gitlab_redis_queues do
add_job(admin, [2])
add_job(create(:user), [3])
delete api("/admin/sidekiq/queues/authorized_projects?user=#{admin.username}", admin)
delete api("/admin/sidekiq/queues/authorized_projects?user=#{admin.username}&worker_class=AuthorizedProjectsWorker", admin)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq('completed' => true,
......
......@@ -9,7 +9,7 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do
let(:queue) { 'authorized_projects' }
let(:variables) { { user: admin.username, queue_name: queue } }
let(:variables) { { user: admin.username, worker_class: 'AuthorizedProjectsWorker', queue_name: queue } }
let(:mutation) { graphql_mutation(:admin_sidekiq_queues_delete_jobs, variables) }
def mutation_response
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment