← Назад к вопросам

Как определить общую оценку надёжности программы?

2.3 Middle🔥 81 комментариев
#Архитектура и паттерны#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Метрики надёжности программы (Reliability Metrics)

Надёжность программы — это способность работать без сбоев в течение длительного времени. Есть несколько метрик для её оценки.

1. Mean Time Between Failures (MTBF)

Определение: среднее время между отказами.

МТВF = Общее время наблюдения / Количество отказов

Пример:

Время наблюдения: 30 дней = 43200 часов
Отказы произошли в часы: 5, 108, 284, 1032
Количество отказов: 4

МТВF = 43200 / 4 = 10800 часов (~1.2 года)
from datetime import datetime, timedelta

class ReliabilityMetrics:
    def __init__(self):
        self.failures = []  # время в формате timestamp
        self.start_time = datetime.now()
    
    def log_failure(self):
        self.failures.append(datetime.now())
    
    def calculate_mtbf(self) -> float:
        """Возвращает MTBF в часах"""
        if not self.failures:
            return float('inf')
        
        observation_time = (datetime.now() - self.start_time).total_seconds() / 3600
        return observation_time / len(self.failures)
    
    def get_mtbf_report(self):
        mtbf = self.calculate_mtbf()
        if mtbf == float('inf'):
            return "Ещё не было отказов"
        elif mtbf > 8760:  # > года
            return f"MTBF > 1 года (очень надёжная)"
        elif mtbf > 720:   # > месяца
            return f"MTBF = {mtbf:.0f} часов (~{mtbf/24:.0f} дней)"
        else:
            return f"MTBF = {mtbf:.1f} часов (не надёжная)"

# Использование
metrics = ReliabilityMetrics()
metrics.log_failure()  # пробой прошёл
metrics.log_failure()  # ещё пробой
print(metrics.get_mtbf_report())

2. Mean Time To Recovery (MTTR)

Определение: среднее время восстановления после отказа.

МТТR = Сумма времени на восстановление / Количество отказов

Пример:

Отказ 1: восстанавливался 15 минут
Отказ 2: восстанавливался 8 минут
Отказ 3: восстанавливался 22 минуты

МТТR = (15 + 8 + 22) / 3 = 15 минут
from datetime import datetime

class FailureRecovery:
    def __init__(self):
        self.failure_start = None
        self.recovery_times = []
    
    def failure_detected(self):
        self.failure_start = datetime.now()
    
    def recovered(self):
        if self.failure_start:
            recovery_time = (datetime.now() - self.failure_start).total_seconds() / 60
            self.recovery_times.append(recovery_time)
            self.failure_start = None
    
    def get_mttr(self) -> float:
        """Возвращает MTTR в минутах"""
        if not self.recovery_times:
            return 0
        return sum(self.recovery_times) / len(self.recovery_times)

# Использование
recovery = FailureRecovery()
recovery.failure_detected()
# ... приложение упало
recovery.recovered()
print(f"MTTR: {recovery.get_mttr():.1f} минут")

3. Availability (Доступность)

Определение: процент времени когда система доступна и работает.

Availability = MTBF / (MTBF + MTTR) * 100%

Или:

Availability = (Uptime / (Uptime + Downtime)) * 100%

Примеры доступности:

99%     = 52 минуты downtime в год (2 нуля после запятой)
99.9%   = 8.7 часа downtime в год (три нуля) — "three nines"
99.99%  = 52 минуты downtime в год (four nines) — SLA для критичных систем
99.999% = 26 секунд downtime в год (five nines) — Financial systems
class SystemAvailability:
    def __init__(self):
        self.uptime_seconds = 0
        self.downtime_seconds = 0
    
    def calculate_availability(self) -> float:
        """Возвращает доступность в процентах"""
        total_time = self.uptime_seconds + self.downtime_seconds
        if total_time == 0:
            return 100.0
        return (self.uptime_seconds / total_time) * 100
    
    def get_availability_nines(self) -> str:
        """Определяет 'nines' """
        avail = self.calculate_availability()
        
        if avail >= 99.999:
            return "Five nines (99.999%) — Enterprise level"
        elif avail >= 99.99:
            return "Four nines (99.99%) — High availability"
        elif avail >= 99.9:
            return "Three nines (99.9%) — Standard for SaaS"
        elif avail >= 99:
            return "Two nines (99%) — Acceptable"
        else:
            return "Below 99% — Unacceptable"
    
    def estimate_downtime_per_year(self) -> dict:
        """Расчёт downtime в год"""
        avail = self.calculate_availability()
        downtime_percent = 100 - avail
        seconds_per_year = 365 * 24 * 3600
        downtime_seconds = (downtime_percent / 100) * seconds_per_year
        
        hours = downtime_seconds // 3600
        minutes = (downtime_seconds % 3600) // 60
        seconds = int(downtime_seconds % 60)
        
        return {
            "availability": f"{avail:.3f}%",
            "downtime_per_year": f"{hours:.0f}h {minutes:.0f}m {seconds}s"
        }

# Использование
availability = SystemAvailability()
availability.uptime_seconds = 31536000 - 3600  # год минус час
availability.downtime_seconds = 3600

print(availability.calculate_availability())  # 99.997%
print(availability.get_availability_nines())  # Four nines
print(availability.estimate_downtime_per_year())

4. Reliability at Time t — R(t)

Определение: вероятность что система будет работать без отказов до времени t.

R(t) = e^(-λt)

где λ (lambda) — failure rate
import math

def reliability_at_time(failure_rate: float, time_hours: float) -> float:
    """Рассчитывает надёжность в момент времени t"""
    return math.exp(-failure_rate * time_hours)

