Minion 작업 대기열에 대한 우선 순위가 높은 고속 레인

MinionMojolicious과 함께 사용하도록 설계된 고성능 작업 대기열입니다. 이 문서에서는 10.17 release의 새로운 기능 중 하나를 소개합니다.

문제





최근에 work projects의 내 SUSE 중 하나에서 작업 대기열 혼잡 문제가 발생했습니다. 더 이상 필요하지 않은 디스크에서 오래된 파일을 정리하는 것과 같은 다양한 유지 관리 작업을 수행하기 위해 매우 느린 백그라운드 작업이 많이 있습니다. 이러한 작업 중 일부는 완료하는 데 1시간 이상 걸릴 수 있으며 특히 시간이 중요하지 않으므로 우선순위가 매우 낮습니다.

이제 운이 좋지 않아 이러한 1시간 작업의 대량 백로그가 발생했습니다. 그리고 어느 시점에서 대기열에 더 높은 우선 순위 작업이 남지 않았습니다. 따라서 Minion 작업자는 전체 용량에서 1시간 작업만 처리하게 되었습니다.

중요하고 우선순위가 높은 작업이 바로 그 순간에 들어오는 시간이 없었다면 문제가 되지 않았을 것입니다. 남은 추가 용량이 없어서 한 시간 내내 기다려야 했습니다. 그리고 그 우선 순위가 높은 작업은 웹 서비스 사용자에 의해 생성되었으므로 적시에 결과를 받지 못하면 문제가 발생했다고 가정하고 있습니다. 그리고 우리는 많은 버그 보고서를 작성했습니다.

빠른 차선





물론 이것은 특별히 드문 문제는 아닙니다. 더 큰 웹 서비스는 단순히 다양한 서비스 수준에 대해 서로 다른 이름의 대기열이 있는 여러 Minion 작업자를 사용합니다. 유료 고객과 무료 요금제를 사용하는 고객을 위한 하드웨어가 다를 수 있습니다. 그러나 이것은 일부 사용자가 자신의 시스템에 로컬로 설치하는 더 작은 서비스입니다. 따라서 일반적인 상황에서는 미니언 일꾼 한 명이면 충분합니다.

우리에게 필요한 것은 더 간단한 솔루션이었습니다. Mojolicious와 함께 제공되는 prefork 웹 서버에는 이러한 예비 프로세스 개념이 있습니다. 로드가 많은 경우 피크 시간에 요청 처리를 위해 몇 가지 추가 프로세스를 생성할 수 있습니다. 일반적으로 더 적은 수의 프로세스만 미리 포크된 상태로 유지합니다.

Minion worker에도 동일한 개념을 적용할 수 있습니다. 우리는 우선 순위가 높은 작업에만 사용하기 위해 최대 용량에서 소수의 작업을 따로 설정합니다. 따라서 단순한 --jobs 4 설정 대신 이제 --spare 2 설정도 있습니다. 그리고 피크 시간에는 6개의 작업이 병렬로 실행되며 그 중 최소 2개는 높은 우선 순위가 보장됩니다. 모든 작업의 ​​기본 우선 순위는 --spare-min-priority 5이고 우선 순위가 낮은 작업에는 일반적으로 음수 우선 순위 번호가 지정되므로 1로 기본 설정됩니다.

$ script/myapp minion worker --jobs 4 --spare 2
[2021-03-06 13:50:22.02638] [30272] [info] Worker 30272 started
...


구현



Minion은 PostrgeSQL에 의존하기 때문에 실제 구현은 매우 간단했습니다. 예비 프로세스를 생성하는 Perltwo lines과 작업을 대기열에서 빼는 데 사용되는 SQL 쿼리의 한 줄 변경.

UPDATE minion_jobs SET started = NOW(), state = 'active', worker = ?
WHERE id = (
  SELECT id FROM minion_jobs AS j
  WHERE delayed <= NOW() AND id = COALESCE(?, id) AND (parents = '{}' OR NOT EXISTS (
    SELECT 1 FROM minion_jobs WHERE id = ANY (j.parents) AND (
      state = 'active' OR (state = 'failed' AND NOT j.lax)
      OR (state = 'inactive' AND (expires IS NULL OR expires >
-  )) AND queue = ANY (?) AND state = 'inactive' AND task = ANY (?) AND (EXPIRES IS NULL OR expires > NOW())
+  )) AND priority >= COALESCE(?, priority) AND queue = ANY (?) AND state = 'inactive' AND task = ANY (?)
+    AND (EXPIRES IS NULL OR expires > NOW())
  ORDER BY priority DESC, id
  LIMIT 1
  FOR UPDATE SKIP LOCKED
)
RETURNING id, args, retries, task;


운 좋게도 0에 사용하는 인덱스는 최소 우선 순위 확인에도 작동하므로 추가 최적화가 필요하지 않습니다. 여전히 초당 수천 개의 작업을 대기열에서 제거할 수 있습니다. 저는 PostgreSQL을 좋아합니다.

좋은 웹페이지 즐겨찾기