До вмісту
fr0staman
Усі статті

Підтримувати залежність самому

2 хв читання

Мій телеграм-бот надсилає голосові повідомлення. Telegram хоче їх у форматі Ogg Opus, тож бот декодує й перекодовує аудіо в 48 кГц моно. Це розв'язана задача, і для неї є крейт — невеликий, написаний іншою людиною, який затих.

Тепер я його підтримую. Без жодного великого рішення: мені знадобилося виправлення, я його надіслав, а потім виправлення знадобилися ще.

Маленькі залежності — це інший вид ризику

Зазвичай про залежності хвилюються через розмір і ланцюг постачання. Для крейта на дев'ятсот рядків справжній ризик простіший: він робить рівно те, що вам потрібно, а потім перестає рухатися. Не драматично закинутий — просто тихий. Минає редакція Rust. Спільна залежність виходить у версії 2.0. Ваша збірка починає попереджати, потім ламатися — через те, про що автор уже не має причин дбати.

Варіантів три. Вендорити й розходитись. Замінити чимось більшим. Або взяти на себе.

Що насправді означає «взяти на себе»

Здебільшого неефектну роботу. Перехід на thiserror 2.0. Перехід rand через 0.9 і далі на 0.10. Перехід на редакцію 2024 і чесний MSRV. Додавання тестів, які й мали б спіймати те, через що я взагалі відкрив цей файл.

Найцікавішим виявилося поводження з алокаціями. При декодуванні Ogg Opus старий код нарощував Vec з порожнього, перевиділяючи пам'ять дорогою, — для голосового це кілька перевиділень і чимало копіювання на кожне повідомлення, яке надсилає бот. Потік сам каже свою довжину через granule position, тож буфер можна виділити один раз наперед.

І тут виникає питання, на яке початковому коду відповідати не доводилось: а що, як файл бреше? Granule position — це просто число в заголовку. Пошкоджений файл, або ворожий (а це бот, який приймає аудіо від незнайомців), може попросити зарезервувати щось абсурдне ще до декодування першого семпла.

Тому підказку про розмір обмежено стелею. Вище стелі вона ігнорується, і буфер росте по-старому. Тоді помилкова підказка коштує щонайбільше одного перевиділення, а зловмисна — взагалі нічого. Кодування отримало те саме: місткість оцінюється з розміру корисного навантаження плюс одна восьма й невелика константа, тож одна алокація зазвичай покриває весь потік.

Що змінилося для мене

Очевидне: потрібне виправлення нарешті з'явилося. Менш очевидне: я перестав сприймати цей шар як непрозорий. Коли бот тепер поводиться дивно з якимось аудіофайлом, кодек — не чорна скриня, у яку я створюю issue й чекаю, а код, який я читав, із тестами, які я написав.

Це цінніше за продуктивність. Здебільшого «я можу піти й подивитися» — і є різниця між тим, щоб полагодити проблему й обійти її.

Коли так робити не варто

Якщо крейт великий або його предметна область така, де помилка небезпечна, а ви в ній не експерт, то «взяти на себе» — це спосіб набути зобов'язань. Нутрощі кодеків — розумне місце для обережності: я підтримую тонкий шар Ogg-контейнера навколо перевіреної реалізації Opus, а не пишу аудіокодек.

Правило, до якого я дійшов: беріть те, що вже розумієте і від чого вже залежите, коли альтернатива — чекати. Усе інше — користуйтесь і сподівайтесь.

Поділитися