Lightning Network: наблюдаемость и отказы поставщиков
Оглавление курса «Bitcoin и Lightning: платежи на сайте» · Урок 29 из 30.
Сайт способен корректно обработать один платёж, но для эксплуатации необходимо понимать состояние всего маршрута. Поставщик может перестать отвечать, поток уведомлений может отстать, а outbox — накопить недоставленные сообщения. В этом уроке соберём наблюдаемость библиотеки и политику ограниченных отказов. Результат позволит отличить проблему источника от ошибки конкретного заказа без массовой отмены ожидающих оплат.
Метрики и модели ниже не запускались. У нас нет измеренной скорости узла, стоимости запросов или производственного SLA. Все пороги являются выбранными условиями собственной лаборатории. При внедрении они определяются по фактическому профилю и потребностям продукта; статья не выдаёт универсальное число секунд за обязательное правило Lightning.
Наблюдаем процессы отдельно
У платёжного маршрута несколько стадий: подготовка инструмента, наблюдение источника, принятие предметного решения, создание права и доставка сообщения читателю. Каждая может задержаться независимо. Если письмо не отправлено, это не означает отсутствие платежа. Если источника нет, это не означает, что заказ отменён.
Для on-chain линии наблюдаем согласованный срез, вершину, отставание и необработанные выходы. Для Lightning линии — время последнего успешного чтения, курсоры потока, число unresolved инструментов и результаты сверки. Общее слово health полезно лишь как обзор; оно не должно скрывать различия механизмов.
Также отделяем готовность выдавать новые инструменты от способности показывать сохранённый статус. При сбое create можно временно закрыть новые счета, сохраняя страницу ранее оплаченного заказа. При недоступности lookup может работать публичный каталог статей. Ограниченное прекращение конкретного действия помогает пользователю лучше, чем общий экран «весь сайт сломан».
Паспорт состояния услуги
Создадим собственный объект с безопасными агрегатами. Он не содержит payment hash всех покупателей, содержимое подписей или секреты клиента. Временные значения имеют явный смысл, чтобы оператор не угадывал, какое событие обновило общий timestamp.
from dataclasses import dataclass
@dataclass(frozen=True)
class PaymentServiceHealth:
profile: str
create_enabled: bool
last_successful_lookup_at: int | None
unresolved_count: int
reconciliation_queue_size: int
outbox_pending_count: int
consecutive_source_failures: int
def health_view(health, now, stale_after_seconds):
last = health.last_successful_lookup_at
stale = last is None or now - last > stale_after_seconds
return {
'profile': health.profile,
'new_instruments_available': health.create_enabled,
'observation_state': 'stale' if stale else 'recent',
'unresolved_count': health.unresolved_count,
'queue_size': health.reconciliation_queue_size,
'outbox_pending': health.outbox_pending_count,
}
recent означает, что выбранное наблюдение было недавно успешным. Оно не доказывает свежесть каждого invoice и не гарантирует будущий платёж. Для отдельного заказа сохраняем своё время достоверного ответа. Если один инструмент постоянно недоступен, общий удачный lookup другого не должен скрывать этот случай.
Часы сервера могут меняться, поэтому длительности внутри процесса желательно измерять подходящим монотонным источником. Абсолютные времена нужны для журналов и интерфейса. В простой функции использован now как договор приложения; реальные механизмы времени фиксируются в эксплуатационном профиле.
Очереди и возраст важнее одного числа
Сто pending заказов могут быть нормальными неоплаченными счетами, а десять старых unresolved после массового сбоя — проблемой. Поэтому вместе с количеством полезны возраст, причины и состояние последней сверки. Не назначаем ошибку всем pending только по достижению выбранного порога числа.
Outbox имеет собственные показатели: число ожидающих, возраст самой старой задачи, частота отказов и безопасная категория причины. Settlement уже принят, но email-поставщик недоступен — читатель может получить доступ через кабинет без ожидания письма. Уведомление информирует о праве, а не создаёт его во внешней почтовой системе.
Сверка не должна обслуживать только самые новые заказы. Планировщик обеспечивает движение через всю покрываемую область, ограничивает параллелизм и сохраняет результаты. Долгий проблемный инструмент получает следующее время попытки, чтобы не занять весь цикл бесконечным быстрым повтором.
Ограничение повторов источника
При временной недоступности используем контролируемые задержки и предел работ. Когда отказов много, можно временно прекратить новые create и уменьшить нагрузку lookup. Это эксплуатационное решение, а не доказательство отсутствия платежей. Сохранённые инструменты остаются unresolved до достаточного наблюдения.
def source_policy(failures, active_requests, max_parallel):
if active_requests >= max_parallel:
return {'allow_request': False, 'reason': 'local-capacity'}
if failures >= 5: # выбранная граница учебной модели
return {'allow_request': False, 'reason': 'source-review'}
return {'allow_request': True, 'reason': 'within-policy'}
Число пять здесь не рекомендация для любого узла. Полноценная политика включает время, пробные чтения, восстановление и классификацию отказов. Ошибка неправильных credentials не исправляется бесконечным backoff. Несовместимая схема требует обновления профиля. Временная сеть допускает новый ограниченный запрос.
Не помещаем отправку денег или создание нового invoice в общий цикл «попробовать ещё раз». Наблюдение исходного инструмента может повторяться согласно договору чтения; изменение имеет собственную идемпотентность и риск неизвестного результата. Даже при аварии это разделение сохраняется.
Сверка денежных и предметных агрегатов
Сумма оплаченных заказов не обязана совпадать с текущим остатком узла. Существуют комиссии, административные переводы, возвраты, иные источники поступлений и незавершённые обязательства. Метрика paid_total отражает журнал приложения, а не баланс кошелька. Разницу нельзя немедленно назвать потерей средств без собственного финансового учёта.
Для лаборатории важнее найти нерассмотренные поступления, права без объяснимого решения и сообщения, которые не дошли после принятого settlement. Каждый класс имеет собственную очередь разбора. Статистика не удаляет такие записи ради красивого нулевого числа ошибок.
Операторский журнал содержит профиль, безопасный requestId, instrumentId и категорию. Личные данные и секреты остаются за своим контролем доступа. Если поставщик вернул полный заголовок авторизации в диагностике, адаптер фильтрует его перед записью. Наблюдаемость не является исключением из правил предыдущего урока.
Ожидаемые ситуации
Источник недоступен, но paid заказы продолжают давать сохранённое право. Новые инструменты могут быть временно выключены. Поток прервался, но сверка по payment hash продолжает восстанавливать данные. Outbox задержался, а кабинет всё равно показывает доступ по предметной базе.
При восстановлении источника очередь обрабатывается ограниченными порциями. Успешный lookup конкретного инструмента снимает его неопределённость, не переписывая остальные. Так мы получаем управляемый сервис, который сохраняет правду о платежах во время отказов. Последняя глава соберёт обе ветки в один паспорт учебного проекта.
Диагностика после переключения источника
Смена профиля получает отдельную запись: причина, область старых инструментов и выбранный способ их дальнейшей сверки. Новый endpoint не наследует историю только потому, что похож на прежний. Метрика recent нового сервиса не доказывает способность найти старые invoice.
Пока миграция не определена, прежний профиль сохраняется у своих инструментов. Это позволяет одновременно наблюдать несколько контуров и честно показывать ограничение конкретной ветки, сохраняя непрерывность предметных решений библиотеки.