# Пример: failure rate = 0.001 отказа в час
failure_rate = 0.001

# Надёжность через 100 часов
reliability_100h = reliability_at_time(failure_rate, 100)
print(f"R(100h) = {reliability_100h:.3f} = {reliability_100h*100:.1f}%")
# Вывод: есть 90.5% шанс что система будет работать 100 часов без отказов

# Надёжность через 1000 часов
reliability_1000h = reliability_at_time(failure_rate, 1000)
print(f"R(1000h) = {reliability_1000h:.3f} = {reliability_1000h*100:.1f}%")
# Вывод: есть 36.8% шанс что система будет работать 1000 часов без отказов

5. Failure Rate (λ — Lambda)

Определение: количество отказов в единицу времени

λ = Количество отказов / Общее время (часы, дни)
class FailureRateTracker:
    def __init__(self):
        self.failures_per_hour = {}
        self.current_hour = None
    
    def log_failure(self):
        from datetime import datetime
        now = datetime.now()
        hour_key = now.strftime("%Y-%m-%d %H:00")
        
        if hour_key not in self.failures_per_hour:
            self.failures_per_hour[hour_key] = 0
        self.failures_per_hour[hour_key] += 1
    
    def get_failure_rate(self) -> float:
        """Возвращает λ (отказы в час)"""
        if not self.failures_per_hour:
            return 0
        
        total_failures = sum(self.failures_per_hour.values())
        total_hours = len(self.failures_per_hour)
        
        return total_failures / total_hours

tracker = FailureRateTracker()
tracker.log_failure()
tracker.log_failure()
print(f"Failure rate: {tracker.get_failure_rate():.4f} failures/hour")

6. Error Budget

Концепция: если SLA = 99.9%, сколько downtime нам "позволено"?

Error Budget = (1 - SLA%) × Время периода

Пример:

SLA = 99.9% (three nines)
Период = 30 дней

Allowed downtime = (1 - 0.999) × 30 дней × 24 часа
                 = 0.001 × 720 часов
                 = 0.72 часа
                 = 43.2 минуты downtime в месяц
def calculate_error_budget(sla_percentage: float, days_in_period: int) -> dict:
    """Рассчитывает error budget"""
    minutes_per_period = days_in_period * 24 * 60
    allowed_downtime_minutes = minutes_per_period * (100 - sla_percentage) / 100
    
    hours = int(allowed_downtime_minutes // 60)
    minutes = int(allowed_downtime_minutes % 60)
    
    return {
        "sla": f"{sla_percentage}%",
        "period": f"{days_in_period} дней",
        "allowed_downtime": f"{hours}h {minutes}m",
        "used_minutes": 0
    }

budget = calculate_error_budget(99.9, 30)  # Three nines за месяц
print(budget)
# {'sla': '99.9%', 'period': '30 дней', 'allowed_downtime': '0h 43m'}

7. Резервный расчёт: Series vs Parallel Systems

Series (компоненты последовательно) — отказ одного = отказ всей системы:

R_system = R1 × R2 × R3

Parallel (компоненты параллельно) — отказ одного ok:

R_system = 1 - (1-R1) × (1-R2) × (1-R3)
def reliability_series(*component_reliabilities) -> float:
    """Надёжность системы с компонентами в series"""
    result = 1.0
    for r in component_reliabilities:
        result *= r
    return result

def reliability_parallel(*component_reliabilities) -> float:
    """Надёжность системы с компонентами в parallel"""
    result = 1.0
    for r in component_reliabilities:
        result *= (1 - r)
    return 1 - result

# Пример: 3 компонента, каждый R = 0.95
print(f"Series: {reliability_series(0.95, 0.95, 0.95):.3f}")   # 0.857
print(f"Parallel: {reliability_parallel(0.95, 0.95, 0.95):.3f}") # 0.9988

# Series: каждый компонент должен работать
# Parallel: нужен хотя бы один рабочий

Практический пример: комплексная метрика

class ComprehensiveReliabilityReport:
    def __init__(self, mtbf: float, mttr: float, sla_target: float):
        self.mtbf = mtbf  # часы
        self.mttr = mttr  # часы
        self.sla_target = sla_target  # процент
    
    def calculate_metrics(self) -> dict:
        availability = self.mtbf / (self.mtbf + self.mttr) * 100
        failure_rate = 1 / self.mtbf
        
        # Error budget в год
        minutes_per_year = 365 * 24 * 60
        downtime_budget = minutes_per_year * (100 - self.sla_target) / 100
        
        return {
            "MTBF": f"{self.mtbf:.0f} часов ({self.mtbf/24:.0f} дней)",
            "MTTR": f"{self.mttr:.2f} часов ({self.mttr*60:.0f} минут)",
            "Availability": f"{availability:.2f}%",
            "Failure Rate": f"{failure_rate:.6f} отказов/час",
            "SLA Target": f"{self.sla_target}%",
            "Error Budget/Year": f"{downtime_budget:.0f} минут ({downtime_budget/60:.1f} часов)",
            "Status": "✓ OK" if availability >= self.sla_target else "✗ FAIL"
        }

# Использование
report = ComprehensiveReliabilityReport(
    mtbf=8760,    # 1 год между отказами
    mttr=0.5,     # 30 минут на восстановление
    sla_target=99.95  # Four nines
)

for key, value in report.calculate_metrics().items():
    print(f"{key}: {value}")

Лучшие практики

Отслеживай MTBF и MTTR — основные метрики надёжности. Задавай SLA целевые — communicate expectations. Мониторь error budget — знай сколько downtime осталось. Тестируй failover — убедись что восстановление работает. Анализируй root cause отказов. Улучшай непрерывно — каждый incident — урок.