Почему в одну и ту же яму можно упасть трижды: хроника повторных исправлений zidongjiexi (с системным разбором)

«Одна и та же яма — почему в неё можно упасть три раза? — хроника повторяющихся фиксов zidongjiexi (с системными уроками)»

Некоторые баги как подростковый возраст: тебе кажется, что всё прошло, а он просто сменит причёску и вернётся.
Эта авария в astrbot_plugin_zidongjiexiplusyoutubexiazai — типичный случай: кажется, что каждый раз “починили”, но при следующем перезапуске снова всё ломается.


I. Хронология инцидента: от «ошибки PIL» до «совместимости окружения»

Этап 1: первая ошибка (краш на загрузке ресурсов)

При запуске плагина в render.py на _load_video_button срабатывает:

  • img.convert("RGBA")
  • внутри Pillow — AssertionError

Интуитивный вывод: в ресурсе media_button.png есть скрытый риск декодирования (возможно, изображение повреждено; возможно, различается путь/способ декодирования во время выполнения).

Этап 2: первый фикс (на вид успешно)

Сделали пересохранение ресурса (RGBA) — сразу после перезапуска всё заработало.
Отсюда легко сделать вывод «проблема решена».

Этап 3: рецидив (снова краш в том же месте)

После следующих перезапусков снова упало. Значит проблема не просто «файл однажды был битый», а:

  • в самом кодовом пути нет отказоустойчивости;
  • при разных состояниях выполнения плагин всё равно может ловить аналогичную ошибку декодирования.

Этап 4: во время второго фикса «фикс принёс новые проблемы»

При накатывании патча возникли две классические вторичные аварии:

  1. На уровне синтаксиса внесли ошибку (неаккуратная работа с комментариями/строками) → SyntaxError;
  2. Различия версий библиотек окружения привели к несовместимости параметров EmojiCDNSource (параметр enable_tqdm не поддерживается).

В итоге сошлись через откат к стабильным файлам + минимальные инкрементальные изменения + проверку компиляции + валидацию перезапуском.


II. Анализ причин: почему «починили — и снова сломалось»

1) «Один раз успешно» ≠ «системно исправлено»

Пересохранить «битую» картинку — это устранить текущий экземпляр проблемы, но не гарантия, что в будущем не сработает тот же путь.

2) В коде плагина нет стратегии деградации для критических ресурсов

Ресурсы — внешняя зависимость. Внешняя зависимость должна быть «может упасть, но не смертельно».

3) Окружение выполнения не постоянно

Один и тот же плагин в разных контейнерных образах и при разных версиях зависимостей может инициализироваться по-разному.
Например, различия совместимости параметров apilmoji — это не ошибка бизнес-логики, а сцепление с версиями экосистемы.

4) В процессе фикса нет «жёстких шлюзов-ограничителей»

Если после каждого патча не делать сразу обязательные проверки ниже, легко откатиться из «запускается» в «падает по синтаксису»:

  • синтаксическая проверка py_compile
  • минимальная проверка холодного старта
  • утверждение по ключевым логам загрузки плагина

III. Что именно сделала финальная правка

zidongjiexi

  1. Добавили отказоустойчивую логику в _load_video_button:
    • img.load() — принудительное раннее декодирование;
    • при исключении — деградация до прозрачной заглушки, чтобы не валить старт всего плагина.
  2. Убрали несовместимый параметр enable_tqdm у EmojiCDNSource, чтобы подогнать под текущее окружение.
  3. Многократной проверкой перезапуска подтвердили, что плагин стабильно переходит в состояние «загружен».

memelite (попутно починили)

  • Исправили отсутствие libEGL.so.1: установили libegl1/libgl1, убрали цикл восстановления зависимостей на старте.

IV. Почему перезапуск вырос с 20 секунд до 600+ секунд

Это не «машина внезапно стала медленной», а в цепочке старта появился блокирующий фактор:

  1. У memelite повторяющееся восстановление зависимостей + процесс pip растягивают запуск;
  2. Повторные провалы загрузки плагина увеличивают общее время инициализации;
  3. Шторм ошибок в логах (включая аномалии формата логов сторонних библиотек) дополнительно усиливает ощущение «зависания».

По-человечески: не AstrBot за ночь постарел — он просто просыпается и одновременно чинит колесо.


V. Инженерные уроки (дороже самого фикса бага)

Урок 1: загрузка ресурсов должна «умирать, но позволять жить»

Картинки, шрифты, внешний CDN, cookie-файлы… нельзя предполагать, что они всегда в порядке.
Отказоустойчивость + деградация — нижняя граница сопровождаемости плагина.

Урок 2: фиксы должны идти по «замкнутому циклу минимальных изменений»

Рекомендуемый фиксированный процесс:

  1. Сначала вернуться к известной стабильной базе;
  2. Менять только одно место;
  3. Сделать синтаксическую/точечную проверку;
  4. Проверить перезапуском;
  5. Затем менять следующее место.

Урок 3: «починили» должно включать воспроизводимую проверку

«У меня заработало» не имеет значения — нужно, чтобы «всё ещё работает» выдерживало перезапуски и время.

Урок 4: при логировании остерегайтесь глобального сцепления формата

Если поля логов сторонней библиотеки не совпадают с форматом логов вашей системы, это может вызвать шум и даже мешать диагностике.
Можно выделить стороннему logger отдельный handler или понизить уровень логирования.


VI. Рекомендации на будущее (чтобы в следующий раз не повторилось)

  1. Добавить в плагин команду самопроверки при старте (проверка файлов ресурсов, версий зависимостей, ключевых конфигов);
  2. В CI или скрипт релиза добавить:
    • синтаксическую проверку
    • минимальный тест импорта
    • smoke test ключевых функций инициализации
  3. Для «ресурсов некритичной функциональности» унифицировать стратегию деградации;
  4. Ввести «порог тревоги по времени перезапуска» (например, >90s — предупреждение) и автоматически собирать логи узких мест.

Заключение

Самое ценное в этом инциденте не «наконец-то починили», а то, что мы подтвердили:

  • реально поставляемый фикс — это не разовый ручной пожарный выезд;
  • а перевод системы из «запускается на удаче» в «запускается предсказуемо».

Если баги приходят собирать налог на интеллект, то в этот раз хотя бы чек сохранили.:slightly_smiling_face: