Как реализовать переключение между приоритетными и менее приоритетными очередями при ограниченном числе воркеров
Что проверяют
Понимаете ли вы, что строгий приоритет при фиксированном числе воркеров приводит к голоданию низкой очереди, и умеете ли это обойти. Следом обычно спрашивают, что делать, если все воркеры заняты долгими неважными задачами, а пришла срочная.
Как отвечать
Начните с простого варианта и его ограничения:
- одна
queue.PriorityQueue: кладёте кортеж(priority, seq, task), гдеseqберётся изitertools.count(). Блокирующийget()не крутит процессор впустую. Минус: пока приходят срочные задачи, обычные не выполняются никогда; - взвешенный опрос двух очередей: воркер берёт, например, три задачи из приоритетной на одну из обычной. Низкая очередь движется медленнее, но гарантированно;
- старение: приоритет задачи растёт со временем ожидания, и старая обычная задача в итоге обгоняет свежую срочную;
- резерв воркеров: из N воркеров часть обслуживает только срочные задачи, остальные берут любые. Число одновременных обычных задач ограничивает
threading.Semaphore, чтобы они не заняли весь пул.
Это и ответ на уточняющий вопрос: вытеснить запущенную задачу в Python нельзя, поэтому ёмкость под срочное держат заранее.
Про среду исполнения: потоки подходят для задач с ожиданием сети и диска, для счётных задач из-за GIL нужен ProcessPoolExecutor. В asyncio есть свой asyncio.PriorityQueue, а число одновременных задач ограничивает asyncio.Semaphore.
В продакшене это обычно делают через брокер. В Celery заводят отдельные очереди и отдельных воркеров: один слушает только срочную, другой обе. Важная деталь: worker_prefetch_multiplier=1 и task_acks_late, иначе воркер заранее набирает пачку обычных задач, и срочная ждёт за ними.
Где спотыкаются
Опрашивают очереди через get_nowait() в цикле. Воркер либо жжёт процессор на пустых очередях, либо выходит, как только они на миг опустели, и задачи, пришедшие позже, никто не возьмёт.
Кладут в PriorityQueue пары (priority, task). При равных приоритетах Python сравнивает сами задачи и падает с TypeError, если они несравнимы. Для этого и нужен счётчик.
Реализуют строгий приоритет и не говорят о голодании. А для интервьюера это главный пункт вопроса.