Декоратор Circuit Breaker на Python: остановка вызовов после сбоев
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Декораторы Python: задачи на retry, rate limiter, кэш и Circuit Breaker.
Предохранитель сбоев
Модуль автоматики передаёт команды исполнительным узлам и защищает оборудование от каскадных сбоев. Если целевая операция начинает регулярно отказывать, бессмысленно повторять обращения к аварийному узлу — поток запросов нужно вовремя перекрыть.
За вызовами функции следит специальный декоратор. Любое её успешное выполнение обнуляет счётчик подряд идущих неудач. Если же возникает исключение из заданного кортежа, счётчик увеличивается на единицу, а сама ошибка пробрасывается дальше. Любые другие исключения, не вошедшие в перечень, всплывают сразу же и никак не влияют на накопленный счётчик.
Как только число таких последовательных сбоев достигает установленного порога, предохранитель взводится. С этого момента все дальнейшие обращения немедленно возбуждают системную ошибку блокировки без попытки вызвать реальную функцию. Защита остаётся активной до тех пор, пока состояние предохранителя не будет явно сброшено через специальную функцию управления, закреплённую в замыкании как атрибут готовой обёртки.
Реализуйте фабрику параметризованного декоратора, которая настраивается порогом допустимых ошибок и кортежем отслеживаемых исключений, а также предоставляет метод для ручного сброса состояния.
Что уже дано
В среде исполнения уже объявлен класс исключения CircuitOpen(RuntimeError). Реализовывать его самостоятельно не нужно: он доступен напрямую в коде.
Вам предстоит реализовать функцию-фабрику failure_breaker(limit, exceptions).
Что нужно сделать
Напишите функцию failure_breaker(limit, exceptions).
Функция принимает два аргумента:
limit— целое число от 1 до 100, порог подряд идущих отслеживаемых ошибок, при достижении которого срабатывает блокировка;exceptions— кортеж классов исключений (подклассовBaseException), сбои которых необходимо учитывать.
Фабрика возвращает декоратор, принимающий синхронную целевую функцию func и возвращающий функцию-обёртку wrapper(*args, **kwargs) со следующими правилами работы:
- Для каждой декорированной функции ведётся независимое состояние: начальный счётчик отслеживаемых ошибок равен нулю, а вызовы функции разрешены.
- При успешном выполнении
funcсчётчик ошибок сбрасывается в 0, а обёртка возвращает исходный результат. - Если при вызове
funcвозникает исключение, тип которого совпадает с одним из классов вexceptions(или является его подклассом): - счётчик ошибок увеличивается на 1;
- тот же самый объект исключения пробрасывается дальше без изменений.
- Любые другие исключения пробрасываются дальше в исходном виде, не меняя текущий счётчик ошибок.
- Вызов, на котором счётчик ошибок впервые достигает значения
limit, ещё выполняетfuncи пробрасывает возникшую оригинальную ошибку, но переводит предохранитель в заблокированное состояние. - Все последующие вызовы заблокированной обёртки не должны вызывать
func: они обязаны сразу возбуждать исключениеCircuitOpen. - У объекта обёртки должен присутствовать метод
reset(): вызовwrapper.reset()разблокирует предохранитель и обнуляет счётчик ошибок (возвращаемое значение метода не регламентируется). - Если один и тот же декоратор применяется к нескольким разным функциям, их счётчики и состояния блокировки должны оставаться полностью изолированными друг от друга.
- В пределах одного теста выполняется от 0 до 1000 операций вызова и сброса.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня failure_breaker и вызывает её с проверочными аргументами.
Пример использования
probe = make_breaker_probe([["error", "ValueError", "bad"], ["return", "ready"]])
command = failure_breaker(1, (ValueError,))(probe.operation)
try:
command()
except ValueError:
pass
try:
command()
except CircuitOpen:
print("blocked")
command.reset()
print(command())
print(len(observe_breaker_probe(probe)["calls"]))
Вывод:
blocked ready 2
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Functools.wraps в Python: имя функции и атрибут wrapped
- Декораторы Python: проверка цепочки wrapped и метаданных функции
- Декоратор Python: логирование только медленных вызовов
- Кэширование Python: постройте ключ из выбранных аргументов функции
- Rate limiter на Python: декоратор со скользящим окном запросов
- Retry и fallback на Python: декоратор повторов с резервным вызовом
