Asyncio TaskGroup: двухфазная проверка и публикация конфигурации
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Asyncio TaskGroup и gather: задачи на ошибки и отмену корутин.
Двухфазная публикация конфигурации
Центральный диспетчер обновляет настройки серверов кластера строго в два последовательных этапа. На первой фазе подготовленный манифест проверяется параллельно на всех узлах внутри первой asyncio.TaskGroup. Чтобы проверка считалась пройденной, валидатор каждого узла обязан завершиться штатно: как обычная ошибка схемы (Exception), так и досрочная самоотмена корутины (CancelledError) означают, что подтверждение не получено.
Если хотя бы один узел не подтвердил корректность манифеста, первая группа отменяет оставшиеся проверки, вторая фаза вовсе не запускается, а диспетчер фиксирует срыв валидации со списком всех возникших причин отказа. Вторая независимая TaskGroup для применения настроек стартует только тогда, когда получено явное успешное подтверждение от абсолютно всех участников.
Если же сбой случается уже на этапе применения, диспетчер фиксирует срыв второй фазы, сохраняя отметку о том, что предварительную валидацию без ошибок прошли все узлы. Реализуйте этот двухфазный диспетчер и сформируйте итоговый отчёт с указанием фазы, на которой произошёл сбой.
Что уже дано
В среде выполнения доступны вспомогательные функции observe_phase_probe и make_phase_example_observation. В редакторе вам предстоит реализовать саму корутину publish_configuration.
Что нужно сделать
Напишите асинхронную функцию publish_configuration(nodes).
Функция принимает конечную последовательность nodes (от 0 до 8 объектов). Каждый узел предоставляет две асинхронные операции без параметров: await node.validate() и await node.apply(). Штатное завершение вызова (включая возврат None, False, 0 или пустой строки) подтверждает успех операции. Любой метод узла может выбросить исключение Exception или досрочно отменить собственную задачу asyncio.Task.
Правила работы диспетчера:
- Фаза валидации запускается строго в отдельном нативном контексте
asyncio.TaskGroup: проверки всех узлов изnodesвыполняются конкурентно. - Если хотя бы один узел выбросил обычную ошибку либо самоотменился, подтверждение считается неполученным. Диспетчер должен отменить остальные незавершённые проверки в этой группе и ни в коем случае не начинать фазу применения.
- Фаза применения
applyзапускается во второй, полностью отдельнойasyncio.TaskGroupи только тогда, когда валидация всех узлов завершилась штатно. - Если список
nodesпуст, обе фазы считаются успешно пройденными без запуска операций. - К моменту возврата отчёта ни одна внутренняя задача фаз не должна оставаться запущенной в цикле событий.
- Функция должна вернуть словарь со следующими ключами:
status(str):'complete'при успехе обеих фаз,'validation_failed'при срыве валидации,'apply_failed'при срыве применения;validated(bool):True, если абсолютно все валидации завершились штатно (в том числе когда упало последующее применение), иначеFalse;failures(list): список исходных объектовBaseException, приведших к срыву операций (включая исключения отмены незавершённых параллельных задач, отменённых из-за сбоя соседа). Вспомогательные внутренние управляющие исключения не должны попадать в этот список. Порядок элементов в списке не имеет значения.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня publish_configuration и вызывает её с проверочными аргументами.
В примере ниже на вход передаются два узла: валидация обоих завершается штатно, но операция применения второго узла возбуждает подготовленное исключение ValueError. Диспетчер фиксирует статус 'apply_failed', сохраняет validated=True и возвращает список с исходным объектом возникшей ошибки.
Пример использования
import asyncio
class ExampleNode:
def __init__(self, apply_error=None):
self.apply_error = apply_error
async def validate(self):
return True
async def apply(self):
if self.apply_error is not None:
raise self.apply_error
return None
apply_error = ValueError('node-b apply')
nodes = [ExampleNode(), ExampleNode(apply_error)]
report = asyncio.run(publish_configuration(nodes))
print(report['status'], report['validated'])
print([(type(error).__name__, str(error)) for error in report['failures']])
print(report['failures'][0] is apply_error)
Вывод:
apply_failed True
[('ValueError', 'node-b apply')]
True
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Asyncio TaskGroup и gather: что происходит при ошибке корутины
- Asyncio TaskGroup: сбор результатов с отменёнными задачами
- Asyncio gather: отмена оставшихся задач при ошибке
- Asyncio TaskGroup: обработка ошибок параллельного опроса
- Asyncio gather и TaskGroup: соберите исключения конкурентных задач
- Удаление ключа из ChainMap в Python: проверка поведения del
