Solid Queue는 단순성과 성능을 염두에 두고 설계된 Active Job용 DB 기반 대기열 백엔드입니다.
일반 작업 대기열 추가 및 처리 외에도 Solid Queue는 지연된 작업, 동시성 제어, 반복 작업, 대기열 일시 중지, 작업당 숫자 우선 순위, 대기열 순서별 우선 순위 및 대량 대기열 추가(Active Job의 perform_all_later
에 대한 enqueue_all
)를 지원합니다.
Solid Queue는 MySQL, PostgreSQL 또는 SQLite와 같은 SQL 데이터베이스와 함께 사용할 수 있으며, 가능한 경우 FOR UPDATE SKIP LOCKED
절을 활용하여 작업 폴링 시 잠금 대기 및 차단을 방지합니다. 재시도, 삭제, 오류 처리, 직렬화 또는 지연을 위해 Active Job을 사용하며 Ruby on Rails의 멀티스레딩과 호환됩니다.
Solid Queue는 새로운 Rails 8 애플리케이션에서 기본적으로 구성됩니다. 하지만 이전 버전을 실행 중인 경우 다음 단계에 따라 수동으로 추가할 수 있습니다.
bundle add solid_queue
bin/rails solid_queue:install
그러면 Solid Queue가 프로덕션 활성 작업 백엔드로 구성되고 구성 파일 config/queue.yml
및 config/recurring.yml
생성되며 db/queue_schema.rb
생성됩니다. 또한 Solid Queue를 시작하는 데 사용할 수 있는 bin/jobs
실행 가능 래퍼도 생성됩니다.
이 작업을 완료한 후에는 config/database.yml
에 대기열 데이터베이스에 대한 구성을 추가해야 합니다. SQLite를 사용하는 경우 다음과 같습니다.
production :
primary :
<< : *default
database : storage/production.sqlite3
queue :
<< : *default
database : storage/production_queue.sqlite3
migrations_paths : db/queue_migrate
...또는 MySQL/PostgreSQL/Trilogy를 사용하는 경우:
production :
primary : &primary_production
<< : *default
database : app_production
username : app
password : <%= ENV["APP_DATABASE_PASSWORD"] %>
queue :
<< : *primary_production
database : app_production_queue
migrations_paths : db/queue_migrate
참고: bin/rails solid_queue:install
호출하면 config.solid_queue.connects_to = { database: { writing: :queue } }
config/environments/production.rb
에 자동으로 추가되므로 추가 구성이 필요하지 않습니다. 이를 일치시키려면 database.yml
의 queue
이름을 사용해야 합니다!) 그러나 다른 환경(스테이징 또는 개발 등)에서 Solid Queue를 사용하려면 해당 환경 파일에 해당 config.solid_queue.connects_to
행을 수동으로 추가해야 합니다. 그리고 항상 그렇듯이 config/database.yml
에서 데이터베이스에 사용하는 이름이 config.solid_queue.connects_to
에서 사용하는 이름과 일치하는지 확인하세요.
그런 다음 프로덕션에서 db:prepare
실행하여 데이터베이스가 생성되고 스키마가 로드되었는지 확인합니다.
이제 작업을 수행하는 서버에서 bin/jobs
실행하여 작업 처리를 시작할 준비가 되었습니다. 그러면 기본 구성을 사용하여 모든 대기열의 작업 처리가 시작됩니다. Solid Queue 구성에 대한 자세한 내용은 아래를 참조하세요.
소규모 프로젝트의 경우 웹 서버와 동일한 시스템에서 Solid Queue를 실행할 수 있습니다. 확장할 준비가 되면 Solid Queue는 기본적으로 수평 확장을 지원합니다. 웹 서버와 별도의 서버에서 Solid Queue를 실행하거나 동시에 여러 컴퓨터에서 bin/jobs
실행할 수도 있습니다. 구성에 따라 일부 시스템을 디스패처만 실행하거나 작업자만 실행하도록 지정할 수 있습니다. 이에 대한 자세한 내용은 구성 섹션을 참조하세요.
참고 : 향후 스키마 변경 사항은 정기적인 마이그레이션 형태로 제공됩니다.
별도의 데이터베이스에서 Solid Queue를 실행하는 것이 권장되지만 앱과 대기열 모두에 하나의 단일 데이터베이스를 사용하는 것도 가능합니다. 다음 단계를 따르세요.
db/queue_schema.rb
의 내용을 일반 마이그레이션에 복사하고 db/queue_schema.rb
삭제합니다.production.rb
에서 config.solid_queue.connects_to
제거합니다.bin/jobs
실행할 준비가 되었습니다. 여러 데이터베이스가 없으므로, database.yml
기본 데이터베이스와 큐 데이터베이스가 필요하지 않습니다.
한 번에 하나의 작업을 전환하여 Solid Queue를 점진적으로 채택하려는 경우 config.active_job.queue_adapter
이전 백엔드로 설정된 상태로 두고 이동 중인 작업에서 queue_adapter
직접 설정하면 됩니다.
# app/jobs/my_job.rb
class MyJob < ApplicationJob
self . queue_adapter = :solid_queue
# ...
end
Solid Queue는 FOR UPDATE SKIP LOCKED
지원하므로 MySQL 8+ 또는 PostgreSQL 9.5+와 함께 사용할 때 가장 높은 처리량을 제공하도록 설계되었습니다. 이전 버전에서도 사용할 수 있지만 이 경우 동일한 대기열에 대해 여러 작업자를 실행하면 잠금 대기가 발생할 수 있습니다. 소규모 애플리케이션에서는 SQLite와 함께 사용할 수도 있습니다.
Solid Queue에는 여러 유형의 액터가 있습니다:
solid_queue_ready_executions
테이블에서 작동합니다.solid_queue_scheduled_executions
테이블에서 solid_queue_ready_executions
테이블로 이동하는 것입니다. 또한 동시성 제어와 관련된 일부 유지 관리 작업도 수행합니다.Solid Queue의 감독자는 각 감독 대상 작업자/디스패처/스케줄러에 대해 별도의 프로세스를 포크합니다.
기본적으로 Solid Queue는 config/queue.yml
에서 구성을 찾으려고 시도하지만 다음과 같이 환경 변수 SOLID_QUEUE_CONFIG
를 사용하거나 bin/jobs
와 함께 -c/--config_file
옵션을 사용하여 다른 경로를 설정할 수 있습니다.
bin/jobs -c config/calendar.yml
이 구성은 다음과 같습니다.
production :
dispatchers :
- polling_interval : 1
batch_size : 500
concurrency_maintenance_interval : 300
workers :
- queues : " * "
threads : 3
polling_interval : 2
- queues : [ real_time, background ]
threads : 5
polling_interval : 0.1
processes : 3
모든 것은 선택 사항입니다. 구성이 전혀 제공되지 않으면 Solid Queue는 기본 설정을 사용하여 하나의 디스패처와 하나의 작업자로 실행됩니다. 디스패처나 작업자만 실행하려면 구성에 해당 섹션만 포함하면 됩니다. 예를 들어 다음 구성을 사용합니다.
production :
dispatchers :
- polling_interval : 1
batch_size : 500
concurrency_maintenance_interval : 300
감독자는 1명의 파견자를 운영하고 직원은 운영하지 않습니다.
다양한 옵션에 대한 개요는 다음과 같습니다.
polling_interval
: 작업자와 디스패처가 추가 작업을 확인하기 전에 기다리는 시간 간격(초)입니다. 이 시간의 기본값은 디스패처의 경우 1
초, 작업자의 경우 0.1
초입니다.
batch_size
: 디스패처는 이 크기의 배치로 작업을 전달합니다. 기본값은 500입니다.
concurrency_maintenance_interval
: 차단 해제할 수 있는 차단된 작업을 확인하기 전에 디스패처가 기다리는 시간 간격(초)입니다. 이 설정에 대해 자세히 알아보려면 동시성 제어에 대해 자세히 알아보세요. 기본값은 600
초입니다.
queues
: 작업자가 작업을 선택할 대기열 목록입니다. *
사용하여 모든 대기열을 나타낼 수 있습니다(이는 기본값이며 이를 생략할 경우 얻게 되는 동작이기도 합니다). 단일 대기열 또는 대기열 목록을 배열로 제공할 수 있습니다. 작업은 해당 대기열에서 순서대로 폴링됩니다. 예를 들어 [ real_time, background ]
사용하면 real_time
에 대기 중인 작업이 더 이상 없으면 background
에서 작업이 가져오지 않습니다. 접두사로 시작하는 대기열과 일치하도록 와일드카드가 포함된 접두사를 제공할 수도 있습니다. 예를 들어:
staging :
workers :
- queues : staging*
threads : 3
polling_interval : 5
이렇게 하면 staging
로 시작하는 모든 대기열에서 작업을 가져오는 작업자가 생성됩니다. 와일드카드 *
는 단독으로 사용하거나 큐 이름 끝에만 사용할 수 있습니다. *_some_queue
와 같은 대기열 이름을 지정할 수 없습니다. 이는 무시됩니다.
마지막으로 [ staging*, background ]
와 같이 정확한 이름과 접두사를 결합할 수 있으며 순서에 관한 동작은 정확한 이름만 사용한 경우와 동일합니다.
우선순위와 결합된 대기열 순서의 작동 방식과 작업자당 대기열을 지정하는 방식이 성능에 어떤 영향을 미칠 수 있는지 아래 섹션을 확인하세요.
threads
: 각 작업자가 작업을 실행해야 하는 스레드 풀의 최대 크기입니다. 각 작업자는 대기열에서 최대 이 수의 작업을 가져와 실행할 스레드 풀에 게시합니다. 기본적으로 이는 3
입니다. 작업자에게만 이 설정이 있습니다.
processes
: 주어진 설정으로 감독자가 분기할 작업자 프로세스의 수입니다. 기본적으로 이는 1
이며 단일 프로세스입니다. 이 설정은 동일한 구성을 가진 대기열에 둘 이상의 CPU 코어를 전용으로 사용하려는 경우에 유용합니다. 작업자에게만 이 설정이 있습니다.
concurrency_maintenance
: 디스패처가 동시성 유지 관리 작업을 수행할지 여부입니다. 이는 기본적으로 true
이며 동시성 제어를 사용하지 않고 이를 비활성화하려는 경우 또는 여러 디스패처를 실행하고 그 중 일부가 다른 작업을 수행하지 않고 작업을 디스패치하도록 하려는 경우 유용합니다.
위에서 언급했듯이 작업자에 대한 대기열 목록을 지정하면 real_time,background
목록과 같이 지정된 순서대로 폴링됩니다. 대기 중인 작업이 더 이상 없으면 background
에서 작업이 수행되지 않습니다. real_time
.
Active Job은 작업을 대기열에 추가할 때 양의 정수 우선순위도 지원합니다. Solid Queue에서는 값이 작을수록 우선순위가 높습니다. 기본값은 0
입니다.
이는 동일한 대기열에서 중요도나 긴급도가 다른 작업을 실행할 때 유용합니다. 동일한 대기열 내에서는 작업이 우선순위에 따라 선택되지만 대기열 목록에서는 대기열 순서가 우선하므로 real_time,background
사용하는 이전 예에서는 real_time
대기열의 작업이 background
의 작업보다 먼저 선택됩니다. background
큐에 있는 큐의 우선순위가 더 높은(더 작은 값) 설정되어 있는 경우에도 마찬가지입니다.
대기열 순서와 우선 순위를 혼합하지 말고 둘 중 하나를 선택하는 것이 좋습니다. 이렇게 하면 작업 실행 순서가 더 간단해집니다.
폴링 성능을 유지하고 포함 인덱스가 항상 사용되도록 하기 위해 Solid Queue는 두 가지 유형의 폴링 쿼리만 수행합니다.
-- No filtering by queue
SELECT job_id
FROM solid_queue_ready_executions
ORDER BY priority ASC , job_id ASC
LIMIT ?
FOR UPDATE SKIP LOCKED;
-- Filtering by a single queue
SELECT job_id
FROM solid_queue_ready_executions
WHERE queue_name = ?
ORDER BY priority ASC , job_id ASC
LIMIT ?
FOR UPDATE SKIP LOCKED;
첫 번째 항목(큐별 필터링 없음)은 다음을 지정할 때 사용됩니다.
queues : *
모든 대기열을 타겟팅하려고 하므로 일시 중지된 대기열이 없습니다.
다른 경우에는 정렬할 인덱스를 사용하기 위해 한 번에 하나의 대기열로만 필터링할 수 있기 때문에 필터링할 대기열 목록이 순서대로 있어야 합니다. 이는 대기열을 다음과 같이 지정하는 경우를 의미합니다.
queues : beta*
먼저 다음과 같은 쿼리를 사용하여 해당 접두사와 일치하는 모든 기존 대기열 목록을 가져와야 합니다.
SELECT DISTINCT (queue_name)
FROM solid_queue_ready_executions
WHERE queue_name LIKE ' beta% ' ;
인덱스의 가장 왼쪽 열에 대한 이러한 유형의 DISTINCT
쿼리는 Loose Index Scan이라는 기술 덕분에 MySQL에서 매우 빠르게 수행될 수 있습니다. 그러나 PostgreSQL과 SQLite는 이 기술을 구현하지 않습니다. 즉, 대기열이 매우 깊어져서 solid_queue_ready_executions
테이블이 매우 큰 경우 이 쿼리가 느려질 것입니다. 일반적으로 solid_queue_ready_executions
테이블은 작지만 이런 일이 발생할 수 있습니다.
접두사를 사용하는 것과 마찬가지로 대기열을 일시 중지한 경우에도 같은 일이 발생합니다. 다음과 같은 쿼리를 사용하여 모든 대기열 목록을 가져와야 하기 때문입니다.
SELECT DISTINCT (queue_name)
FROM solid_queue_ready_executions
그런 다음 일시 중지된 항목을 제거하세요. 일반적으로 일시 중지는 드물게 사용해야 하며 특별한 상황에서 짧은 기간 동안 사용해야 합니다. 더 이상 대기열의 작업을 처리하고 싶지 않은 경우 가장 좋은 방법은 대기열 목록에서 해당 작업을 제거하는 것입니다.
요약하자면, 폴링 시 최적의 성능을 보장하려는 경우 가장 좋은 방법은 항상 정확한 이름을 지정하고 대기열을 일시 중지하지 않는 것입니다.
다음을 수행하십시오.
queues : background, backend
대신에:
queues : back*
Solid Queue의 작업자는 스레드 풀을 사용하여 위의 threads
매개변수를 통해 구성 가능한 여러 스레드에서 작업을 실행합니다. 이 외에도 한 시스템의 여러 프로세스(다른 작업자 또는 위의 processes
매개변수를 통해 구성 가능) 또는 수평 확장을 통해 병렬성을 달성할 수 있습니다.
감독자는 이러한 프로세스 관리를 담당하며 다음 신호에 응답합니다.
TERM
, INT
: 정상적인 종료를 시작합니다. 감독자는 감독되는 프로세스에 TERM
신호를 보내고 완료될 때까지 SolidQueue.shutdown_timeout
시간까지 기다립니다. 그때까지 감독되는 프로세스가 아직 남아 있으면 종료해야 함을 나타내기 위해 QUIT
신호를 보냅니다.QUIT
: 즉시 종료를 시작합니다. 감독자는 감독 대상 프로세스에 QUIT
신호를 보내 즉시 종료되도록 합니다. QUIT
신호를 수신할 때 작업자에게 아직 진행 중인 작업이 있으면 프로세스가 등록 취소될 때 해당 작업이 대기열로 반환됩니다.
프로세스가 종료되기 전에 정리할 기회가 없는 경우(예: 누군가 케이블을 어딘가에서 당기는 경우) 진행 중인 작업은 해당 작업을 실행하는 프로세스에 의해 요청된 상태로 남아 있을 수 있습니다. 프로세스는 하트비트를 보내고 감독자는 만료된 하트비트가 있는 프로세스를 확인하고 정리하여 요청된 모든 작업을 대기열로 다시 해제합니다. 프로세스가 중단된 것으로 간주하도록 하트비트 빈도와 임계값을 모두 구성할 수 있습니다. 이에 대해서는 아래 섹션을 참조하세요.
config/application.rb
또는 config/environments/production.rb
구성 파일의 config.solid_queue.connects_to
옵션을 통해 Solid Queue에서 사용하는 데이터베이스를 구성할 수 있습니다. 기본적으로 설치 중에 설정한 데이터베이스 구성과 일치하도록 queue
이라는 쓰기 및 읽기에 단일 데이터베이스가 사용됩니다.
여러 데이터베이스에 대해 Active Record에서 사용할 수 있는 모든 옵션을 여기에서 사용할 수 있습니다.
Solid Queue에서는 감독자의 삶에서 두 가지 다른 지점에 연결할 수 있습니다.
start
: 감독자가 부팅을 마친 후 작업자와 디스패처를 포크하기 직전입니다.stop
: 신호( TERM
, INT
또는 QUIT
)를 수신한 후 및 정상 종료 또는 즉시 종료를 시작하기 직전.그리고 노동자의 삶에서 두 가지 다른 점으로:
worker_start
: 작업자가 부팅을 마친 후와 폴링 루프를 시작하기 직전입니다.worker_stop
: 신호( TERM
, INT
또는 QUIT
)를 받은 후 및 정상 종료 또는 즉시 종료를 시작하기 직전(그냥 exit!
)이 작업을 수행하려면 블록에 다음 방법을 사용할 수 있습니다.
SolidQueue . on_start
SolidQueue . on_stop
SolidQueue . on_worker_start
SolidQueue . on_worker_stop
예를 들어:
SolidQueue . on_start { start_metrics_server }
SolidQueue . on_stop { stop_metrics_server }
여러 후크를 추가하기 위해 여러 번 호출할 수 있지만 Solid Queue가 시작되기 전에 발생해야 합니다. 초기화 프로그램은 이를 수행하기에 좋은 장소입니다.
참고 : 이 섹션의 설정은 config/application.rb
또는 환경 구성에서 다음과 같이 설정되어야 합니다: config.solid_queue.silence_polling = true
Solid Queue의 작동 방식을 제어하는 몇 가지 설정도 설정할 수 있습니다.
logger
: Solid Queue에서 사용하려는 로거입니다. 기본값은 앱 로거입니다.
app_executor
: 비동기 작업을 래핑하는 데 사용되는 Rails 실행기, 기본값은 앱 실행기입니다.
on_thread_error
: 발생한 예외를 인수로 사용하는 Solid Queue 스레드 내에 오류가 있을 때 호출할 사용자 정의 람다/Proc입니다. 기본값은
-> ( exception ) { Rails . error . report ( exception , handled : false ) }
작업 실행 중에 발생한 오류에는 사용되지 않습니다 . Job에서 발생하는 오류는 Active Job의 retry_on
또는 discard_on
에 의해 처리되며 궁극적으로 Job이 실패하게 됩니다. 이는 Solid Queue 자체 내에서 발생하는 오류에 대한 것입니다.
use_skip_locked
: 읽기 잠금 수행 시 FOR UPDATE SKIP LOCKED
사용할지 여부. 이는 앞으로 자동으로 감지될 예정이며 현재로서는 데이터베이스가 이를 지원하지 않는 경우에만 이를 false
로 설정하면 됩니다. MySQL의 경우 버전 < 8이고 PostgreSQL의 경우 버전 < 9.5입니다. SQLite를 사용하는 경우 쓰기가 순차적이므로 아무런 효과가 없습니다.
process_heartbeat_interval
: 모든 프로세스가 따르는 하트비트 간격(기본값은 60초)
process_alive_threshold
: 마지막 하트비트 이후 프로세스가 죽은 것으로 간주될 때까지 기다리는 시간(기본값은 5분)입니다.
shutdown_timeout
: 감독자가 즉시 종료를 요청하는 QUIT
버전을 보내기 전에 감독되는 프로세스에 TERM
신호를 보낸 후 대기하는 시간입니다. 기본값은 5초입니다.
silence_polling
: 워커와 디스패처 모두에 대해 폴링할 때 생성된 Active Record 로그를 침묵할지 여부. 기본값은 true
입니다.
supervisor_pidfile
: 동일한 호스트에서 둘 이상의 감독자가 실행되는 것을 방지하기 위해 또는 상태 확인에 사용하려는 경우 감독자가 부팅할 때 생성할 pidfile의 경로입니다. 기본적으로는 nil
입니다.
preserve_finished_jobs
: 완료된 작업을 solid_queue_jobs
테이블에 유지할지 여부 - 기본값은 true
입니다.
clear_finished_jobs_after
: preserve_finished_jobs
가 true인 경우 완료된 작업을 유지하는 기간(기본값은 1일) 참고: 현재 완료된 작업은 자동으로 정리되지 않습니다. SolidQueue::Job.clear_finished_in_batches
주기적으로 호출하여 이 작업을 수행해야 하지만 이는 가까운 시일 내에 자동으로 수행됩니다.
default_concurrency_control_period
: 동시성 제어에서 duration
매개변수의 기본값으로 사용할 값입니다. 기본값은 3분입니다.
Solid Queue는 작업을 대기열에 추가할 때 발생하는 모든 활성 레코드 오류에 대해 SolidQueue::Job::EnqueueError
발생시킵니다. ActiveJob::EnqueueError
발생시키지 않는 이유는 이 오류가 Active Job에 의해 처리되어 perform_later
false
반환하고 job.enqueue_error
설정하여 해당 작업을 perform_later
에 전달해야 하는 블록으로 만들기 때문입니다. 이는 자신의 작업에 매우 잘 작동하지만, perform_later
에 대한 호출을 제어할 수 없기 때문에 Rails나 Turbo::Streams::BroadcastJob
또는 ActiveStorage::AnalyzeJob
과 같은 다른 gem에 의해 대기열에 추가된 작업의 경우 실패를 처리하기가 매우 어렵습니다. 그 경우에는.
반복되는 작업의 경우 해당 작업을 대기열에 넣을 때 이러한 오류가 발생하면 처리되고 기록되지만 버블링되지는 않습니다.
Solid Queue는 동시성 제어를 통해 Active Job을 확장합니다. 이를 통해 특정 유형 또는 특정 인수를 사용하여 동시에 실행할 수 있는 작업 수를 제한할 수 있습니다. 이러한 방식으로 제한되면 작업 실행이 차단되고 다른 작업이 완료되어 차단이 해제될 때까지 또는 설정된 만료 시간(동시성 제한 기간 )이 경과한 후에도 작업이 차단된 상태로 유지됩니다. 작업은 결코 폐기되거나 손실되지 않으며 차단만 됩니다.
class MyJob < ApplicationJob
limits_concurrency to : max_concurrent_executions , key : -> ( arg1 , arg2 , ** ) { ... } , duration : max_interval_to_guarantee_concurrency_limit , group : concurrency_group
# ...
key
유일한 필수 매개변수이며 작업 인수를 매개변수로 받는 기호, 문자열 또는 proc일 수 있으며 함께 제한해야 하는 작업을 식별하는 데 사용됩니다. proc이 Active Record 레코드를 반환하면 키는 해당 클래스 이름과 id
에서 생성됩니다.to
기본적으로 1
입니다.duration
기본적으로 SolidQueue.default_concurrency_control_period
으로 설정되며, 기본값은 3 minutes
이지만 구성할 수도 있습니다.group
서로 다른 작업 클래스의 동시성을 함께 제어하는 데 사용됩니다. 기본값은 작업 클래스 이름입니다. 작업에 이러한 제어가 포함되면 동일한 key
생성하는 최대 작업 수( to
표시됨)가 동시에 수행되도록 보장하며 이 보장은 duration
에 추가된 각 작업에 대해 지속됩니다. 실행 순서 에 대한 보장은 없으며 동시에 수행되는 작업(중복)에 대해서만 보장됩니다.
동시성 제한은 대기열에 추가할 때 세마포어 개념을 사용하고 다음과 같이 작동합니다. 작업이 대기열에 추가되면 동시성 제어를 지정하는지 확인합니다. 그렇다면 계산된 동시성 키에 대한 세마포어를 확인합니다. 세마포어가 열려 있으면 이를 요청하고 작업을 Ready 로 설정합니다. 준비(Ready)는 작업자가 실행을 위해 선택할 수 있음을 의미합니다. 작업 실행이 완료되면(성공적이거나 실패하여 실행 실패로 이어짐) 세마포어에 신호를 보내고 동일한 키(있는 경우)를 사용하여 다음 작업의 차단을 해제하려고 시도합니다. 다음 작업 차단을 해제한다는 것은 해당 작업을 즉시 실행하는 것이 아니라 차단 에서 준비 로 이동하는 것을 의미합니다. 첫 번째 작업이 세마포어를 해제하고 다음 작업의 차단을 해제하는 것을 방해하는 일이 발생할 수 있으므로(예: 작업자가 실행 중인 시스템에서 누군가 플러그를 뽑는 경우) 안전 장치로서의 duration
있습니다. 일정 기간 이상 차단된 작업은 해제될 후보이지만 동시성 규칙이 허용하는 만큼만 해당 작업이 세마포어 댄스 확인을 거쳐야 합니다. 이는 duration
이 실제로 대기열에 추가되거나 실행 중인 작업에 관한 것이 아니라 대기가 차단된 작업에 관한 것임을 의미합니다.
예를 들어:
class DeliverAnnouncementToContactJob < ApplicationJob
limits_concurrency to : 2 , key : -> ( contact ) { contact . account } , duration : 5 . minutes
def perform ( contact )
# ...
contact
와 account
ActiveRecord
레코드입니다. 이 경우 동일한 계정에 대해 DeliverAnnouncementToContact
종류의 최대 2개 작업이 동시에 실행되도록 보장합니다. 어떤 이유로든 해당 작업 중 하나가 5분 이상 걸리거나 획득 후 5분 이내에 동시성 잠금을 해제(세마포 신호 전달)하지 않으면 동일한 키를 가진 새 작업이 잠금을 얻을 수 있습니다.
group
사용하는 또 다른 예를 살펴보겠습니다.
class Box :: MovePostingsByContactToDesignatedBoxJob < ApplicationJob
limits_concurrency key : -> ( contact ) { contact } , duration : 15 . minutes , group : "ContactActions"
def perform ( contact )
# ...
class Bundle :: RebundlePostingsJob < ApplicationJob
limits_concurrency key : -> ( bundle ) { bundle . contact } , duration : 15 . minutes , group : "ContactActions"
def perform ( bundle )
# ...
이 경우 ID가 123
인 연락처 레코드에 대해 대기열에 포함된 Box::MovePostingsByContactToDesignatedBoxJob
작업과 연락처 123
참조하는 번들 레코드에 대해 동시에 대기열에 추가된 다른 Bundle::RebundlePostingsJob
작업이 있는 경우 그 중 하나만 진행할 수 있습니다. 다른 하나는 첫 번째 작업이 완료될 때까지(또는 먼저 무슨 일이 일어나든 15분이 경과할 때까지) 차단된 상태로 유지됩니다.
duration
설정은 디스패처에 대해 설정한 concurrency_maintenance_interval
값에 간접적으로 의존합니다. 이는 차단된 작업을 확인하고 차단 해제하는 빈도가 되기 때문입니다. 일반적으로 모든 작업이 해당 기간 내에 잘 완료되도록 duration
설정하고, 동시성 유지 관리 작업을 문제가 발생할 경우를 대비한 안전 장치로 생각해야 합니다.
작업은 우선순위에 따라 차단 해제되지만 작업 차단 해제에는 대기열 순서가 고려되지 않습니다. 즉, 동시성 그룹을 공유하지만 다른 대기열에 있는 작업 그룹이 있거나 다른 대기열에 대기열에 넣은 동일한 클래스의 작업이 있는 경우 차단을 해제할 때 작업자에 대해 설정한 대기열 순서가 고려되지 않습니다. 것들. 그 이유는 실행되는 작업이 다음 작업의 차단을 해제하고 작업 자체는 특정 작업자의 대기열 순서에 대해 알지 못하고(다른 대기열 순서를 가진 다른 작업자가 있을 수도 있음) 우선순위만 알 수 있기 때문입니다. 차단된 작업이 차단 해제되어 폴링이 가능해지면 대기열 순서에 따라 작업자가 해당 작업을 선택합니다.
마지막으로 자동 또는 수동으로 재시도되는 실패한 작업은 대기열에 추가된 새 작업과 동일한 방식으로 작동합니다. 즉, 열린 세마포어를 얻기 위해 대기열에 들어가며, 이를 얻을 때마다 실행됩니다. 과거에 이미 개방형 수기 신호를 얻었는지 여부는 중요하지 않습니다.
Solid Queue에는 자동 재시도 메커니즘이 포함되어 있지 않으며 이를 위해 Active Job을 사용합니다. 실패한 작업은 시스템에 보관되며 이에 대해 실패한 실행 ( solid_queue_failed_executions
테이블의 레코드)이 생성됩니다. 작업은 수동으로 삭제되거나 다시 대기열에 포함될 때까지 해당 위치에 유지됩니다. 콘솔에서 다음과 같이 이 작업을 수행할 수 있습니다.
failed_execution = SolidQueue :: FailedExecution . find ( ... ) # Find the failed execution related to your job
failed_execution . error # inspect the error
failed_execution . retry # This will re-enqueue the job as if it was enqueued for the first time
failed_execution . discard # This will delete the job from the system
하지만 무엇보다도 실패한 작업을 검사하고 재시도/삭제할 수 있는 대시보드인 Mission_control-jobs를 살펴보는 것이 좋습니다.
Sentry 또는 Rollbar와 같이 Rails와 통합되는 일부 오류 추적 서비스는 Active Job에 연결되어 작업 실행 중에 발생하는 처리되지 않은 오류를 자동으로 보고합니다. 그러나 오류 추적 시스템이 그렇지 않거나 사용자 정의 보고가 필요한 경우 Active Job에 직접 연결할 수 있습니다. 이를 수행하는 가능한 방법은 다음과 같습니다.
# application_job.rb
class ApplicationJob < ActiveJob :: Base
rescue_from ( Exception ) do | exception |
Rails . error . report ( exception )
raise exception
end
end
ActionMailer::MailDeliveryJob
에서도 위의 논리를 복제해야 합니다. 이는 ActionMailer
ApplicationJob
에서 상속받지 않고 대신 ActiveJob::Base
에서 상속된 ActionMailer::MailDeliveryJob
사용하기 때문입니다.
# application_mailer.rb
class ApplicationMailer < ActionMailer :: Base
ActionMailer :: MailDeliveryJob . rescue_from ( Exception ) do | exception |
Rails . error . report ( exception )
raise exception
end
end
Solid Queue의 Supervisor를 Puma와 함께 실행하고 Puma에서 모니터링하고 관리하고 싶다면 Puma 플러그인을 제공합니다. 추가하기 만하면됩니다.
plugin :solid_queue
puma.rb
구성에.
이는 매우 까다로울 수 있고 많은 사람들이 걱정할 필요가 없기 때문에 기본적으로 Solid Queue는 기본 앱과 다른 데이터베이스에 구성되어 있으며 Active Job의 내장 덕분에 진행 중인 트랜잭션이 커밋될 때까지 작업 대기열 추가가 연기됩니다. 이것을 할 수 있는 능력. 이는 앱과 동일한 DB에서 Solid Queue를 실행하더라도 이러한 트랜잭션 무결성을 활용할 수 없음을 의미합니다.
이를 변경하려면 config.active_job.enqueue_after_transaction_commit
never
로 설정할 수 있습니다. 작업별로 설정할 수도 있습니다.
이를 never
로 설정했지만 실수로 트랜잭션 무결성을 유지하지 않으려면 다음을 확인하세요.
특정 데이터에 의존하는 작업은 항상 after_commit
콜백을 통해 대기열에 추가되거나 작업이 대기열에 추가되기 전에 작업에서 사용할 데이터가 데이터베이스에 커밋되었다고 확신하는 위치에서 대기열에 추가됩니다.
또는 앱과 동일하더라도 Solid Queue에 대해 다른 데이터베이스를 구성하여 앱에 대해 요청을 처리하거나 작업을 실행하는 스레드의 다른 연결이 작업을 큐에 추가하는 데 사용되도록 합니다. 예를 들어:
class ApplicationRecord < ActiveRecord :: Base
self . abstract_class = true
connects_to database : { writing : :primary , reading : :replica }
config . solid_queue . connects_to = { database : { writing : :primary , reading : :replica } }
Solid Queue는 cron 작업과 같이 정기적으로 미래의 특정 시간에 실행되는 반복 작업 정의를 지원합니다. 이는 스케줄러 프로세스에 의해 관리되며 자체 구성 파일에 정의됩니다. 기본적으로 파일은 config/recurring.yml
에 있지만 다음과 같이 환경 변수 SOLID_QUEUE_RECURRING_SCHEDULE
사용하거나 bin/jobs
와 함께 --recurring_schedule_file
옵션을 사용하여 다른 경로를 설정할 수 있습니다.
bin/jobs --recurring_schedule_file=config/schedule.yml
구성 자체는 다음과 같습니다.
production :
a_periodic_job :
class : MyJob
args : [ 42, { status: "custom_status" } ]
schedule : every second
a_cleanup_task :
command : " DeletedStuff.clear_all "
schedule : every day at 9am
작업은 해시/사전으로 지정되며, 여기서 키는 내부적으로 작업의 키가 됩니다. 각 작업에는 대기열에 넣을 작업 클래스가 될 class
또는 일정에 따라 대기열에 추가될 작업( SolidQueue::RecurringJob
)의 컨텍스트에서 평가될 command
있어야 합니다. solid_queue_recurring
대기열.
각 작업에는 Fugit을 사용하여 구문 분석되는 일정도 있어야 하므로 Fugit이 크론으로 허용하는 모든 항목을 허용합니다. 선택적으로 각 작업에 대해 다음을 제공할 수 있습니다.
args
: 단일 인수, 해시 또는 kwargs를 배열의 마지막 요소로 포함할 수도 있는 인수 배열로 작업에 전달할 인수입니다.위 구성 예의 작업은 다음과 같이 매초 대기열에 추가됩니다.
MyJob . perform_later ( 42 , status : "custom_status" )
queue
: 작업을 대기열에 추가할 때 사용되는 다른 대기열입니다. 없는 경우 작업 클래스에 대해 대기열이 설정됩니다.
priority
: 작업을 대기열에 추가할 때 사용되는 숫자 우선순위 값입니다.
작업은 스케줄러에 의해 해당 시간에 대기열에 추가되며 각 작업은 다음 작업을 예약합니다. 이는 GoodJob이 수행하는 작업에서 많은 영감을 받았습니다.
예를 들어 중복성을 위해 여러 서버가 있고 둘 이상의 서버에서 scheduler
실행하는 경우 동일한 recurring_tasks
구성으로 여러 스케줄러를 실행할 수 있습니다. 동시에 중복된 작업을 대기열에 넣는 것을 방지하기 위해 작업이 대기열에 추가되는 것과 동일한 트랜잭션에 새 solid_queue_recurring_executions
테이블의 항목이 생성됩니다. 이 테이블에는 task_key
및 run_at
에 대한 고유 인덱스가 있으므로 시간당 작업당 하나의 항목만 생성됩니다. 이는 preserve_finished_jobs
가 true
(기본값)로 설정된 경우에만 작동하며 작업을 유지하는 한 보장이 적용됩니다.
참고 : 단일 반복 일정이 지원되므로 동일한 일정을 사용하는 여러 스케줄러가 있을 수 있지만 다른 구성을 사용하는 여러 스케줄러는 있을 수 없습니다.
마지막으로 Solid Queue에서 처리되지 않는 작업을 구성할 수 있습니다. 즉, 앱에서 다음과 같은 작업을 수행할 수 있습니다.
class MyResqueJob < ApplicationJob
self . queue_adapter = :resque
def perform ( arg )
# ..
end
end
Solid Queue에서 이를 구성할 수 있습니다.
my_periodic_resque_job :
class : MyResqueJob
args : 22
schedule : " */5 * * * * "
작업은 perform_later
통해 대기열에 추가되므로 Resque에서 실행됩니다. 그러나 이 경우에는 이에 대한 solid_queue_recurring_execution
기록을 추적하지 않으며 작업이 매번 한 번만 대기열에 추가된다는 보장도 없습니다.
Solid Queue는 resque와 GoodJob에서 영감을 받았습니다. 이 프로젝트는 우리가 많은 것을 배운 훌륭한 사례이므로 확인해 보시기 바랍니다.
이 gem은 MIT 라이선스 조건에 따라 오픈 소스로 제공됩니다.